乐闻世界logo
搜索文章和话题

How to implement security and authentication in NestJS?

2月17日 22:41

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

bash
npm install @nestjs/jwt @nestjs/passport passport passport-jwt npm install -D @types/passport-jwt

Create JWT Strategy

typescript
import { 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

typescript
import { 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

typescript
import { 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

typescript
import { 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

bash
npm install @nestjs/passport passport passport-google-oauth20 npm install -D @types/passport-google-oauth20

Create Google OAuth Strategy

typescript
import { 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)

typescript
import { 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

typescript
import { 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

typescript
import * 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

typescript
import helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(helmet()); await app.listen(3000); }

CORS Configuration

typescript
async 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

typescript
import 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

typescript
import { IsEmail, IsString, MinLength } from 'class-validator'; export class CreateUserDto { @IsEmail() email: string; @IsString() @MinLength(6) password: string; }

Global Validation Pipe

typescript
import { 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

typescript
import * as xss from 'xss'; function sanitizeInput(input: string): string { return xss(input); }

Content Security Policy (CSP)

typescript
import 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

typescript
import * 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

  1. Use HTTPS: Always use HTTPS in production environments
  2. Environment Variables: Store sensitive information in environment variables
  3. Password Encryption: Use secure algorithms like bcrypt to encrypt passwords
  4. Input Validation: Validate all user inputs
  5. Principle of Least Privilege: Users should only have necessary permissions
  6. Regular Dependency Updates: Keep dependencies updated to fix security vulnerabilities
  7. Logging: Log security-related events
  8. Error Handling: Do not expose sensitive error information to clients
  9. Session Management: Use secure session management
  10. 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.

标签:NestJS