implementing-jwt-authentication-in-a-nestjs-api-with-typescript.html

Implementing JWT Authentication in a NestJS API with TypeScript

Introduction

In the world of web development, security is paramount, especially when dealing with user authentication. JSON Web Tokens (JWT) have become a popular choice for securing APIs due to their efficiency and ease of use. In this article, we will explore how to implement JWT authentication in a NestJS API using TypeScript. We’ll cover everything from setting up your NestJS project to creating a fully functional authentication system. Whether you're a beginner or an experienced developer, this guide will provide you with actionable insights and clear code examples.

What is JWT?

JWT, or JSON Web Token, is an open standard (RFC 7519) that defines a compact way to represent claims securely between two parties. A JWT is a string made up of three parts: the header, payload, and signature. This structure allows you to verify the integrity of the data and authenticate users across your application.

Use Cases for JWT

  • Stateless Authentication: JWTs can be used in stateless APIs where the server does not need to store session data.
  • Single Sign-On (SSO): JWTs are ideal for SSO implementations since they can be shared across different domains.
  • Mobile Applications: JWTs are lightweight and can easily be sent in HTTP headers, making them suitable for mobile app authentication.

Setting Up a NestJS Project

Before we dive into JWT implementation, let’s create a new NestJS project. If you haven’t installed the Nest CLI, you can do so with the following command:

npm i -g @nestjs/cli

Now, create a new project:

nest new jwt-auth-example
cd jwt-auth-example

Installing Required Packages

To implement JWT authentication, you’ll need to install several packages:

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
  • @nestjs/jwt: Provides JWT utilities for NestJS.
  • @nestjs/passport: Integrates Passport.js with NestJS.
  • passport: A popular authentication middleware for Node.js.
  • passport-jwt: A Passport strategy for JWT authentication.
  • bcrypt: For hashing passwords.

Creating the Authentication Module

Let’s create an authentication module to handle user login and JWT creation. First, generate the module and service:

nest generate module auth
nest generate service auth

Now, we will implement the authentication logic in auth.service.ts. Here’s how you can set it up:

// auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { User } from './user.entity'; // Assume you have a User entity
import * as bcrypt from 'bcrypt';

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

  async validateUser(username: string, password: string): Promise<any> {
    const user = await this.findUserByUsername(username); // Implement user fetching
    if (user && await bcrypt.compare(password, user.password)) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

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

  private async findUserByUsername(username: string): Promise<User | null> {
    // Implement this to fetch user from your database
    return null;
  }
}

Implementing the JWT Strategy

Next, we need to create a JWT strategy using Passport. Create a file named jwt.strategy.ts:

// jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'your_secret_key', // Should be environment variable
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

Creating the Authentication Controller

Now, let’s create a controller to handle user login requests. Generate the controller:

nest generate controller auth

In auth.controller.ts, implement the login endpoint:

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

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

  @Post('login')
  async login(@Body() user: any) {
    return this.authService.login(user);
  }
}

Securing Routes with JWT

To secure your routes, use the @UseGuards() decorator provided by NestJS. Here’s an example of securing a protected route:

// app.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './jwt-auth.guard'; // Implement this guard

@Controller('protected')
export class AppController {
  @UseGuards(JwtAuthGuard)
  @Get()
  getProtectedResource() {
    return { message: 'This is a protected resource' };
  }
}

Testing Your Implementation

You can test your JWT authentication using tools like Postman. Here’s a quick guide:

  1. Login: Send a POST request to /auth/login with JSON payload containing the username and password. You should receive a JWT in response.

  2. Access Protected Route: Use the received JWT as a Bearer token in the Authorization header to access the protected route.

Conclusion

Implementing JWT authentication in a NestJS API with TypeScript can greatly enhance your application's security. By following the steps outlined in this article, you now have a solid foundation to build upon. Always remember to manage your secrets securely and to handle user data responsibly. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.