import { Injectable, UnauthorizedException, Logger } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ConfigService } from '@nestjs/config'; import { Strategy, ExtractJwt } from 'passport-jwt'; import { passportJwtSecret } from 'jwks-rsa'; import { AuthService } from '../auth.service'; import { HttpService } from '@nestjs/axios'; import { firstValueFrom } from 'rxjs'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { private readonly logger = new Logger(JwtStrategy.name); constructor( private configService: ConfigService, private authService: AuthService, private httpService: HttpService, ) { super({ secretOrKeyProvider: passportJwtSecret({ cache: true, rateLimit: true, jwksRequestsPerMinute: 5, jwksUri: `${configService.get('AUTH0_ISSUER')}.well-known/jwks.json`, }), jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), audience: configService.get('AUTH0_AUDIENCE'), issuer: configService.get('AUTH0_ISSUER'), algorithms: ['RS256'], passReqToCallback: true, // We need the request to get the token }); } async validate(req: any, payload: any) { // Extract token from Authorization header const token = req.headers.authorization?.replace('Bearer ', ''); // Fetch user info from Auth0 /userinfo endpoint try { const userInfoUrl = `${this.configService.get('AUTH0_ISSUER')}userinfo`; const response = await firstValueFrom( this.httpService.get(userInfoUrl, { headers: { Authorization: `Bearer ${token}`, }, }), ); // Merge userinfo data into payload const userInfo = response.data; payload.email = userInfo.email || payload.email; payload.name = userInfo.name || payload.name; payload.picture = userInfo.picture || payload.picture; payload.email_verified = userInfo.email_verified; } catch (error) { this.logger.warn(`Failed to fetch user info: ${error.message}`); // Continue with payload-only data (fallbacks will apply) } // Get or create user from Auth0 token const user = await this.authService.validateUser(payload); if (!user) { throw new UnauthorizedException('User not found'); } if (!user.isApproved) { throw new UnauthorizedException('User account pending approval'); } return user; } }