oidc-spa
Version:
Openidconnect client for Single Page Applications
213 lines • 9.15 kB
JavaScript
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