import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { Request } from 'express'; import { IS_PUBLIC_KEY, ROLES_KEY, PERMISSIONS_KEY, } from '../../../common/decorators'; interface AuthenticatedUser { id: string; email: string; roles: string[]; permissions: string[]; } /** * JWT Auth Guard - Validates JWT token */ @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { constructor(private reflector: Reflector) { super(); } canActivate(context: ExecutionContext) { // Check if route is public const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) { return true; } return super.canActivate(context); } handleRequest( err: Error | null, user: TUser | false, info: any, ): TUser { if (err || !user) { if (info?.name === 'TokenExpiredError') { throw new UnauthorizedException('TOKEN_EXPIRED'); } throw err || new UnauthorizedException('AUTH_REQUIRED'); } return user; } } /** * Roles Guard - Check if user has required roles */ @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.getAllAndOverride( ROLES_KEY, [context.getHandler(), context.getClass()], ); if (!requiredRoles || requiredRoles.length === 0) { return true; } const request = context.switchToHttp().getRequest(); const user = request.user as AuthenticatedUser | undefined; if (!user || !user.roles) { return false; } const hasRole = requiredRoles.some((role) => user.roles.includes(role)); if (!hasRole) { throw new ForbiddenException('PERMISSION_DENIED'); } return true; } } /** * Permissions Guard - Check if user has required permissions */ @Injectable() export class PermissionsGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredPermissions = this.reflector.getAllAndOverride( PERMISSIONS_KEY, [context.getHandler(), context.getClass()], ); if (!requiredPermissions || requiredPermissions.length === 0) { return true; } const request = context.switchToHttp().getRequest(); const user = request.user as AuthenticatedUser | undefined; if (!user || !user.permissions) { return false; } const hasPermission = requiredPermissions.every((permission) => user.permissions.includes(permission), ); if (!hasPermission) { throw new ForbiddenException('PERMISSION_DENIED'); } return true; } }