2026-04-17 13:34:57 +01:00
|
|
|
import { Injectable } from "@nestjs/common";
|
|
|
|
|
import { PassportStrategy } from "@nestjs/passport";
|
|
|
|
|
import { ExtractJwt, Strategy } from "passport-jwt";
|
|
|
|
|
import * as jwksRsa from "jwks-rsa";
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class KeycloakStrategy extends PassportStrategy(Strategy, "keycloak") {
|
|
|
|
|
constructor() {
|
|
|
|
|
super({
|
|
|
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
|
|
|
|
|
|
|
|
// 🔑 Get signing key from Keycloak
|
|
|
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
|
|
|
cache: true,
|
|
|
|
|
rateLimit: true,
|
|
|
|
|
jwksRequestsPerMinute: 5,
|
|
|
|
|
jwksUri:
|
|
|
|
|
"https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/certs",
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
//audience: "tvone-web", // your Keycloak clientId
|
|
|
|
|
issuer: "https://keycloak.petermaquiran.xyz/realms/tvone",
|
|
|
|
|
algorithms: ["RS256"],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async validate(payload: any) {
|
|
|
|
|
return {
|
|
|
|
|
userId: payload.sub,
|
|
|
|
|
email: payload.email,
|
2026-04-17 14:53:21 +01:00
|
|
|
name: payload.name,
|
|
|
|
|
// Google profile image is usually in 'picture'
|
|
|
|
|
email_verified: payload.email_verified,
|
2026-04-17 19:23:43 +01:00
|
|
|
picture: payload.picture,
|
2026-04-17 13:34:57 +01:00
|
|
|
roles: payload.realm_access?.roles || [],
|
2026-04-17 14:53:21 +01:00
|
|
|
// Keep raw for debugging other custom claims
|
2026-04-17 13:34:57 +01:00
|
|
|
raw: payload,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|