Introduction
Handling authentication and authorization is a critical component in modern web applications. This tutorial dives into best practices for managing these security features within a NestJS application, with the latest TypeScript syntax in play.
Setting Up Your NestJS Project
Before diving into authentication and authorization, initialize your NestJS project by running:
nest new project-name
After creating the project, navigate to its directory:
cd project-name
Implementing Authentication with Passport
NestJS leverages Passport.js for authentication. First, install the required packages:
npm install @nestjs/passport passport passport-local
npm install @types/passport-local --save-dev
Create an auth
module, service, and a local strategy:
nest generate module auth
nest generate service auth
nest generate strategy auth/local
In your local strategy:
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise {
// Add your validation logic here
}
}
For handling user validation, you should implement AuthService
:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AuthService {
// Your user validation logic goes here
}
Setting Up JWT for Sessions
To manage sessions with JWT, you’ll need additional packages:
npm install @nestjs/jwt passport-jwt
npm install @types/passport-jwt --save-dev
Implement a JWT strategy similar to the local strategy, using passport-jwt
.
Securing Routes with Guards
Authentication isn’t useful without securing routes. Implement a guard to ensure users are authenticated:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
Then, apply the guard to your routes using decorators:
import { Controller, Request, Post, UseGuards } from '@nestjs/common';
@Controller()
export class AppController {
@UseGuards(LocalAuthGuard)
@Post('auth/login')
async login(@Request() req) {
return req.user;
}
// ... additional methods ...
}
Implementing Authorization with Roles
Authorization ensures that authenticated users have permissions to perform certain actions. NestJS supports role-based access control:
import { Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { CanActivate, ExecutionContext } from '@nestjs/common';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
To associate roles with routes, create a custom decorator:
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
Then, secure a route with the roles decorator and RolesGuard
:
@Roles('admin')
@UseGuards(RolesGuard)
async adminAction() {
// admin only action
}
In advanced scenarios, you may need to refresh tokens. Provide an endpoint for refresh token generation and a strategy to handle it.
Conclusion
Addressing authentication and authorization in NestJS is fundamental for creating secure applications. This tutorial guides you through setting up basic to advanced strategies within the NestJS framework, equipping you to effectively manage your application’s security with confidence. Always test your implementations and keep your dependencies current to uphold best practices.