Authentication is a fundamental part of most modern web applications. In this module, you’ll learn how to implement JWT (JSON Web Token) based authentication in NestJS to handle user signup, login, and secure route protection.
NestJS integrates seamlessly with Passport.js, providing powerful tools to implement authentication strategies like JWT.
Table of Contents
- What is JWT?
- Installing Required Packages
- Setting Up the AuthModule
- User Signup
- User Login and Token Issuance
- Protecting Routes with Guards
- Accessing Authenticated User in Controllers
- Conclusion
What is JWT?
JWT (JSON Web Token) is a compact, URL-safe token format used for securely transmitting information between parties. In authentication, it’s commonly used to represent user identity after successful login.
JWTs typically consist of:
- Header: Token type and signing algorithm
- Payload: User data (e.g.,
sub
,email
,roles
) - Signature: Ensures the token hasn’t been tampered with
Installing Required Packages
Install the necessary packages for JWT and Passport integration:
bashCopyEditnpm install @nestjs/passport passport passport-jwt
npm install @nestjs/jwt
npm install --save-dev @types/passport-jwt
Setting Up the AuthModule
1. Create AuthModule
and services
bashCopyEditnest g module auth
nest g service auth
nest g controller auth
2. Configure JwtModule
in auth.module.ts
tsCopyEdit// auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: 'your_jwt_secret', // Should be stored in env file
signOptions: { expiresIn: '1h' },
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
User Signup
Create DTO
tsCopyEdit// dto/create-user.dto.ts
import { IsEmail, IsNotEmpty } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsNotEmpty()
password: string;
}
Hash Password and Save User
tsCopyEdit// auth.service.ts
import * as bcrypt from 'bcrypt';
async signup(createUserDto: CreateUserDto) {
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(createUserDto.password, salt);
const user = this.usersRepository.create({
...createUserDto,
password: hashedPassword,
});
return this.usersRepository.save(user);
}
User Login and Token Issuance
Login DTO
tsCopyEdit// dto/login.dto.ts
import { IsEmail, IsNotEmpty } from 'class-validator';
export class LoginDto {
@IsEmail()
email: string;
@IsNotEmpty()
password: string;
}
Validate User and Generate Token
tsCopyEdit// auth.service.ts
import { JwtService } from '@nestjs/jwt';
async validateUser(email: string, password: string): Promise<any> {
const user = await this.usersRepository.findOne({ where: { email } });
if (user && await bcrypt.compare(password, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { email: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
AuthController
tsCopyEdit// auth.controller.ts
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateUserDto } from './dto/create-user.dto';
import { LoginDto } from './dto/login.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('signup')
signup(@Body() dto: CreateUserDto) {
return this.authService.signup(dto);
}
@Post('login')
async login(@Body() dto: LoginDto) {
const user = await this.authService.validateUser(dto.email, dto.password);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
return this.authService.login(user);
}
}
Protecting Routes with Guards
Create JWT Strategy
tsCopyEdit// jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'your_jwt_secret',
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}
Apply Auth Guard to Routes
tsCopyEdit// user.controller.ts
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
@Controller('users')
export class UserController {
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
}
Create Guard
tsCopyEdit// jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Accessing Authenticated User in Controllers
NestJS automatically injects the user object into the request if the route is guarded:
tsCopyEdit@Get('me')
@UseGuards(JwtAuthGuard)
getMe(@Request() req) {
return req.user;
}
You can also create a custom decorator:
tsCopyEdit// user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
Then use it like this:
tsCopyEdit@Get('me')
getMe(@CurrentUser() user) {
return user;
}
Conclusion
JWT authentication in NestJS provides a powerful way to secure your APIs. In this module, you learned how to:
- Implement user signup with password hashing
- Authenticate users and issue JWTs
- Protect routes using guards and extract user info from the token