UNPKG

@nibssplc/cams-sdk-react

Version:

React hooks and components for NIBSS CAMS SDK

1,077 lines (1,050 loc) 307 kB
'use strict'; var React = require('react'); var camsSdk = require('@nibssplc/cams-sdk'); var msalReact = require('@azure/msal-react'); var msalBrowser = require('@azure/msal-browser'); var jsxRuntime = require('react/jsx-runtime'); var z = require('zod'); var inputOtp = require('input-otp'); var lucideReact = require('lucide-react'); var reactHookForm = require('react-hook-form'); var resolvers = require('@hookform/resolvers'); var clsx = require('clsx'); var tailwindMerge = require('tailwind-merge'); var reactSlot = require('@radix-ui/react-slot'); var LabelPrimitive = require('@radix-ui/react-label'); var classVarianceAuthority = require('class-variance-authority'); var DialogPrimitive = require('@radix-ui/react-dialog'); var sonner = require('sonner'); var framerMotion = require('framer-motion'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespaceDefault(LabelPrimitive); var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespaceDefault(DialogPrimitive); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __awaiter$1(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()); }); } function __generator$1(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; function useCAMSAuth(options) { var _this = this; var _a; if (options === void 0) { options = { appCode: "", loginExpiry: 1, storageKey: "CAMS-AUTH-SDK", }; } var _b = React.useState(false), isAuthenticated = _b[0], setIsAuthenticated = _b[1]; var _c = React.useState(false), isLoading = _c[0], setIsLoading = _c[1]; var _d = React.useState(null), error = _d[0], setError = _d[1]; var _e = React.useState(""), token = _e[0], setToken = _e[1]; var _f = React.useState(null), profile = _f[0], setProfile = _f[1]; var sessionManagerRef = React.useRef(null); React.useEffect(function () { var _a, _b; // Only initialize on client side if (typeof window === "undefined") return; (_a = sessionManagerRef.current) !== null && _a !== void 0 ? _a : (sessionManagerRef.current = new camsSdk.CAMSSessionManager(localStorage, options.storageKey || "CAMS-AUTH-SDK", { onAuthSuccess: function (res) { var _a; setToken(res.userProfile.tokens.Bearer); setIsAuthenticated(true); setIsLoading(false); (_a = options.onAuthSuccess) === null || _a === void 0 ? void 0 : _a.call(options, res.userProfile.tokens.Bearer); }, onAuthError: function (error) { var _a; setError(error); setIsAuthenticated(false); setToken(""); setProfile(null); setIsLoading(false); (_a = options.onAuthError) === null || _a === void 0 ? void 0 : _a.call(options, error); }, onTokenExpired: function () { var _a; setIsAuthenticated(false); setToken(""); setProfile(null); (_a = options.onTokenExpired) === null || _a === void 0 ? void 0 : _a.call(options); }, })); // Check initial auth state (skip if in popup) if (!camsSdk.isPopupWindow()) { var initialAuth = sessionManagerRef.current.isAuthenticated(); setIsAuthenticated(initialAuth); if (initialAuth) { setToken((_b = sessionManagerRef.current.getAccessToken()) !== null && _b !== void 0 ? _b : ""); sessionManagerRef.current.getProfile().then(setProfile); } } }, [options.storageKey]); var login = React.useCallback(function (config) { return __awaiter$1(_this, void 0, void 0, function () { var loginConfig, userProfile, err_1, e, isPopupClosedError, restoredToken, userProfile; var _a, _b, _c; return __generator$1(this, function (_d) { switch (_d.label) { case 0: if (!sessionManagerRef.current) return [2 /*return*/]; setIsLoading(true); setError(null); _d.label = 1; case 1: _d.trys.push([1, 4, 8, 9]); loginConfig = __assign(__assign({}, config), { idleTimeout: options.idleTimeout }); return [4 /*yield*/, sessionManagerRef.current.login(loginConfig)]; case 2: _d.sent(); return [4 /*yield*/, sessionManagerRef.current.getProfile()]; case 3: userProfile = _d.sent(); setProfile(userProfile); return [3 /*break*/, 9]; case 4: err_1 = _d.sent(); e = err_1; isPopupClosedError = ((_a = e === null || e === void 0 ? void 0 : e.message) === null || _a === void 0 ? void 0 : _a.includes("Authentication window was closed")) || (e === null || e === void 0 ? void 0 : e.error) === "Authentication window was closed"; if (!(isPopupClosedError && sessionManagerRef.current.isAuthenticated())) return [3 /*break*/, 6]; restoredToken = (_b = sessionManagerRef.current.getAccessToken()) !== null && _b !== void 0 ? _b : ""; setToken(restoredToken); setIsAuthenticated(true); return [4 /*yield*/, sessionManagerRef.current.getProfile()]; case 5: userProfile = _d.sent(); setProfile(userProfile); (_c = options.onAuthSuccess) === null || _c === void 0 ? void 0 : _c.call(options, restoredToken); return [3 /*break*/, 7]; case 6: setError(err_1); setIsAuthenticated(false); setToken(""); setProfile(null); _d.label = 7; case 7: return [3 /*break*/, 9]; case 8: setIsLoading(false); return [7 /*endfinally*/]; case 9: return [2 /*return*/]; } }); }); }, [options.idleTimeout]); var logout = React.useCallback(function () { return __awaiter$1(_this, void 0, void 0, function () { return __generator$1(this, function (_a) { switch (_a.label) { case 0: if (!sessionManagerRef.current) return [2 /*return*/]; return [4 /*yield*/, sessionManagerRef.current.logout()]; case 1: _a.sent(); setIsAuthenticated(false); setToken(""); setProfile(null); setError(null); return [2 /*return*/]; } }); }); }, []); return { login: login, logout: logout, isAuthenticated: isAuthenticated, isLoading: isLoading, error: error, token: token, profile: profile, loginExpiry: (_a = options.loginExpiry) !== null && _a !== void 0 ? _a : 1, appCode: options.appCode, storageKey: options.storageKey || "CAMS-AUTH-SDK", }; } var _a; // Crypto polyfill for MSAL browser compatibility // Only apply if crypto.subtle is completely missing if (typeof window !== "undefined" && !((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) { // Ensure crypto object exists if (!window.crypto) { window.crypto = {}; } // Polyfill getRandomValues if (!window.crypto.getRandomValues) { window.crypto.getRandomValues = function (array) { var bytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength); for (var i = 0; i < bytes.length; i++) { bytes[i] = Math.floor(Math.random() * 256); } return array; }; } var sha256_1 = function (data) { return __awaiter(void 0, void 0, void 0, function () { var toUint8, bytes, hash, k, ml, msg, dv, high, low, i, w, j, j, s0, s1, a, b, c, d, e, f, g, h, j, S1, ch, temp1, S0, maj, temp2, result, i; return __generator(this, function (_a) { toUint8 = function (src) { if (src instanceof ArrayBuffer) return new Uint8Array(src); if (ArrayBuffer.isView(src)) { var view = src; return new Uint8Array(view.buffer, view.byteOffset || 0, view.byteLength); } throw new TypeError("Unsupported BufferSource"); }; bytes = toUint8(data); hash = new Uint32Array(8); k = new Uint32Array([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, ]); hash[0] = 0x6a09e667; hash[1] = 0xbb67ae85; hash[2] = 0x3c6ef372; hash[3] = 0xa54ff53a; hash[4] = 0x510e527f; hash[5] = 0x9b05688c; hash[6] = 0x1f83d9ab; hash[7] = 0x5be0cd19; ml = bytes.length * 8; msg = new Uint8Array(bytes.length + 64 + ((64 - ((bytes.length + 9) % 64)) % 64)); msg.set(bytes); msg[bytes.length] = 0x80; dv = new DataView(msg.buffer); high = Math.floor(ml / 0x100000000); low = ml >>> 0; dv.setUint32(msg.length - 8, high, false); dv.setUint32(msg.length - 4, low, false); for (i = 0; i < msg.length; i += 64) { w = new Uint32Array(64); for (j = 0; j < 16; j++) w[j] = new DataView(msg.buffer).getUint32(i + j * 4, false); for (j = 16; j < 64; j++) { s0 = ((w[j - 15] >>> 7) | (w[j - 15] << 25)) ^ ((w[j - 15] >>> 18) | (w[j - 15] << 14)) ^ (w[j - 15] >>> 3); s1 = ((w[j - 2] >>> 17) | (w[j - 2] << 15)) ^ ((w[j - 2] >>> 19) | (w[j - 2] << 13)) ^ (w[j - 2] >>> 10); w[j] = (w[j - 16] + s0 + w[j - 7] + s1) >>> 0; } a = hash[0], b = hash[1], c = hash[2], d = hash[3], e = hash[4], f = hash[5], g = hash[6], h = hash[7]; for (j = 0; j < 64; j++) { S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); ch = (e & f) ^ (~e & g); temp1 = (h + S1 + ch + k[j] + w[j]) >>> 0; S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); maj = (a & b) ^ (a & c) ^ (b & c); temp2 = (S0 + maj) >>> 0; h = g; g = f; f = e; e = (d + temp1) >>> 0; d = c; c = b; b = a; a = (temp1 + temp2) >>> 0; } hash[0] = (hash[0] + a) >>> 0; hash[1] = (hash[1] + b) >>> 0; hash[2] = (hash[2] + c) >>> 0; hash[3] = (hash[3] + d) >>> 0; hash[4] = (hash[4] + e) >>> 0; hash[5] = (hash[5] + f) >>> 0; hash[6] = (hash[6] + g) >>> 0; hash[7] = (hash[7] + h) >>> 0; } result = new Uint8Array(32); for (i = 0; i < 8; i++) new DataView(result.buffer).setUint32(i * 4, hash[i], false); return [2 /*return*/, result.buffer]; }); }); }; // Create the polyfilled subtle object var polyfillSubtle = { digest: function (algorithm, data) { return __awaiter(void 0, void 0, void 0, function () { var alg; return __generator(this, function (_a) { alg = typeof algorithm === "string" ? algorithm : algorithm.name; if (alg === "SHA-256") return [2 /*return*/, sha256_1(data)]; throw new Error("Unsupported algorithm: ".concat(alg)); }); }); }, }; // Set the polyfilled subtle object window.crypto.subtle = polyfillSubtle; // Polyfill randomUUID if (!window.crypto.randomUUID) { window.crypto.randomUUID = function () { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { var r = (Math.random() * 16) | 0; var v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); }; } console.log("Crypto polyfill applied (native crypto.subtle not available)"); } var setCookie = function (name, value, days) { if (days === void 0) { days = 1; } var expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString(); console.log("Auth Cookie Expires >>>", { name: name, value: value, expires: expires }); document.cookie = "".concat(name, "=").concat(encodeURIComponent(value), "; expires=").concat(expires, "; path=/; samesite=Lax"); }; var getCookie = function (name) { var _a; return ((_a = document.cookie .split("; ") .find(function (row) { return row.startsWith(name + "="); })) === null || _a === void 0 ? void 0 : _a.split("=")[1]) ? decodeURIComponent(document.cookie .split("; ") .find(function (row) { return row.startsWith(name + "="); }) .split("=")[1]) : null; }; var deleteCookie = function (name) { document.cookie = name + "=; Max-Age=-99999999; path=/"; }; function useCAMSMSALAuth(options) { var _this = this; var optStorageKey = options.storageKey, optScopes = options.scopes, prompt = options.prompt, appCode = options.appCode, ValidateUserEndpoint = options.ValidateUserEndpoint, _a = options.activeCookiePeriod, activeCookiePeriod = _a === void 0 ? 1 : _a; var storageKey = optStorageKey || "CAMS-MSAL-AUTH-SDK"; var _b = msalReact.useMsal(), instance = _b.instance, inProgress = _b.inProgress, accounts = _b.accounts; var account = msalReact.useAccount(accounts[0] || {}); var _c = React.useState(null), error = _c[0], setError = _c[1]; var _d = React.useState(""), idToken = _d[0], setIdToken = _d[1]; var _e = React.useState(""), accessToken = _e[0], setAccessToken = _e[1]; // const [mfaAuthenticator, setMfaAuthenticator] = // useState<CAMSMFAAuthenticator | null>(null); var _f = React.useState(false), requiresMFA = _f[0], setRequiresMFA = _f[1]; var isLoading = inProgress !== msalBrowser.InteractionStatus.None; var isAuthenticated = !!account && !!accessToken && !requiresMFA; var scopes = optScopes || ["openid", "profile", "email"]; var isTokenValid = function (token) { try { var payload = JSON.parse(atob(token.split(".")[1])); return payload.exp * 1000 > Date.now(); } catch (_a) { return false; } }; React.useEffect(function () { if (typeof window !== "undefined" && !accessToken) { var stored = localStorage.getItem(storageKey); if (stored) { try { var _a = JSON.parse(stored), accessToken_1 = _a.accessToken, idToken_1 = _a.idToken, storedRequiresMFA = _a.requiresMFA, storedIsAuthenticated = _a.isAuthenticated; // Restore MFA state if (accessToken_1 && isTokenValid(accessToken_1)) { setAccessToken(accessToken_1); setIdToken(idToken_1); setRequiresMFA(storedRequiresMFA); camsSdk.Logger.debug("Restored authentication state from storage", { accessToken: accessToken_1, idToken: idToken_1, requiresMFA: storedRequiresMFA, isAuthenticated: storedIsAuthenticated, }); } else { localStorage.removeItem(storageKey); } } catch (_b) { } } else if (account) { // Storage cleared but MSAL account exists - clear MSAL state instance.logoutRedirect().catch(function () { }); } } }, [accessToken, account, instance, options.storageKey]); var login = React.useCallback(function () { return __awaiter$1(_this, void 0, void 0, function () { var response, mfaConfig, authenticator, userConfig, err_1, camsError_1, camsError; var _a; return __generator$1(this, function (_b) { switch (_b.label) { case 0: if (inProgress !== msalBrowser.InteractionStatus.None) { camsSdk.Logger.warn("Authentication already in progress, ignoring duplicate call"); return [2 /*return*/]; } setError(null); _b.label = 1; case 1: _b.trys.push([1, 4, , 5]); return [4 /*yield*/, instance.loginPopup({ scopes: scopes, prompt: prompt || "login", })]; case 2: response = _b.sent(); console.log("Login Token response:", { accessToken: response.accessToken, idToken: response.idToken, }); setAccessToken(response.accessToken); setIdToken(response.idToken); mfaConfig = { accessToken: response.accessToken, idToken: response.idToken, provider: "MSAL", APIAuthEndpoint: ValidateUserEndpoint, }; authenticator = new camsSdk.CAMSMFAAuthenticator(); return [4 /*yield*/, authenticator.GetUserMFAConfig(mfaConfig)]; case 3: userConfig = _b.sent(); camsSdk.Logger.debug("MFA Authenticator initialized:", userConfig); console.log("MFA Authenticator initialized:", userConfig); // Don't persist as authenticated until MFA is complete if (typeof window !== "undefined" && userConfig.isValid) { setRequiresMFA(userConfig.userInfo.isMFAEnabled); localStorage.setItem(storageKey, JSON.stringify({ isAuthenticated: false, requiresMFA: userConfig.userInfo.isMFAEnabled, accessToken: response.accessToken, idToken: response.idToken, })); } return [3 /*break*/, 5]; case 4: err_1 = _b.sent(); // Handle interaction_in_progress error if (err_1.errorCode === "interaction_in_progress") { camsSdk.Logger.warn("Interaction already in progress, please wait"); return [2 /*return*/]; } // Handle user cancellation gracefully if (err_1.errorCode === "user_cancelled") { camsSdk.Logger.error("User cancelled login"); setError(null); // Don't treat cancellation as an error return [2 /*return*/]; } // If popup is blocked if (err_1.errorCode === "popup_window_error" || ((_a = err_1.message) === null || _a === void 0 ? void 0 : _a.includes("popup"))) { camsError_1 = new camsSdk.CAMSError(camsSdk.CAMSErrorType.POPUP_BLOCKED, "Popup blocked by browser. Please allow popups and try again."); setError(camsError_1); return [2 /*return*/]; } camsError = new camsSdk.CAMSError(camsSdk.CAMSErrorType.API_VALIDATION_ERROR, "Login failed: " + err_1.message || err_1); setError(camsError); return [3 /*break*/, 5]; case 5: return [2 /*return*/]; } }); }); }, [ instance, scopes, prompt, appCode, ValidateUserEndpoint, storageKey, inProgress, ]); var completeMFA = React.useCallback(function (data) { return __awaiter$1(_this, void 0, void 0, function () { return __generator$1(this, function (_a) { camsSdk.Logger.info("Completed MFA.. Setting State"); try { // Update storage with complete authentication BEFORE setting state if (typeof window !== "undefined") { localStorage.setItem(storageKey, JSON.stringify({ isAuthenticated: true, requiresMFA: false, accessToken: accessToken, idToken: idToken, })); setCookie("CAMS-MSAL-AUTH-SDK-PROFILE", JSON.stringify({ state: "AUTH_SUCCESS", role: data.userInfo.role, profile: __assign({}, data), }), activeCookiePeriod); setRequiresMFA(false); // Set requiresMFA to false after storage update camsSdk.Logger.debug("MFA completed successfully, storage updated", { accessToken: accessToken, idToken: idToken, isAuthenticated: true, requiresMFA: false, }); } camsSdk.Logger.debug("MFA completed successfully, requiresMFA set to false"); return [2 /*return*/, data]; } catch (error) { setError(error); throw error; } return [2 /*return*/]; }); }); }, [accessToken, idToken, storageKey, activeCookiePeriod]); var LoginADCredentials = React.useCallback(function (credentials, appCode, CredentialsAuthEndpoint) { return __awaiter$1(_this, void 0, void 0, function () { var authenticator; return __generator$1(this, function (_a) { switch (_a.label) { case 0: authenticator = new camsSdk.CAMSMFAAuthenticator(); return [4 /*yield*/, authenticator.LoginADCredentials(credentials, CredentialsAuthEndpoint)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }, []); var logout = React.useCallback(function () { return __awaiter$1(_this, void 0, void 0, function () { var err_2, camsError; return __generator$1(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); camsSdk.Logger.info("Logging User Out"); return [4 /*yield*/, instance.logoutRedirect()]; case 1: _a.sent(); setAccessToken(""); setIdToken(""); setError(null); setRequiresMFA(false); if (typeof window !== "undefined") { localStorage.removeItem(storageKey); // deleteCookie("CAMS-MSAL-AUTH-SDK-PROFILE"); } return [3 /*break*/, 3]; case 2: err_2 = _a.sent(); camsError = new camsSdk.CAMSError(camsSdk.CAMSErrorType.API_VALIDATION_ERROR, "Logout failed: " + err_2); setError(camsError); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }, [instance, storageKey]); return { login: login, logout: logout, storageKey: storageKey, isAuthenticated: isAuthenticated, isLoading: isLoading, error: error, idToken: idToken, accessToken: accessToken, appCode: appCode, requiresMFA: requiresMFA, completeMFA: completeMFA, LoginADCredentials: LoginADCredentials, setRequiresMFA: setRequiresMFA, activeCookiePeriod: activeCookiePeriod, }; } /** * Converts a base64url-encoded string to an ArrayBuffer. * @param base64url The base64url-encoded string */ function base64urlToArrayBuffer(base64url) { var base64 = base64url.replace(/-/g, "+").replace(/_/g, "/"); var padLength = (4 - (base64.length % 4)) % 4; var padded = base64 + "=".repeat(padLength); var binaryStr = atob(padded); var buffer = new ArrayBuffer(binaryStr.length); var bytes = new Uint8Array(buffer); for (var i = 0; i < binaryStr.length; i++) { bytes[i] = binaryStr.charCodeAt(i); } return buffer; } /** * Converts an ArrayBuffer to a base64url-encoded string. * @param buffer The ArrayBuffer to convert */ function arrayBufferToBase64url(buffer) { var bytes = new Uint8Array(buffer); var binaryStr = Array.from(bytes, function (b) { return String.fromCharCode(b); }).join(""); return btoa(binaryStr) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); // use regex to remove all trailing '=' } /** * Initiates the WebAuthn registration process. * It takes server-provided options, converts them for the browser API, * calls navigator.credentials.create(), and then converts the result * back into a JSON-friendly format. * * @param options - The PublicKeyCredentialCreationOptions from the server. * @returns A promise that resolves to a JSON-serializable representation of the PublicKeyCredential. */ function register(options) { return __awaiter$1(this, void 0, void 0, function () { var createOptions, credential, publicKeyCredential, attestationResponse, transports, err_1; var _a, _b, _c, _d, _e, _f, _g, _h, _j; return __generator$1(this, function (_k) { switch (_k.label) { case 0: _k.trys.push([0, 2, , 3]); createOptions = __assign(__assign({}, options), { challenge: base64urlToArrayBuffer(options.challenge), user: __assign(__assign({}, options.user), { id: base64urlToArrayBuffer(options.user.id) }) }); return [4 /*yield*/, navigator.credentials.create({ publicKey: __assign(__assign({}, createOptions), { pubKeyCredParams: (_a = options.pubKeyCredParams) === null || _a === void 0 ? void 0 : _a.map(function (param) { return ({ type: "public-key", alg: param.alg, }); }), attestation: (((_b = options.attestation) === null || _b === void 0 ? void 0 : _b.toLowerCase()) || "none"), authenticatorSelection: __assign(__assign({}, options.authenticatorSelection), { residentKey: (((_d = (_c = options.authenticatorSelection) === null || _c === void 0 ? void 0 : _c.residentKey) === null || _d === void 0 ? void 0 : _d.toLowerCase()) || "discouraged"), userVerification: (((_f = (_e = options.authenticatorSelection) === null || _e === void 0 ? void 0 : _e.userVerification) === null || _f === void 0 ? void 0 : _f.toLowerCase()) || "discouraged") }) }), })]; case 1: credential = _k.sent(); if (!credential) throw new Error("No credential created."); publicKeyCredential = credential; attestationResponse = publicKeyCredential.response; transports = (_j = (_h = (_g = publicKeyCredential.response).getTransports) === null || _h === void 0 ? void 0 : _h.call(_g)) !== null && _j !== void 0 ? _j : []; return [2 /*return*/, { id: publicKeyCredential.id, rawId: arrayBufferToBase64url(publicKeyCredential.rawId), type: credential.type, response: { clientDataJSON: arrayBufferToBase64url(attestationResponse.clientDataJSON), attestationObject: arrayBufferToBase64url(attestationResponse.attestationObject), transports: transports, }, }]; case 2: err_1 = _k.sent(); console.error("Error during registration:", err_1); if (err_1.name === "NotAllowedError") { throw new Error("Face ID/Touch ID cancelled or failed. Please try again."); } if (err_1.name === "InvalidStateError") { throw new Error("Passkey already registered for this user."); } if (err_1.name === "NotSupportedError") { throw new Error("Passkeys not supported on this device."); } throw err_1; case 3: return [2 /*return*/]; } }); }); } /** * Initiates the WebAuthn authentication process. * It takes server-provided options, converts them for the browser API, * calls navigator.credentials.get(), and then converts the result * back into a JSON-friendly format. * * @param options - The PublicKeyCredentialRequestOptions from the server. * @returns A promise that resolves to a JSON-serializable representation of the PublicKeyCredential. */ function authenticate(options) { return __awaiter$1(this, void 0, void 0, function () { var getOptions, credential, publicKeyCredential, assertionResponse; var _a; return __generator$1(this, function (_b) { switch (_b.label) { case 0: getOptions = __assign(__assign({}, options), { challenge: base64urlToArrayBuffer(options.challenge), allowCredentials: (_a = options.allowCredentials) === null || _a === void 0 ? void 0 : _a.map(function (cred) { return (__assign(__assign({}, cred), { id: base64urlToArrayBuffer(cred.id) })); }) }); return [4 /*yield*/, navigator.credentials.get({ publicKey: getOptions })]; case 1: credential = _b.sent(); if (!credential) { throw new Error("Failed to get credential."); } publicKeyCredential = credential; assertionResponse = publicKeyCredential.response; return [2 /*return*/, { id: publicKeyCredential.id, rawId: arrayBufferToBase64url(publicKeyCredential.rawId), type: credential.type, response: { clientDataJSON: arrayBufferToBase64url(assertionResponse.clientDataJSON), authenticatorData: arrayBufferToBase64url(assertionResponse.authenticatorData), signature: arrayBufferToBase64url(assertionResponse.signature), userHandle: assertionResponse.userHandle ? arrayBufferToBase64url(assertionResponse.userHandle) : null, }, }]; } }); }); } /** * A React hook that provides access to the WebAuthn `register` and `authenticate` functions. * @returns An object containing the `register` and `authenticate` functions. */ var useWebAuthn = function () { return { register: register, authenticate: authenticate, }; }; var CAMSContext$1 = React.createContext(null); function useCAMSContext$1() { var context = React.useContext(CAMSContext$1); if (!context) { throw new Error('useCAMSContext must be used within a CAMSProvider'); } return context; } function ProtectedRoute(_a) { var children = _a.children, fallback = _a.fallback, redirectTo = _a.redirectTo; var _b = useCAMSContext$1(), isAuthenticated = _b.isAuthenticated, isLoading = _b.isLoading; if (isLoading) { return fallback || jsxRuntime.jsx("div", { className: 'h-screen flex items-center justify-center', children: "Loading..." }); } if (!isAuthenticated) { if (redirectTo && typeof window !== 'undefined') { window.location.href = redirectTo; return null; } return fallback || jsxRuntime.jsx("div", { children: "Access denied. Please log in." }); } return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children }); } var CAMSMSALContext = React.createContext(null); var isTokenValid = function (token) { try { var payload = JSON.parse(atob(token.split(".")[1])); return payload.exp * 1000 > Date.now(); } catch (_a) { return false; } }; function CAMSMSALProviderInner(_a) { var _this = this; var children = _a.children, authOptions = __rest(_a, ["children"]); var auth = useCAMSMSALAuth(authOptions); var profileStorageKey = "".concat(auth.storageKey, "-PROFILE"); var getInitialProfile = function () { if (typeof window === "undefined") { return null; } try { var storedProfile = getCookie(profileStorageKey); return storedProfile ? JSON.parse(storedProfile) : null; } catch (_a) { return null; } }; var _b = React.useState(getInitialProfile), userProfile = _b[0], setUserProfile = _b[1]; // Load profile from storage on mount React.useEffect(function () { if (typeof window !== "undefined") { // const storedProfile = localStorage.get Item(profileStorageKey); var storedProfile = getCookie(profileStorageKey); if (storedProfile) { try { setUserProfile(JSON.parse(storedProfile)); } catch (_a) { } } } }, [profileStorageKey]); // Persist tokens and profile React.useEffect(function () { if (auth.accessToken && isTokenValid(auth.accessToken) && typeof window !== "undefined") { localStorage.setItem(auth.storageKey, JSON.stringify({ accessToken: auth.accessToken, idToken: auth.idToken, appCode: auth.appCode, })); } }, [auth.accessToken, auth.idToken, auth.storageKey]); // Persist profile separately React.useEffect(function () { if (typeof window !== "undefined") { if (userProfile) { setCookie(profileStorageKey, JSON.stringify(userProfile), 1); // Store for 1 day } else { deleteCookie(profileStorageKey); } } }, [userProfile, profileStorageKey]); // Enhanced logout that also clears profile var enhancedLogout = function () { return __awaiter$1(_this, void 0, void 0, function () { return __generator$1(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, auth.logout()]; case 1: _a.sent(); setUserProfile(null); if (typeof window !== "undefined") { deleteCookie(profileStorageKey); } return [2 /*return*/]; } }); }); }; var value = React.useMemo(function () { return (__assign(__assign({}, auth), { logout: enhancedLogout, userProfile: userProfile, setUserProfile: setUserProfile })); }, [auth, userProfile]); return (jsxRuntime.jsx(CAMSMSALContext.Provider, { value: value, children: children })); } function CAMSMSALProvider(props) { var _a; var msalConfig = props.msalConfig, msalInstance = props.msalInstance; // Ensure crypto is available before creating MSAL instance if (typeof window !== "undefined" && !((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) { throw new Error("Crypto API not available. Polyfill failed to load."); } var instance = msalInstance || new msalBrowser.PublicClientApplication(msalConfig); return (jsxRuntime.jsx(msalReact.MsalProvider, { instance: instance, children: jsxRuntime.jsx(CAMSMSALProviderInner, __assign({}, props)) })); } function useCAMSMSALContext() { var context = React.useContext(CAMSMSALContext); if (!context) { throw new Error("useCAMSMSALContext must be used within a CAMSMSALProvider"); } return context; } var ClientOnly = function (_a) { var children = _a.children, _b = _a.fallback, fallback = _b === void 0 ? null : _b; var _c = React.useState(false), hasMounted = _c[0], setHasMounted = _c[1]; React.useEffect(function () { setHasMounted(true); }, []); if (!hasMounted) { return jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }); } return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children }); }; var CAMSContext = React.createContext(null); function useCAMSContext() { var context = React.useContext(CAMSContext); if (!context) { throw new Error("useCAMSContext must be used within a UnifiedCAMSProvider"); } return context; } var GuidSchema = z.z.uuid("appCode must be a valid GUID"); function CAMSProviderCore(props) { var _this = this; var children = props.children, mode = props.mode, appCode = props.appCode; // Always call both hooks to satisfy Rules of Hooks var regularAuth = useCAMSAuth(mode === "REGULAR" ? __assign(__assign({}, props), { appCode: appCode }) : { appCode: "" }); var msalAuth = useCAMSMSALAuth(mode === "MSAL" ? __assign(__assign({}, props), { appCode: appCode, ValidateUserEndpoint: props.ValidateUserEndpoint }) : { appCode: "", ValidateUserEndpoint: "" }); var auth = mode === "REGULAR" ? regularAuth : msalAuth; var profileStorageKey = "".concat(auth.storageKey, "-PROFILE"); var getInitialProfile = function () { if (typeof window === "undefined") return null; try { var storedProfile = getCookie(profileStorageKey); return storedProfile ? JSON.parse(storedProfile) : null; } catch (_a) { return null; } }; var _a = React.useState(getInitialProfile), userProfile = _a[0], setUserProfile = _a[1]; React.useEffect(function () { if (typeof window !== "undefined") { var storedProfile = getCookie(profileStorageKey); if (storedProfile) { try { setUserProfile(JSON.parse(storedProfile)); } catch (_a) { } } } }, [profileStorageKey]); React.useEffect(function () { if (typeof window !== "undefined") { if (userProfile) { setCookie(profileStorageKey, JSON.stringify(userProfile), 1); } else { deleteCookie(profileStorageKey); } } }, [userProfile, profileStorageKey]); var enhancedLogout = function () { return __awaiter$1(_this, void 0, void 0, function () { return __generator$1(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, auth.logout()]; case 1: _a.sent(); setUserProfile(null); if (typeof window !== "undefined") { deleteCookie(profileStorageKey); } return [2 /*return*/]; } }); }); }; React.useEffect(function () { if (mode === "MSAL" && "requiresMFA" in auth && !auth.requiresMFA && auth.isAuthenticated) { var storedData = localStorage.getItem(auth.storageKey); if (storedData) { try { var parsed = JSON.parse(storedData); if (parsed.userProfile) { setUserProfile(parsed.userProfile); } } catch (_a) { } } } }, [ mode, "requiresMFA" in auth ? auth.requiresMFA : false, auth.isAuthenticated, auth.storageKey, userProfile, ]); var value = React.useMemo(function () { auth.logout; var authRest = __rest(auth, ["logout"]); return __assign(__assign({}, authRest), { logout: enhancedLogout, user: userProfile, setUserProfile: setUserProfile, authMode: mode, onAuthSuccess: mode === "MSAL" ? props.onAuthSuccess : undefined, onAuthError: mode === "MSAL" ? props.onAuthError : undefined }); }, [auth, userProfile, mode, props]); return jsxRuntime.jsx(CAMSContext.Provider, { value: value, children: children }); } function UnifiedCAMSProvider(props) { var _a; // Validate appCode is a valid GUID var appCodeValidation = GuidSchema.safeParse(props.appCode); if (!appCodeValidation.success) { throw new Error("Invalid CAS APP CODE: ".concat(appCodeValidation.error.issues[0].message)); } if (props.mode === "MSAL") { var msalConfig = props.msalConfig, msalInstance = props.msalInstance; if (typeof window !== "undefined" && !((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) { throw new Error("Crypto API not available. Polyfill failed to load."); } var instance = msalInstance || new msalBrowser.PublicClientApplication(msalConfig); return (jsxRuntime.jsx(msalReact.MsalProvider, { instance: instance, children: jsxRuntime.jsx(CAMSProviderCore, __assign({}, props)) })); } return (jsxRuntime.jsx(ClientOnly, { fallback: jsxRuntime.jsx("div", { className: "h-screen flex items-center justify-center", children: "Loading..." }), children: jsxRuntime.jsx(CAMSProviderCore, __assign({}, props)) })); } // Backward compatibility exports var CAMSProvider = function (props) { return (jsxRuntime.jsx(UnifiedCAMSProvider, __assign({}, props, { mode: "REGULAR" }))); }; /** A special constant with type `never` */ function $constructor(name, initializer, params) { function init(inst, def) { var _a; Object.defineProperty(inst, "_zod", { value: inst._zod ?? {}, enumerable: false, }); (_a = inst._zod).traits ?? (_a.traits = new Set()); inst._zod.traits.add(name); initializer(inst, def); // support prototype modific