import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy, ExtractJwt } from 'passport-jwt'; import { ConfigService } from '@nestjs/config'; import { passportJwtSecret } from 'jwks-rsa'; import { mapGroupsToPermissions } from './permissions.enum'; import type { AuthenticatedUser } from './authenticated-request'; interface JwtPayload { sub: string; email: string; name?: string; preferred_username?: string; groups?: string[]; } @Injectable() export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { constructor(configService: ConfigService) { const issuer = configService.get('OIDC_ISSUER', ''); super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, issuer, algorithms: ['RS256'], secretOrKeyProvider: passportJwtSecret({ cache: true, rateLimit: true, jwksRequestsPerMinute: 5, jwksUri: `${issuer.endsWith('/') ? issuer.slice(0, -1) : issuer}/jwks/`, }), }); } validate(payload: JwtPayload): AuthenticatedUser { const groups = payload.groups ?? []; return { userId: payload.sub, email: payload.email, name: payload.name || payload.preferred_username || '', preferredUsername: payload.preferred_username ?? null, groups: groups, permissions: mapGroupsToPermissions(groups), }; } }