bento-auth-js
Version:
Authentication library for web applications of Bento-Platform
154 lines • 7.7 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { useCallback, useEffect } from "react";
import { useDispatch } from "react-redux";
import { useNavigate, useLocation } from "react-router-dom";
import { DEFAULT_AUTH_SCOPE, useBentoAuthContext } from "./contexts";
import { useIsAuthenticated, useOpenIdConfig } from "./hooks";
import { PKCE_LS_STATE, PKCE_LS_VERIFIER, pkceChallengeFromVerifier, secureRandomString } from "./pkce";
import { tokenHandoff } from "./redux/authSlice";
import { buildUrlEncodedData, logMissingAuthContext, popLocalStorageItem } from "./utils";
export const LS_SIGN_IN_POPUP = "BENTO_DID_CREATE_SIGN_IN_POPUP";
export const LS_BENTO_WAS_SIGNED_IN = "BENTO_WAS_SIGNED_IN";
export const LS_BENTO_POST_AUTH_REDIRECT = "BENTO_POST_AUTH_REDIRECT";
const DEFAULT_REDIRECT = "/overview";
export const createAuthURL = (authorizationEndpoint_1, clientId_1, authCallbackUrl_1, ...args_1) => __awaiter(void 0, [authorizationEndpoint_1, clientId_1, authCallbackUrl_1, ...args_1], void 0, function* (authorizationEndpoint, clientId, authCallbackUrl, scope = "openid email") {
const state = secureRandomString();
const verifier = secureRandomString();
localStorage.setItem(PKCE_LS_STATE, state);
localStorage.setItem(PKCE_LS_VERIFIER, verifier);
const { pathname, search, hash } = window.location;
localStorage.setItem(LS_BENTO_POST_AUTH_REDIRECT, `${pathname}${search}${hash}`);
return (`${authorizationEndpoint}?` +
buildUrlEncodedData({
response_type: "code",
client_id: clientId,
state,
scope,
redirect_uri: authCallbackUrl,
code_challenge: yield pkceChallengeFromVerifier(verifier),
code_challenge_method: "S256",
}).toString());
});
export const performAuth = (authorizationEndpoint_1, clientId_1, authCallbackUrl_1, ...args_1) => __awaiter(void 0, [authorizationEndpoint_1, clientId_1, authCallbackUrl_1, ...args_1], void 0, function* (authorizationEndpoint, clientId, authCallbackUrl, scope = "openid email") {
window.location.href = yield createAuthURL(authorizationEndpoint, clientId, authCallbackUrl, scope);
});
export const usePerformAuth = () => {
const { authCallbackUrl, clientId, scope } = useBentoAuthContext();
const { data: openIdConfig } = useOpenIdConfig();
const authorizationEndpoint = openIdConfig === null || openIdConfig === void 0 ? void 0 : openIdConfig["authorization_endpoint"];
return useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
if (!authCallbackUrl || !clientId) {
logMissingAuthContext("authCallbackUrl", "clientId");
throw new Error("Could not create auth URL; missing authCallbackUrl or clientId");
}
if (!authorizationEndpoint)
throw new Error("Could not create auth URL; missing authorization_endpoint");
window.location.href = yield createAuthURL(authorizationEndpoint, clientId, authCallbackUrl, scope !== null && scope !== void 0 ? scope : DEFAULT_AUTH_SCOPE);
}), [authCallbackUrl, clientId, authorizationEndpoint, scope]);
};
const useDefaultAuthCodeCallback = (onSuccessfulAuthentication) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { authCallbackUrl, clientId } = useBentoAuthContext();
return useCallback((code, verifier) => __awaiter(void 0, void 0, void 0, function* () {
if (!authCallbackUrl || !clientId) {
logMissingAuthContext("authCallbackUrl", "clientId");
return;
}
const lastPath = popLocalStorageItem(LS_BENTO_POST_AUTH_REDIRECT);
yield dispatch(tokenHandoff({ code, verifier, clientId, authCallbackUrl }));
navigate(lastPath !== null && lastPath !== void 0 ? lastPath : DEFAULT_REDIRECT, { replace: true });
yield onSuccessfulAuthentication();
}), [dispatch, navigate, authCallbackUrl, clientId, onSuccessfulAuthentication]);
};
export const setLSNotSignedIn = () => {
localStorage.removeItem(LS_BENTO_WAS_SIGNED_IN);
};
export const useHandleCallback = (callbackPath, onSuccessfulAuthentication, authCodeCallback = undefined, uiErrorCallback) => {
const navigate = useNavigate();
const location = useLocation();
const { authCallbackUrl, clientId } = useBentoAuthContext();
const { data: oidcConfig } = useOpenIdConfig();
const isAuthenticated = useIsAuthenticated();
const defaultAuthCodeCallback = useDefaultAuthCodeCallback(onSuccessfulAuthentication);
useEffect(() => {
var _a;
// Not used directly in this effect, but if we don't have it our auth callback / token handoff presumably won't
// work properly, so we terminate early.
if (!authCallbackUrl || !clientId) {
logMissingAuthContext("authCallbackUrl", "clientId");
return;
}
// Ignore non-callback URLs
if (!location.pathname.startsWith(callbackPath))
return;
// End early if we don't have OpenID config (yet)
if (!oidcConfig)
return;
// If we're already authenticated, don't try to reauthenticate
if (isAuthenticated) {
navigate(DEFAULT_REDIRECT, { replace: true });
return;
}
const params = new URLSearchParams(window.location.search);
const error = params.get("error");
if (error) {
uiErrorCallback(`Error encountered during sign-in: ${error}`);
console.error(error);
setLSNotSignedIn();
return;
}
const code = params.get("code");
if (!code) {
// No code, don't do anything
setLSNotSignedIn();
return;
}
const localState = popLocalStorageItem(PKCE_LS_STATE);
if (!localState) {
console.error("no local state");
setLSNotSignedIn();
return;
}
const paramState = params.get("state");
if (localState !== paramState) {
console.error("state mismatch");
setLSNotSignedIn();
return;
}
const verifier = (_a = popLocalStorageItem(PKCE_LS_VERIFIER)) !== null && _a !== void 0 ? _a : "";
(authCodeCallback !== null && authCodeCallback !== void 0 ? authCodeCallback : defaultAuthCodeCallback)(code, verifier).catch((err) => {
console.error(err);
setLSNotSignedIn();
});
}, [
authCallbackUrl,
authCodeCallback,
callbackPath,
clientId,
defaultAuthCodeCallback,
isAuthenticated,
location,
navigate,
oidcConfig,
uiErrorCallback,
]);
};
export const checkIsInAuthPopup = (applicationUrl) => {
try {
const didCreateSignInPopup = localStorage.getItem(LS_SIGN_IN_POPUP);
return window.opener && window.opener.origin === applicationUrl && didCreateSignInPopup === "true";
}
catch (_a) {
return false;
}
};
//# sourceMappingURL=performAuth.js.map