/** * JWT Auth Guard * * Protects routes by verifying JWT tokens. */ import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, Logger, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; export interface JwtPayload { sub: string; // User ID or service ID type: 'access' | 'refresh' | 'service'; partyId?: string; // Party ID for MPC operations iat?: number; exp?: number; } export interface CurrentUserData { userId: string; partyId?: string; tokenType: string; } @Injectable() export class JwtAuthGuard implements CanActivate { private readonly logger = new Logger(JwtAuthGuard.name); constructor( private readonly jwtService: JwtService, private readonly configService: ConfigService, private readonly reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise { // Check if endpoint is public const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) { return true; } const request = context.switchToHttp().getRequest(); const token = this.extractTokenFromHeader(request); if (!token) { throw new UnauthorizedException('缺少认证令牌'); } try { const payload = await this.jwtService.verifyAsync(token, { secret: this.configService.get('JWT_SECRET'), }); // Validate token type if (payload.type !== 'access' && payload.type !== 'service') { throw new UnauthorizedException('无效的令牌类型'); } // Inject user data into request request.user = { userId: payload.sub, partyId: payload.partyId, tokenType: payload.type, } as CurrentUserData; return true; } catch (error) { if (error instanceof UnauthorizedException) { throw error; } this.logger.warn(`Token verification failed: ${error.message}`); throw new UnauthorizedException('令牌无效或已过期'); } } private extractTokenFromHeader(request: any): string | undefined { const authHeader = request.headers.authorization; if (!authHeader) { return undefined; } const [type, token] = authHeader.split(' '); return type === 'Bearer' ? token : undefined; } }