groups und impersination
This commit is contained in:
@@ -12,11 +12,13 @@ export interface OidcProfile {
|
||||
subject: string;
|
||||
email: string;
|
||||
name?: string;
|
||||
groups: string[];
|
||||
}
|
||||
|
||||
interface OidcDiscovery {
|
||||
authorization_endpoint: string;
|
||||
token_endpoint: string;
|
||||
userinfo_endpoint?: string;
|
||||
jwks_uri: string;
|
||||
issuer: string;
|
||||
}
|
||||
@@ -29,6 +31,7 @@ interface PendingOidcState {
|
||||
|
||||
interface TokenResponse {
|
||||
id_token?: string;
|
||||
access_token?: string;
|
||||
error?: string;
|
||||
error_description?: string;
|
||||
}
|
||||
@@ -59,7 +62,7 @@ export class OidcService {
|
||||
authorizationUrl.searchParams.set('response_type', 'code');
|
||||
authorizationUrl.searchParams.set('client_id', config.clientId);
|
||||
authorizationUrl.searchParams.set('redirect_uri', config.callbackUrl);
|
||||
authorizationUrl.searchParams.set('scope', 'openid email profile');
|
||||
authorizationUrl.searchParams.set('scope', config.scope);
|
||||
authorizationUrl.searchParams.set('state', state);
|
||||
authorizationUrl.searchParams.set('nonce', nonce);
|
||||
authorizationUrl.searchParams.set('code_challenge', codeChallenge);
|
||||
@@ -89,6 +92,7 @@ export class OidcService {
|
||||
pendingState.codeVerifier,
|
||||
);
|
||||
|
||||
console.log(tokenResponse)
|
||||
if (!tokenResponse.id_token) {
|
||||
throw new ServiceUnavailableException(
|
||||
tokenResponse.error_description ??
|
||||
@@ -120,10 +124,19 @@ export class OidcService {
|
||||
throw new BadRequestException('OIDC email claim is missing.');
|
||||
}
|
||||
|
||||
const idTokenGroups = this.extractGroups(payload, config.groupsClaim);
|
||||
|
||||
return {
|
||||
subject: payload.sub,
|
||||
email,
|
||||
name: typeof payload.name === 'string' ? payload.name : undefined,
|
||||
groups: idTokenGroups.length
|
||||
? idTokenGroups
|
||||
: await this.requestUserInfoGroups(
|
||||
discovery.userinfo_endpoint,
|
||||
tokenResponse.access_token,
|
||||
config.groupsClaim,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -210,9 +223,53 @@ export class OidcService {
|
||||
clientId,
|
||||
callbackUrl,
|
||||
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
||||
scope: process.env.OIDC_SCOPE ?? 'openid email profile',
|
||||
groupsClaim: process.env.OIDC_GROUPS_CLAIM ?? 'groups',
|
||||
};
|
||||
}
|
||||
|
||||
private async requestUserInfoGroups(
|
||||
userInfoEndpoint: string | undefined,
|
||||
accessToken: string | undefined,
|
||||
groupsClaim: string,
|
||||
): Promise<string[]> {
|
||||
if (!userInfoEndpoint || !accessToken) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const response = await fetch(userInfoEndpoint, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const payload = (await response.json().catch(() => ({}))) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
return this.extractGroups(payload, groupsClaim);
|
||||
}
|
||||
|
||||
private extractGroups(
|
||||
payload: Record<string, unknown>,
|
||||
groupsClaim: string,
|
||||
): string[] {
|
||||
const claimValue = payload[groupsClaim];
|
||||
|
||||
if (!Array.isArray(claimValue)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [...new Set(claimValue)]
|
||||
.filter((group): group is string => typeof group === 'string')
|
||||
.map((group) => group.trim())
|
||||
.filter(Boolean)
|
||||
.sort((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
private createOpaqueToken(): string {
|
||||
return randomBytes(32).toString('base64url');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user