aboutsummaryrefslogtreecommitdiffstats
path: root/src/App/oidc.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/App/oidc.tsx')
-rw-r--r--src/App/oidc.tsx213
1 files changed, 0 insertions, 213 deletions
diff --git a/src/App/oidc.tsx b/src/App/oidc.tsx
deleted file mode 100644
index 44eb9e1..0000000
--- a/src/App/oidc.tsx
+++ /dev/null
@@ -1,213 +0,0 @@
-import { useState, useContext, createContext, useEffect } from "react";
-import Keycloak_js from "keycloak-js";
-import { id } from "tsafe/id";
-import { addParamToUrl } from "powerhooks/tools/urlSearchParams";
-import type { ReturnType } from "tsafe/ReturnType";
-import type { Param0 } from "tsafe/Param0";
-import { assert } from "tsafe/assert";
-import { createKeycloakAdapter } from "keycloakify";
-import jwt_decode from "jwt-decode";
-import { Evt } from "evt";
-
-export declare type OidcClient = OidcClient.LoggedIn | OidcClient.NotLoggedIn;
-
-export declare namespace OidcClient {
- export type NotLoggedIn = {
- isUserLoggedIn: false;
- login: (params: {
- //To prevent infinite loop if the user access a page that requires to
- //be authenticated but cancel (clicks back).
- doesCurrentHrefRequiresAuth: boolean;
- }) => Promise<never>;
- };
-
- export type LoggedIn = {
- isUserLoggedIn: true;
- getAccessToken: () => string;
- logout: (params: { redirectTo: "home" | "current page" }) => Promise<never>;
- //If we have sent a API request to change user's email for example
- //and we want that jwt_decode(oidcClient.getAccessToken()).email be the new email
- //in this case we would call this method...
- updateTokenInfos: () => Promise<void>;
- };
-}
-
-type Params = {
- url: string;
- realm: string;
- clientId: string;
- transformUrlBeforeRedirect?: (url: string) => string;
- getUiLocales?: () => string;
- log?: typeof console.log;
-};
-
-async function createKeycloakOidcClient(params: Params): Promise<OidcClient> {
- const {
- url,
- realm,
- clientId,
- transformUrlBeforeRedirect,
- getUiLocales,
- log
- } = params;
-
- const keycloakInstance = new Keycloak_js({ url, realm, clientId });
-
- let redirectMethod: ReturnType<
- Param0<typeof createKeycloakAdapter>["getRedirectMethod"]
- > = "overwrite location.href";
-
- const isAuthenticated = await keycloakInstance
- .init({
- onLoad: "check-sso",
- silentCheckSsoRedirectUri: `${window.location.origin}/silent-sso.html`,
- responseMode: "query",
- checkLoginIframe: false,
- adapter: createKeycloakAdapter({
- transformUrlBeforeRedirect: url =>
- [url]
- .map(transformUrlBeforeRedirect ?? (url => url))
- .map(
- getUiLocales === undefined ?
- (url => url) :
- url =>
- addParamToUrl({
- url,
- "name": "ui_locales",
- "value": getUiLocales()
- }).newUrl
- )
- [0],
- keycloakInstance,
- getRedirectMethod: () => redirectMethod
- })
- })
- .catch((error: Error) => error);
-
- //TODO: Make sure that result is always an object.
- if (isAuthenticated instanceof Error) {
- throw isAuthenticated;
- }
-
- const login: OidcClient.NotLoggedIn["login"] = async ({
- doesCurrentHrefRequiresAuth
- }) => {
- if (doesCurrentHrefRequiresAuth) {
- redirectMethod = "location.replace";
- }
-
- await keycloakInstance.login({ "redirectUri": window.location.href });
-
- return new Promise<never>(() => { });
- };
-
- if (!isAuthenticated) {
- return id<OidcClient.NotLoggedIn>({
- "isUserLoggedIn": false,
- login
- });
- }
-
- let currentAccessToken = keycloakInstance.token!;
-
- const oidcClient = id<OidcClient.LoggedIn>({
- "isUserLoggedIn": true,
- "getAccessToken": () => currentAccessToken,
- "logout": async ({ redirectTo }) => {
- await keycloakInstance.logout({
- "redirectUri": (() => {
- switch (redirectTo) {
- case "current page":
- return window.location.href;
- case "home":
- return window.location.origin;
- }
- })()
- });
-
- return new Promise<never>(() => { });
- },
- "updateTokenInfos": async () => {
- await keycloakInstance.updateToken(-1);
-
- currentAccessToken = keycloakInstance.token!;
- }
- });
-
- (function callee() {
- const msBeforeExpiration = jwt_decode<{ exp: number }>(currentAccessToken)["exp"] * 1000 - Date.now();
-
- setTimeout(async () => {
-
- log?.(`OIDC access token will expire in ${minValiditySecond} seconds, waiting for user activity before renewing`);
-
- await Evt.merge([
- Evt.from(document, "mousemove"),
- Evt.from(document, "keydown")
- ]).waitFor();
-
- log?.("User activity detected. Refreshing access token now");
-
- const error = await keycloakInstance.updateToken(-1).then(
- () => undefined,
- (error: Error) => error
- );
-
- if (error) {
- log?.("Can't refresh OIDC access token, getting a new one");
- //NOTE: Never resolves
- await login({ "doesCurrentHrefRequiresAuth": true });
- }
-
- currentAccessToken = keycloakInstance.token!;
-
- callee();
-
- }, msBeforeExpiration - minValiditySecond * 1000);
- })();
-
- return oidcClient;
-}
-
-const minValiditySecond = 25;
-
-const oidcClientContext = createContext<OidcClient | undefined>(undefined);
-
-export function createOidcClientProvider(params: Params) {
-
-
- const prOidcClient = createKeycloakOidcClient(params);
-
- function OidcClientProvider(props: { children: React.ReactNode; }) {
-
- const { children } = props;
-
- const [oidcClient, setOidcClient] = useState<OidcClient | undefined>(undefined);
-
- useEffect(() => {
-
- prOidcClient.then(setOidcClient);
-
- }, []);
-
- if (oidcClient === undefined) {
- return null;
- }
-
- return (
- <oidcClientContext.Provider value={oidcClient}>
- {children}
- </oidcClientContext.Provider>
- );
-
- }
-
- return { OidcClientProvider };
-
-}
-
-export function useOidcClient() {
- const oidcClient = useContext(oidcClientContext);
- assert(oidcClient !== undefined);
- return { oidcClient };
-}