building-a-secure-rest-api-with-nestjs-and-jwt-authentication.html

Building a Secure REST API with NestJS and JWT Authentication

In today's digital landscape, creating secure applications is paramount. One common requirement for modern web apps is to implement a robust REST API that ensures data protection and user authentication. In this article, we will explore how to build a secure REST API using NestJS, a progressive Node.js framework, along with JSON Web Tokens (JWT) for authentication.

What is NestJS?

NestJS is a powerful framework for building efficient, reliable, and scalable server-side applications. It is built with TypeScript and leverages the power of decorators and modules, making it an ideal choice for creating structured applications. With its modular architecture, developers can easily manage dependencies and keep code organized.

Why Use JWT for Authentication?

JSON Web Tokens (JWT) is an open standard for securely transmitting information between parties as a JSON object. Here are some key reasons to use JWT for authentication:

  • Stateless: JWTs are self-contained; they include all the necessary information about the user, eliminating the need for server-side sessions.
  • Compact: They are compact and can be sent via URLs, POST parameters, or inside HTTP headers.
  • Secure: When signed, JWTs provide a way to ensure the integrity of the data, making them a secure option.

Setting Up Your NestJS Project

To get started, you need to have Node.js and npm installed. Once you have these prerequisites, follow these steps:

Step 1: Create a New NestJS Project

Run the following command to install the NestJS CLI globally:

npm install -g @nestjs/cli

Now, create a new project:

nest new jwt-auth-example

Navigate to the project directory:

cd jwt-auth-example

Step 2: Install Required Packages

We need to install a few packages, including @nestjs/jwt for JWT handling and @nestjs/passport for authentication strategies:

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs

Step 3: Create User Module

Create a user module to handle user registration and retrieval:

nest generate module users
nest generate service users
nest generate controller users

Step 4: User Entity

In users/users.service.ts, let's create a simple in-memory user store. For production, you would typically connect to a database.

import { Injectable } from '@nestjs/common';
import { User } from './user.entity'; // Create a User interface or class

@Injectable()
export class UsersService {
  private readonly users: User[] = [];

  create(user: User) {
    this.users.push(user);
  }

  findOne(username: string): User | undefined {
    return this.users.find(user => user.username === username);
  }
}

Step 5: Authentication Module

Now let’s create the authentication module. Generate the module and service:

nest generate module auth
nest generate service auth

Step 6: Configure JWT

In auth/auth.service.ts, we will set up JWT functionality:

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService, private jwtService: JwtService) {}

  async validateUser(username: string, password: string): Promise<any> {
    const user = this.usersService.findOne(username);
    if (user && user.password === password) { // Consider hashing passwords
      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),
    };
  }
}

Step 7: Implement JWT Strategy

Create a JWT strategy in auth/jwt.strategy.ts:

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

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private usersService: UsersService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'your_secret_key', // Use environment variables in production
    });
  }

  async validate(payload: any) {
    return this.usersService.findOne(payload.username);
  }
}

Step 8: Set Up Routes for Authentication

In auth/auth.controller.ts, we will create authentication routes:

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

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

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

Step 9: Protect Routes with Guards

To protect your API routes, use guards. In your controller, you can apply the @UseGuards(AuthGuard('jwt')) decorator to secure specific endpoints.

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('protected')
export class ProtectedController {
  @UseGuards(AuthGuard('jwt'))
  @Get()
  getProtectedData() {
    return { message: 'This is protected data.' };
  }
}

Conclusion

Building a secure REST API with NestJS and JWT authentication helps ensure that your application is robust and user data is protected. By following the steps outlined in this guide, you can set up a basic user authentication system. As you expand your application, consider implementing features such as password hashing, user roles, and more complex data validation.

With NestJS's powerful features and JWT's robust framework for authentication, you are well on your way to creating secure and efficient web applications. 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.