Setting Up JWT Authentication in a NestJS Application with TypeScript
In the modern web development landscape, securing your applications is more crucial than ever. JSON Web Tokens (JWT) have become a popular method for handling authentication, especially in APIs. In this article, we’ll explore how to set up JWT authentication in a NestJS application using TypeScript. Whether you're building a small project or a large-scale application, understanding JWT can significantly enhance your app’s security.
What is JWT?
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, allowing the claims to be digitally signed or integrity protected with a message authentication code (MAC) and/or encrypted.
Use Cases for JWT
- Authentication: The most common use case, where JWTs are issued after a user logs in.
- Information Exchange: JWTs can be used to securely transmit information between parties.
- Single Sign-On (SSO): JWT allows users to log in once and gain access to multiple applications.
Prerequisites
Before we dive into the implementation, ensure you have the following:
- Node.js installed (preferably the latest LTS version).
- A basic understanding of TypeScript and NestJS.
- NestJS CLI installed globally. If not, you can install it using:
npm install -g @nestjs/cli
Step-by-Step Guide to Implementing JWT Authentication
Step 1: Create a New NestJS Application
Start by creating a new NestJS project:
nest new jwt-auth-example
cd jwt-auth-example
Step 2: Install Required Dependencies
You’ll need several packages to handle JWT authentication:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs
npm install --save-dev @types/passport-jwt @types/bcryptjs
Step 3: Create the Auth Module
Next, generate the authentication module and service:
nest g module auth
nest g service auth
nest g controller auth
Step 4: Configure JWT Strategy
In the auth
folder, create a new file called jwt.strategy.ts
. This file will configure the JWT strategy for Passport:
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
import { JwtPayload } from './jwt-payload.interface';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'your_secret_key', // Replace with your secret
});
}
async validate(payload: JwtPayload) {
return await this.authService.validateUser(payload);
}
}
Step 5: Implement the Auth Service
In the auth.service.ts
, implement the method to validate users and generate JWTs:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { User } from './user.entity'; // Assume you have a User entity
import { JwtPayload } from './jwt-payload.interface';
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
async validateUser(payload: JwtPayload): Promise<User> {
// Implement your user validation logic here
// Example: Find user by ID in the database
return await this.findUserById(payload.id);
}
async login(user: User) {
const payload: JwtPayload = { id: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
Step 6: Create the Auth Controller
In the auth.controller.ts
, set up routes for login:
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { User } from './user.entity'; // Assume User entity is defined
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
async login(@Body() user: User) {
return this.authService.login(user);
}
}
Step 7: Set Up JWT Module
In your auth.module.ts
, import the necessary modules:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
JwtModule.register({
secret: 'your_secret_key', // Replace with your secret
signOptions: { expiresIn: '60s' }, // Token expiration time
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
Step 8: Protecting Routes
To protect routes in your application, you can use the @UseGuards
decorator from NestJS. For example:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('protected')
export class ProtectedController {
@UseGuards(AuthGuard('jwt'))
@Get()
getProtectedResource() {
return 'This is a protected resource';
}
}
Conclusion
By following these steps, you have successfully set up JWT authentication in your NestJS application using TypeScript. JWT provides a secure way to handle user authentication and authorization, ensuring that your application is safe from unauthorized access.
Key Takeaways
- JWT is a compact way to securely transmit information between parties.
- NestJS provides excellent support for implementing JWT authentication through modules and decorators.
- Always protect sensitive routes using guards to enhance security.
With this foundational knowledge, you can now expand your NestJS application, integrate more complex authentication and authorization mechanisms, and build robust APIs. Happy coding!