import { Inject, Injectable, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { passportJwtSecret } from 'jwks-rsa'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { Logger } from 'winston'; import { JwtPayload, JwtUser } from './models/main.model'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor( configService: ConfigService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) { const realm = configService.get('REALM'); super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKeyProvider: passportJwtSecret({ cache: true, rateLimit: true, jwksRequestsPerMinute: 5, jwksUri: `https://auth.bizmatch.net/realms/${realm}/protocol/openid-connect/certs`, }), audience: 'account', // Keycloak Client ID authorize: '', issuer: `https://auth.bizmatch.net/realms/${realm}`, algorithms: ['RS256'], }); } async validate(payload: JwtPayload): Promise { if (!payload) { this.logger.error('Invalid payload'); throw new UnauthorizedException(); } if (!payload.sub || !payload.preferred_username) { this.logger.error('Missing required claims'); throw new UnauthorizedException(); } const result = { userId: payload.sub, firstname: payload.given_name, lastname: payload.family_name, username: payload.preferred_username, roles: payload.realm_access?.roles }; this.logger.info(`JWT User: ${JSON.stringify(result)}`); // Debugging: JWT Payload anzeigen return result; } }