Security is a critical component of any application. NestJS provides multiple security mechanisms, including authentication, authorization, data encryption, and protection measures, helping developers build secure and reliable applications.
Authentication
JWT Authentication
Install Dependencies
bashnpm install @nestjs/jwt @nestjs/passport passport passport-jwt npm install -D @types/passport-jwt
Create JWT Strategy
typescriptimport { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { ConfigService } from '@nestjs/config'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private configService: ConfigService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get('JWT_SECRET'), }); } async validate(payload: any) { return { userId: payload.sub, username: payload.username }; } }
Create Auth Service
typescriptimport { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { UsersService } from '../users/users.service'; import * as bcrypt from 'bcrypt'; @Injectable() export class AuthService { constructor( private usersService: UsersService, private jwtService: JwtService, ) {} async validateUser(username: string, password: string): Promise<any> { const user = await this.usersService.findByUsername(username); 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), }; } async register(username: string, password: string) { const hashedPassword = await bcrypt.hash(password, 10); return this.usersService.create({ username, password: hashedPassword }); } }
Create Auth Controller
typescriptimport { Controller, Post, Body, UseGuards, Request } from '@nestjs/common'; import { AuthService } from './auth.service'; import { LocalAuthGuard } from './local-auth.guard'; import { JwtAuthGuard } from './jwt-auth.guard'; @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} @UseGuards(LocalAuthGuard) @Post('login') async login(@Request() req) { return this.authService.login(req.user); } @Post('register') async register(@Body() registerDto: { username: string; password: string }) { return this.authService.register(registerDto.username, registerDto.password); } @UseGuards(JwtAuthGuard) @Post('profile') getProfile(@Request() req) { return req.user; } }
Local Authentication Strategy
typescriptimport { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-local'; import { AuthService } from './auth.service'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { constructor(private authService: AuthService) { super({ usernameField: 'username', passwordField: 'password', }); } async validate(username: string, password: string): Promise<any> { const user = await this.authService.validateUser(username, password); if (!user) { throw new UnauthorizedException(); } return user; } }
OAuth2 Authentication
Install Dependencies
bashnpm install @nestjs/passport passport passport-google-oauth20 npm install -D @types/passport-google-oauth20
Create Google OAuth Strategy
typescriptimport { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy, VerifyCallback } from 'passport-google-oauth20'; import { ConfigService } from '@nestjs/config'; @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor(private configService: ConfigService) { super({ clientID: configService.get('GOOGLE_CLIENT_ID'), clientSecret: configService.get('GOOGLE_CLIENT_SECRET'), callbackURL: configService.get('GOOGLE_CALLBACK_URL'), scope: ['email', 'profile'], }); } async validate(accessToken: string, refreshToken: string, profile: any, done: VerifyCallback): Promise<any> { const { name, emails, photos } = profile; const user = { email: emails[0].value, firstName: name.givenName, lastName: name.familyName, picture: photos[0].value, accessToken, }; done(null, user); } }
Authorization
Role-Based Access Control (RBAC)
typescriptimport { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @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 { user } = context.switchToHttp().getRequest(); return requiredRoles.some((role) => user?.roles?.includes(role)); } }
Roles Decorator
typescriptimport { SetMetadata } from '@nestjs/common'; export const ROLES_KEY = 'roles'; export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
Use Roles Guard
typescript@Controller('users') export class UsersController { @Get('admin') @UseGuards(JwtAuthGuard, RolesGuard) @Roles('admin') getAdminData() { return 'Admin data'; } }
Password Encryption
Using bcrypt
typescriptimport * as bcrypt from 'bcrypt'; async hashPassword(password: string): Promise<string> { const salt = await bcrypt.genSalt(10); return bcrypt.hash(password, salt); } async comparePassword(password: string, hash: string): Promise<boolean> { return bcrypt.compare(password, hash); }
Security Middleware
Helmet
typescriptimport helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(helmet()); await app.listen(3000); }
CORS Configuration
typescriptasync function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors({ origin: 'https://example.com', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, }); await app.listen(3000); }
Rate Limiting
typescriptimport rateLimit from 'express-rate-limit'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs }), ); await app.listen(3000); }
Data Validation
Using class-validator
typescriptimport { IsEmail, IsString, MinLength } from 'class-validator'; export class CreateUserDto { @IsEmail() email: string; @IsString() @MinLength(6) password: string; }
Global Validation Pipe
typescriptimport { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, }), ); await app.listen(3000); }
SQL Injection Protection
Use Parameterized Queries
typescript// Unsafe const query = `SELECT * FROM users WHERE name = '${name}'`; // Safe const query = `SELECT * FROM users WHERE name = ?`; const result = await this.userRepository.query(query, [name]);
Use ORM
typescript// TypeORM automatically prevents SQL injection const user = await this.userRepository.findOne({ where: { name: username }, });
XSS Protection
Input Sanitization
typescriptimport * as xss from 'xss'; function sanitizeInput(input: string): string { return xss(input); }
Content Security Policy (CSP)
typescriptimport helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"], }, }), ); await app.listen(3000); }
CSRF Protection
Use csurf
typescriptimport * as csurf from 'csurf'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(csurf({ cookie: true })); await app.listen(3000); }
Security Best Practices
- Use HTTPS: Always use HTTPS in production environments
- Environment Variables: Store sensitive information in environment variables
- Password Encryption: Use secure algorithms like bcrypt to encrypt passwords
- Input Validation: Validate all user inputs
- Principle of Least Privilege: Users should only have necessary permissions
- Regular Dependency Updates: Keep dependencies updated to fix security vulnerabilities
- Logging: Log security-related events
- Error Handling: Do not expose sensitive error information to clients
- Session Management: Use secure session management
- Security Headers: Set appropriate security headers
Summary
NestJS security and authentication provides:
- Multiple authentication strategies
- Flexible authorization mechanisms
- Powerful data protection
- Comprehensive security measures
- Easy-to-integrate security tools
Mastering security and authentication is key to building secure, reliable NestJS applications. By properly using authentication strategies, authorization mechanisms, and security best practices, you can protect applications from various security threats and ensure the security of user data and systems.