import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import type { FastifyRequest } from 'fastify'; import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; export interface AuthenticatedUser { id: string; email: string; permissions: string[]; } type RequestWithUser = FastifyRequest & { user?: AuthenticatedUser }; // Extrai o access token do cookie HttpOnly (fluxo normal do frontend) ou do // header Authorization (útil para clients/scripts/testes). function extractToken(request: FastifyRequest): string | null { const cookieToken = request.cookies?.['access_token']; if (cookieToken) return cookieToken; const authHeader = request.headers.authorization; if (authHeader?.startsWith('Bearer ')) return authHeader.slice('Bearer '.length); return null; } @Injectable() export class AuthGuard implements CanActivate { constructor( private readonly jwtService: JwtService, private readonly reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) return true; const request = context.switchToHttp().getRequest(); const token = extractToken(request); if (!token) throw new UnauthorizedException('Token de acesso ausente'); try { const payload = await this.jwtService.verifyAsync<{ sub: string; email: string; permissions: string[]; }>(token); request.user = { id: payload.sub, email: payload.email, permissions: payload.permissions, } satisfies AuthenticatedUser; return true; } catch { throw new UnauthorizedException('Token de acesso inválido ou expirado'); } } }