Skip to content

Commit

Permalink
Merge pull request #936 from InseeFrLab/oidc_next
Browse files Browse the repository at this point in the history
OIDC next
  • Loading branch information
garronej authored Mar 3, 2025
2 parents ff26c1f + 1916901 commit daf78e1
Show file tree
Hide file tree
Showing 43 changed files with 414 additions and 380 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"memoizee": "^0.4.17",
"minimal-polyfills": "^2.2.3",
"mui-icons-material-lazy": "^1.0.4",
"oidc-spa": "^6.1.15",
"oidc-spa": "^6.9.4",
"onyxia-ui": "^6.2.4",
"pathe": "^1.1.2",
"powerhooks": "^1.0.19",
Expand Down
5 changes: 4 additions & 1 deletion web/src/core/adapters/oidc/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ export async function createOidc(params: {
return oidc;
}

return { ...oidc, isAccessTokenSubstitutedWithIdToken: false };
return {
...oidc,
getTokens: async () => oidc.getTokens()
};
}
109 changes: 80 additions & 29 deletions web/src/core/adapters/oidc/oidc.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,99 @@
import type { Oidc } from "core/ports/Oidc";
import { createOidc as createOidcSpa } from "oidc-spa";
import { assert } from "tsafe/assert";
import { parseKeycloakIssuerUri } from "oidc-spa/tools/parseKeycloakIssuerUri";
import type { OidcParams, OidcParams_Partial } from "core/ports/OnyxiaApi";
import { objectKeys } from "tsafe/objectKeys";

export async function createOidc(params: {
issuerUri: string;
clientId: string;
transformUrlBeforeRedirect: (url: string) => string;
}): Promise<Oidc> {
const { issuerUri, clientId, transformUrlBeforeRedirect } = params;
export async function createOidc<AutoLogin extends boolean>(
params: OidcParams & {
transformUrlBeforeRedirect_ui: (params: {
isKeycloak: boolean;
authorizationUrl: string;
}) => string;
autoLogin: AutoLogin;
}
): Promise<AutoLogin extends true ? Oidc.LoggedIn : Oidc> {
const {
issuerUri,
clientId,
scope_spaceSeparated,
audience,
transformUrlBeforeRedirect_ui,
extraQueryParams_raw,
idleSessionLifetimeInSeconds,
autoLogin
} = params;

const oidc = await createOidcSpa({
issuerUri,
clientId,
transformUrlBeforeRedirect,
homeUrl: import.meta.env.BASE_URL,
debugLogs: false
});
scopes: scope_spaceSeparated?.split(" "),
transformUrlBeforeRedirect_next: ({ authorizationUrl, isSilent }) => {
if (!isSilent) {
authorizationUrl = transformUrlBeforeRedirect_ui({
isKeycloak:
parseKeycloakIssuerUri(oidc.params.issuerUri) !== undefined,
authorizationUrl
});
}

if (!oidc.isUserLoggedIn) {
return oidc;
}
if (audience !== undefined) {
const url_obj = new URL(authorizationUrl);

function getTokens_patched() {
assert(oidc.isUserLoggedIn);
url_obj.searchParams.set("audience", audience);

const tokens_real = oidc.getTokens();
authorizationUrl = url_obj.href;
}

if (!oidc_patched.isAccessTokenSubstitutedWithIdToken) {
return tokens_real;
}
if (extraQueryParams_raw !== undefined) {
const url_obj = new URL(authorizationUrl);
const extraUrlSearchParams = new URLSearchParams(extraQueryParams_raw);

for (const [key, value] of extraUrlSearchParams) {
url_obj.searchParams.set(key, value);
}

const tokens: ReturnType<Oidc.LoggedIn["getTokens"]> = {
...tokens_real,
accessToken: tokens_real.idToken
};
authorizationUrl = url_obj.href;
}

return authorizationUrl;
},
idleSessionLifetimeInSeconds,
homeUrl: import.meta.env.BASE_URL
});

return tokens;
if (!oidc.isUserLoggedIn) {
if (autoLogin) {
await oidc.login({ doesCurrentHrefRequiresAuth: true });
// NOTE: Never
}

//@ts-expect-error: We know what we are doing
return oidc;
}

const oidc_patched: Oidc.LoggedIn = {
return {
...oidc,
getTokens: getTokens_patched,
isAccessTokenSubstitutedWithIdToken: false
getTokens: () => oidc.getTokens_next()
};
}

export function mergeOidcParams(params: {
oidcParams: OidcParams;
oidcParams_partial: OidcParams_Partial;
}) {
const { oidcParams, oidcParams_partial } = params;

const oidcParams_merged = { ...oidcParams };

for (const key of objectKeys(oidcParams_partial)) {
const value = oidcParams_partial[key];
if (value === undefined) {
continue;
}
// @ts-expect-error
oidcParams_merged[key] = value;
}

return oidc_patched;
return oidcParams_merged;
}
68 changes: 0 additions & 68 deletions web/src/core/adapters/oidc/utils/createOidcOrFallback.ts

This file was deleted.

32 changes: 15 additions & 17 deletions web/src/core/adapters/onyxiaApi/ApiTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,7 @@ export type ApiTypes = {
};
initScript: string;
k8sPublicEndpoint: {
oidcConfiguration?: {
issuerURI?: string;
clientID: string;
};
oidcConfiguration?: Partial<ApiTypes.OidcConfiguration>;
URL?: string;
};
openshiftSCC?: {
Expand All @@ -97,10 +94,7 @@ export type ApiTypes = {
roleSessionName: string;
}
| undefined;
oidcConfiguration?: {
issuerURI?: string;
clientID: string;
};
oidcConfiguration?: Partial<ApiTypes.OidcConfiguration>;
};

/** Ok to be undefined only if sts is undefined */
Expand All @@ -123,10 +117,7 @@ export type ApiTypes = {
kvEngine: string;
role: string;
authPath?: string;
oidcConfiguration?: {
issuerURI?: string;
clientID: string;
};
oidcConfiguration?: Partial<ApiTypes.OidcConfiguration>;
};
proxyInjection?: {
enabled?: boolean;
Expand All @@ -146,11 +137,7 @@ export type ApiTypes = {
pathToCaBundle: string;
};
}[];
oidcConfiguration?: {
issuerURI: string;
clientID: string;
extraQueryParams?: string;
};
oidcConfiguration?: ApiTypes.OidcConfiguration;
};
"/<public|my-lab>/catalogs": {
catalogs: {
Expand Down Expand Up @@ -270,3 +257,14 @@ export type ApiTypes = {
type: "Normal" | "Warning" | string; // Allows specific types but also remains open for extension.
};
};

export namespace ApiTypes {
export type OidcConfiguration = {
issuerURI: string;
clientID: string;
extraQueryParams?: string;
scope?: string;
audience?: string;
idleSessionLifetimeInSeconds?: number | string;
};
}
Loading

0 comments on commit daf78e1

Please sign in to comment.