import { Strategy as OIDCStrategy } from 'passport-openidconnect'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import { AuthentikAuthService } from '../services/authentik-auth.service'; export interface AuthentikPayload { iss: string; // Issuer sub: string; // Subject (user ID) aud: string; // Audience (client ID) exp: number; // Expiration time (Unix) iat: number; // Issued at time (Unix) auth_time: number; // Time of authentication (Unix) acr?: string; // Auth Context Class Reference amr?: string[]; // Auth Method References (e.g., ['pwd']) email: string; email_verified: boolean; name?: string; given_name?: string; preferred_username?: string; nickname?: string; groups?: string[]; } @Injectable() export class AuthentikStrategy extends PassportStrategy(OIDCStrategy) { constructor(private authentikAuthService: AuthentikAuthService) { super({ issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING", clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING', clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING', callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING', authorizationURL: `${process.env.AUTHENTIK_ISSUER}/authorize/`, tokenURL: `${process.env.AUTHENTIK_ISSUER}/token/`, userInfoURL: `${process.env.AUTHENTIK_ISSUER}/userinfo/`, scope: ['openid', 'email', 'profile'], }); } async validate(payload: AuthentikPayload): Promise { // if ( !payload.email_verified ) { throw new UnauthorizedException(); } const user = await this.authentikAuthService.validateUser(payload.email); if ( !user ) { throw new UnauthorizedException(); } return user; } }