JWT Authentication in NestJS: Login, Signup, and Protected Routes

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

  1. What is JWT?
  2. Installing Required Packages
  3. Setting Up the AuthModule
  4. User Signup
  5. User Login and Token Issuance
  6. Protecting Routes with Guards
  7. Accessing Authenticated User in Controllers
  8. 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