Nest.js Tutorial: JWT Authentication with Passport.js
In our previous tutorial, we've seen how you can build a REST API CRUD application with Nest.js and TypeORM. In this tutorial, we'll add JWT authentication to protect our RESTful endpoints from unauthorized access.
Make sure to read Nest.js Tutorial: Build your First REST API CRUD App with TypeORM first.
If you don't want to follow from the first part, you can also clone the repository containg the code for the REST API CRUD example using the following command:
$ git clone https://github.com/techiediaries/nestjs-crud-rest-api.git
Next, navigate inside the folder of your project:
$ cd nestjs-crud-rest-api
Next, install the dependencies using:
$ npm install
Finally, run the development server using:
$ npm run start:dev
This will start a live-reload development server at the localhost:3000
address.
Now, let's continue developing our demo application by adding JWT authentication using Passport.js.
Creating the Auth Module
Let's start by creating a module for encapsulating the authentication logic. Open a new terminal and run the following command:
$ nest generate module auth
This will create the src/auth/auth.module.ts
file and update the src/app.module.ts
file to include the module.
Creating The User Entity Model
Next, let's create a User
entity for creating and manipulating users. Create a src/auth/user.entity.ts
file:
$ touch src/auth/user.entity.ts
Open the src/auth/user.entity.ts
file and add the following code:
import { Entity, Column, PrimaryGeneratedColumn,BeforeInsert } from 'typeorm';
import * as crypto from 'crypto';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({default: ''})
avatar: string;
@Column()
email: string;
@BeforeInsert()
hashPassword() {
this.password = crypto.createHmac('sha256', this.password).digest('hex');
}
@Column()
password: string;
}
Next, open the src/auth/auth.module.ts
file and add the User
entity:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])]
})
export class AuthModule {}
Next, let's create a service for working with the User
entity. In you terminal, run the following command:
$ nest generate service auth/user
Open the src/auth/user.service.ts
file and add the following content:
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm'
import { User } from './user.entity';
Next inject the User
repository via the constructor:
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) { }
Finally, add the following methods:
async findByEmail(email: string): Promise<User> {
return await this.userRepository.findOne({
where: {
email: email,
}
});
}
async findById(id: number): Promise<User> {
return await this.userRepository.findOne({
where: {
id: id,
}
});
}
async create(user: User): Promise<User> {
return await this.userRepository.save(user);
}
Implementing JWT Authentication
To implement JWT authentication in our application we need to install a few dependencies:
$ npm install --save @nestjs/jwt passport-jwt
passport-jwt
v4.0.0 and @nestjs/jwt
v0.3.0 are installed.
The @nestjs/jwt module provides JWT utilities for Nest.js.
The passport-jwt
package contains the Passport JWT strategy.
Open the src/auth/auth.module.ts
file and include the JWT module:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [TypeOrmModule.forFeature([User]),
JwtModule.register({
secretOrPrivateKey: 'secret12356789'
})
],
providers: [UserService]
})
export class AuthModule { }
Next, generate the authentication service using Nest CLI:
$ nest generate service auth/auth
Open the src/auth/auth.service.ts
file and add the following imports:
import { JwtService } from '@nestjs/jwt';
import { UserService } from '../user/user.service';
import { User } from './user.entity';
Next, inject UserService
and JwtService
via the constructor:
@Injectable()
export class AuthService {
constructor(
private readonly userService: UserService,
private readonly jwtService: JwtService
) { }
}
Finally, add the following methods:
private async validate(userData: User): Promise<User> {
return await this.userService.findByEmail(userData.email);
}
public async login(user: User): Promise< any | { status: number }>{
return this.validate(user).then((userData)=>{
if(!userData){
return { status: 404 };
}
let payload = `${userData.name}${userData.id}`;
const accessToken = this.jwtService.sign(payload);
return {
expires_in: 3600,
access_token: accessToken,
user_id: payload,
status: 200
};
});
}
public async register(user: User): Promise<any>{
return this.userService.create(user)
}
Creating the JWT Authentication Endpoints
After adding the necessary methods for implementing JWT, we now need to add the authentication endpoints. In your terminal run the following command to generate a controller:
$ nest generate controller auth/auth
Open the src/auth/auth/auth.controller.ts
file and add the following imports:
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from '../auth.service';
import { User } from '../user.entity';
Next, inject AuthService
via the contructor:
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
}
Finally, add the routes:
@Post('login')
async login(@Body() user: User): Promise<any> {
return this.authService.login(user);
}
@Post('register')
async register(@Body() user: User): Promise<any> {
return this.authService.register(user);
}
You can now test your JWT endpoints using a REST API client.
To register a user, send a POST request to the /auth/register
endpoint with the user information:
Next, you can login a user by sending a POST request to the /auth/login
endpoint:
Conclusion
In this tutorial, we've seen how to implement JWT authentication in a Nest.js application using TypeORM and Passport.js.
-
Date: