/** * JWT STRATEGY - REFERENCE IMPLEMENTATION * * @description Estrategia de autenticación JWT para NestJS/Passport. * Valida tokens JWT y extrae payload del usuario. * * @usage Copiar y adaptar según necesidades del proyecto. * @origin gamilit/apps/backend/src/modules/auth/strategies/jwt.strategy.ts */ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; // Adaptar import según proyecto // import { User } from '../entities'; /** * Payload del JWT * * @property sub - ID del usuario (auth.users.id) * @property email - Email del usuario * @property role - Rol del usuario * @property iat - Issued at (timestamp) * @property exp - Expiration (timestamp) */ interface JwtPayload { sub: string; email: string; role: string; iat: number; exp: number; } /** * Usuario validado que se inyecta en req.user */ interface ValidatedUser { id: string; email: string; role: string; } @Injectable() export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { constructor( private readonly configService: ConfigService, @InjectRepository(User, 'auth') private readonly userRepository: Repository, ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get('JWT_SECRET'), }); } /** * Validar payload del JWT * * @description Este método es llamado por Passport después de verificar * la firma y expiración del token. El valor retornado se asigna a req.user. * * @param payload - Payload decodificado del JWT * @returns Usuario validado para inyectar en request * @throws UnauthorizedException si el usuario no existe o está inactivo */ async validate(payload: JwtPayload): Promise { // Opción 1: Solo validar que el payload es válido (más rápido) // return { id: payload.sub, email: payload.email, role: payload.role }; // Opción 2: Verificar que el usuario existe en BD (más seguro) const user = await this.userRepository.findOne({ where: { id: payload.sub }, select: ['id', 'email', 'role'], }); if (!user) { throw new UnauthorizedException('Usuario no encontrado'); } // Opción 3: También verificar status (si aplica) // if (user.status !== 'active') { // throw new UnauthorizedException('Usuario inactivo'); // } return { id: user.id, email: user.email, role: user.role, }; } }