Creating a Secure REST API with NestJS and JWT Authentication
In today's digital landscape, APIs play a crucial role in enabling communication between different software systems. One popular framework for building robust APIs in Node.js is NestJS. NestJS is a progressive Node.js framework that uses TypeScript by default and is heavily inspired by Angular. This article will guide you through creating a secure REST API with NestJS and JSON Web Tokens (JWT) for authentication.
What is NestJS?
NestJS is a powerful framework that allows developers to build efficient and scalable server-side applications. It leverages modern JavaScript and TypeScript features, making it a great choice for developers looking for a structured way to build applications.
Key Features of NestJS:
- Modular Architecture: Easily manage code with modules.
- Dependency Injection: Promote code reusability and maintainability.
- Built-in Support for TypeScript: Leverage strong typing for better development experience.
- Extensive Documentation: Provides comprehensive guides and examples.
Understanding JWT Authentication
JSON Web Tokens (JWT) are an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. They are compact, URL-safe, and can be verified and trusted because they are digitally signed.
How JWT Works:
- User Login: The user authenticates with their credentials.
- Token Issuance: The server generates a JWT and sends it back to the user.
- Token Storage: The user stores the token (usually in local storage).
- Token Usage: For every subsequent request, the user sends the token in the Authorization header.
- Server Validation: The server validates the token and allows or denies access based on its validity.
Use Cases for a Secure REST API
Creating a secure REST API is essential for applications where user data is sensitive or needs protection. Use cases include: - E-commerce Platforms: Secure user accounts and transaction data. - Social Media Apps: Protect user profiles and posts. - Financial Services: Safeguard sensitive financial information.
Step-by-Step Guide to Building a Secure REST API with NestJS and JWT
Prerequisites
Before we dive in, ensure you have:
- Node.js installed.
- NestJS CLI installed (npm i -g @nestjs/cli
).
- Basic knowledge of TypeScript and RESTful APIs.
Step 1: Setting up the NestJS Project
Create a new NestJS project by running:
nest new jwt-auth-api
cd jwt-auth-api
Step 2: Install Required Packages
Install the required dependencies for JWT and password hashing:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs
Step 3: Create the Auth Module
Generate an authentication module:
nest g module auth
nest g service auth
nest g controller auth
Step 4: Implementing JWT Authentication
4.1 Create User Entity
Create a simple user entity to store user data. In src/auth/user.entity.ts
:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
}
4.2 User Registration
In auth.service.ts
, implement a method to register a new user:
import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class AuthService {
private users: User[] = []; // This can be replaced with a database
async register(username: string, password: string): Promise<User> {
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = { id: Date.now(), username, password: hashedPassword };
this.users.push(newUser);
return newUser;
}
}
4.3 JWT Generation
Add the login functionality in auth.service.ts
:
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
async login(username: string, password: string): Promise<string> {
const user = this.users.find(u => u.username === username);
if (user && await bcrypt.compare(password, user.password)) {
const payload = { username: user.username, sub: user.id };
return this.jwtService.sign(payload);
}
throw new Error('Invalid credentials');
}
}
Step 5: Protecting Routes with Guards
Create a JWT authentication guard in auth/jwt-auth.guard.ts
:
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Step 6: Create AuthController
In auth.controller.ts
, handle user registration and login:
import { Controller, Post, Body, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtAuthGuard } from './jwt-auth.guard';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('register')
register(@Body() body: { username: string; password: string }) {
return this.authService.register(body.username, body.password);
}
@Post('login')
login(@Body() body: { username: string; password: string }) {
return this.authService.login(body.username, body.password);
}
@UseGuards(JwtAuthGuard)
@Post('protected')
protectedRoute() {
return { message: 'You have access to this route!' };
}
}
Step 7: Testing Your API
Now that your API is set up, you can use tools like Postman or Insomnia to test the registration and login endpoints.
- Register a User: Send a POST request to
/auth/register
. - Login: Send a POST request to
/auth/login
to receive a JWT. - Access Protected Route: Send a POST request to
/auth/protected
with the token in the Authorization header.
Conclusion
By following this guide, you have created a secure REST API using NestJS with JWT authentication. This setup allows for efficient user management and protects sensitive data through secure token-based authentication. Remember to always validate and sanitize user input to enhance security further.
As you continue to develop your API, consider additional features such as role-based access control, refresh tokens, and error handling to make your application more robust and secure. Happy coding!