feat(backend): tolerate Authentik users without an email + verify against real SSO
Authentik accounts don't always have an email set (the test account `hermes` doesn't). AuthentikStrategy / verifyAuthentikClaims no longer reject those — `authentikEmail()` falls back to a stable `<preferred_username|sub>@no-email.authentik` handle for the local User row, and first/last name fall back to preferred_username/name. Set AUTHENTIK_LEITUNGSTEAM_GROUP to the real group "KC-APP-LT". Verified end to end against the live https://sso.konfi-castle.com with a password-grant token for a KC-APP-LT member: backend accepts the RS256 token (JWKS + trailing-slash issuer), JIT-provisions the User, maps the `groups` claim to isLeitungsteam=true, and POST /api/kc returns 201. Only the in-browser redirect round-trip remains untested. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { GuestJwtPayload } from './guest-auth.service';
|
||||
import { TeamAuthService } from './team-auth.service';
|
||||
import {
|
||||
AuthentikClaims,
|
||||
authentikEmail,
|
||||
resolveOrProvisionAuthentikUser,
|
||||
toAuthenticatedUser,
|
||||
} from './provision-user';
|
||||
@@ -55,15 +56,22 @@ export class TokenVerificationService {
|
||||
email?: string;
|
||||
given_name?: string;
|
||||
family_name?: string;
|
||||
preferred_username?: string;
|
||||
name?: string;
|
||||
groups?: string[];
|
||||
};
|
||||
if (!payload.sub || !payload.email) {
|
||||
throw new UnauthorizedException('Authentik token missing subject or email');
|
||||
const sub = payload.sub;
|
||||
if (!sub) {
|
||||
throw new UnauthorizedException('Authentik token missing subject');
|
||||
}
|
||||
return {
|
||||
sub: payload.sub,
|
||||
email: payload.email,
|
||||
firstName: payload.given_name ?? '',
|
||||
sub,
|
||||
email: authentikEmail({
|
||||
email: payload.email,
|
||||
preferred_username: payload.preferred_username,
|
||||
sub,
|
||||
}),
|
||||
firstName: payload.given_name ?? payload.preferred_username ?? payload.name ?? '',
|
||||
lastName: payload.family_name ?? '',
|
||||
isLeitungsteam: (payload.groups ?? []).includes(this.leitungsteamGroup),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user