UNPKG

oidc-spa

Version:

Openidconnect client for Single Page Applications

213 lines 9.15 kB
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useState, createContext, useContext } from "react"; import { createOidc, OidcInitializationError } from "../core"; import { assert } from "../vendor/frontend/tsafe"; import { id } from "../vendor/frontend/tsafe"; import { Deferred } from "../tools/Deferred"; import { toFullyQualifiedUrl } from "../tools/toFullyQualifiedUrl"; { assert(); } export function createReactOidc_dependencyInjection(paramsOrGetParams, createOidc) { const dReadyToCreate = new Deferred(); const oidcContext = createContext(undefined); // NOTE: It can be InitializationError only if autoLogin is true const prOidcOrInitializationError = (async () => { const params = await (async () => { await dReadyToCreate.pr; if (typeof paramsOrGetParams === "function") { const getParams = paramsOrGetParams; const params = await getParams(); return params; } const params = paramsOrGetParams; return params; })(); let oidc; try { oidc = await createOidc(params); } catch (error) { if (!(error instanceof OidcInitializationError)) { throw error; } return error; } return oidc; })(); let prOidcOrInitializationError_resolvedValue = undefined; prOidcOrInitializationError.then(value => (prOidcOrInitializationError_resolvedValue = value)); function OidcProvider(props) { const { fallback, ErrorFallback, children } = props; const [oidcOrInitializationError, setOidcOrInitializationError] = useState(prOidcOrInitializationError_resolvedValue); useEffect(() => { if (oidcOrInitializationError !== undefined) { return; } dReadyToCreate.resolve(); prOidcOrInitializationError.then(setOidcOrInitializationError); }, []); if (oidcOrInitializationError === undefined) { return _jsx(_Fragment, { children: fallback === undefined ? null : fallback }); } if (oidcOrInitializationError instanceof OidcInitializationError) { const initializationError = oidcOrInitializationError; return (_jsx(_Fragment, { children: ErrorFallback === undefined ? (_jsxs("h1", { style: { color: "red" }, children: ["An error occurred while initializing the OIDC client:\u00A0", initializationError.message] })) : (_jsx(ErrorFallback, { initializationError: initializationError })) })); } const oidc = oidcOrInitializationError; return (_jsx(oidcContext.Provider, { value: { oidc, fallback: fallback ?? null }, children: children })); } const useAutoLogoutWarningCountdown = ({ warningDurationSeconds }) => { const contextValue = useContext(oidcContext); assert(contextValue !== undefined); const { oidc } = contextValue; const [secondsLeft, setSecondsLeft] = useState(undefined); useEffect(() => { if (!oidc.isUserLoggedIn) { return; } const { unsubscribeFromAutoLogoutCountdown } = oidc.subscribeToAutoLogoutCountdown(({ secondsLeft }) => setSecondsLeft(secondsLeft === undefined || secondsLeft > warningDurationSeconds ? undefined : secondsLeft)); return () => { unsubscribeFromAutoLogoutCountdown(); }; }, [warningDurationSeconds]); return { secondsLeft }; }; function useOidc(params) { const { assert: assert_params } = params ?? {}; const contextValue = useContext(oidcContext); assert(contextValue !== undefined, "You must use useOidc inside the corresponding OidcProvider"); const { oidc } = contextValue; check_assertion: { if (assert_params === undefined) { break check_assertion; } const getMessage = (v) => [ "There is a logic error in the application.", `If this component is mounted the user is supposed ${v}.`, "An explicit assertion was made in this sense." ].join(" "); switch (assert_params) { case "user logged in": if (!oidc.isUserLoggedIn) { throw new Error(getMessage("to be logged in but currently they arn't")); } break; case "user not logged in": if (oidc.isUserLoggedIn) { throw new Error(getMessage("not to be logged in but currently they are")); } break; default: assert(false); } } const [, reRenderIfDecodedIdTokenChanged] = useState(!oidc.isUserLoggedIn ? undefined : oidc.getDecodedIdToken()); useEffect(() => { if (!oidc.isUserLoggedIn) { return; } const { unsubscribe } = oidc.subscribeToTokensChange(() => reRenderIfDecodedIdTokenChanged(oidc.getDecodedIdToken())); reRenderIfDecodedIdTokenChanged(oidc.getDecodedIdToken()); return unsubscribe; }, []); const common = { params: oidc.params, useAutoLogoutWarningCountdown }; if (!oidc.isUserLoggedIn) { return id({ ...common, isUserLoggedIn: false, login: ({ doesCurrentHrefRequiresAuth = false, ...rest } = {}) => oidc.login({ doesCurrentHrefRequiresAuth, ...rest }), initializationError: oidc.initializationError }); } const oidcReact = { ...common, isUserLoggedIn: true, decodedIdToken: oidc.getDecodedIdToken(), logout: oidc.logout, renewTokens: oidc.renewTokens, goToAuthServer: oidc.goToAuthServer, isNewBrowserSession: oidc.isNewBrowserSession, backFromAuthServer: oidc.backFromAuthServer }; return oidcReact; } function withLoginEnforced(Component, params) { const { onRedirecting } = params ?? {}; function ComponentWithLoginEnforced(props) { const contextValue = useContext(oidcContext); assert(contextValue !== undefined, "094283"); const { oidc, fallback } = contextValue; useEffect(() => { if (oidc.isUserLoggedIn) { return; } oidc.login({ doesCurrentHrefRequiresAuth: true }); }, []); if (!oidc.isUserLoggedIn) { return onRedirecting === undefined ? fallback : onRedirecting(); } return _jsx(Component, { ...props }); } ComponentWithLoginEnforced.displayName = `${Component.displayName ?? Component.name ?? "Component"}WithLoginEnforced`; return ComponentWithLoginEnforced; } async function enforceLogin(loaderParams) { const { cause } = loaderParams; const redirectUrl = (() => { if (loaderParams.request?.url !== undefined) { return toFullyQualifiedUrl({ urlish: loaderParams.request.url, doAssertNoQueryParams: false }); } if (loaderParams.location?.href !== undefined) { return toFullyQualifiedUrl({ urlish: loaderParams.location.href, doAssertNoQueryParams: false }); } return location.href; })(); const oidc = await getOidc(); if (!oidc.isUserLoggedIn) { if (cause === "preload") { throw new Error("oidc-spa: User is not yet logged in. This is an expected error, nothing to be addressed."); } const doesCurrentHrefRequiresAuth = location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, ""); await oidc.login({ redirectUrl, doesCurrentHrefRequiresAuth }); } } async function getOidc() { dReadyToCreate.resolve(); const oidcOrInitializationError = await prOidcOrInitializationError; if (oidcOrInitializationError instanceof OidcInitializationError) { const error = oidcOrInitializationError; throw error; } const oidc = oidcOrInitializationError; return oidc; } const oidcReactApi = { OidcProvider, useOidc: useOidc, getOidc, withLoginEnforced, enforceLogin }; // @ts-expect-error: We know what we are doing return oidcReactApi; } /** @see: https://docs.oidc-spa.dev/v/v8/usage#react-api */ export function createReactOidc(params) { return createReactOidc_dependencyInjection(params, createOidc); } //# sourceMappingURL=react.js.map