Published on

πŸ” Simplified Guide to JWT Authentication with NestJS

Simplified Guide to JWT Authentication with NestJS πŸ”

Introduction

In today's digital world, securing your applications is essential. One effective method to ensure user identity verification is through JWT (JSON Web Token) authentication. In this guide, we'll implement JWT authentication in a NestJS application, focusing on controllers, services, configurations, and repositories. Let’s enhance your app's security step by step!

πŸš€ Step 1: Setting Up Your NestJS Project

Start by creating a new NestJS project. You can do this using the Nest CLI:

npm i -g @nestjs/cli
nest new your-project-name

Next, install the necessary dependencies for JWT authentication:

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs
npm install --save-dev @types/passport-jwt

πŸ“¦ Step 2: Creating User Entity and Repository

Create a User entity to hold user data such as id, username, and password. You can use TypeORM for database interactions.

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

Next, create a UserRepository using TypeORM:

import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  async findByUsername(username: string): Promise<User | undefined> {
    return this.findOne({ where: { username } });
  }
}

πŸ”’ Step 3: Configuring AuthModule

Set up a new module called AuthModule and configure it for JWT authentication.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [
    UsersModule,
    JwtModule.register({
      secret: 'your-secret-key', // Replace with a secure secret
      signOptions: { expiresIn: '15m' },
    }),
  ],
  providers: [AuthService],
  exports: [AuthService],
})
export class AuthModule {}

πŸ‘€ Step 4: Implementing AuthService

Create the AuthService to handle user registration and authentication.

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { User } from './user.entity';
import { UserRepository } from './user.repository';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(
    private readonly userRepository: UserRepository,
    private readonly jwtService: JwtService
  ) {}

  async register(username: string, password: string): Promise<User> {
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = this.userRepository.create({ username, password: hashedPassword });
    return this.userRepository.save(user);
  }

  async validateUser(username: string, password: string): Promise<any> {
    const user = await this.userRepository.findByUsername(username);
    if (user && (await bcrypt.compare(password, user.password))) {
      const { password, ...result } = user; // Exclude password from the response
      return result;
    }
    return null;
  }

  async login(user: User) {
    const payload = { username: user.username, sub: user.id };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

πŸ”‘ Step 5: Creating AuthController

Create an AuthController to handle registration and login requests.

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('register')
  async register(@Body() body: { username: string; password: string }) {
    const user = await this.authService.register(body.username, body.password);
    return { message: 'User registered successfully!', user };
  }

  @Post('login')
  async login(@Body() body: { username: string; password: string }) {
    const user = await this.authService.validateUser(body.username, body.password);
    if (!user) {
      return { message: 'Invalid username or password' };
    }
    return this.authService.login(user);
  }
}

πŸ” Step 6: Implementing JWT Strategy

Create a JwtStrategy to validate JWT tokens on incoming requests.

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { JwtPayload } from './jwt-payload.interface';
import { UsersService } from '../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly usersService: UsersService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'your-secret-key', // Same as in AuthModule
    });
  }

  async validate(payload: JwtPayload) {
    return this.usersService.findById(payload.sub);
  }
}

🌟 Conclusion Keep in mind that security is an ongoing process, so always stay updated with best practices to further enhance your application’s defenses. Happy coding, and stay secure! πŸ”’πŸ”