UNPKG

@criipto/verify-react

Version:

Verify SDK for React Single Page Applications

1,388 lines 110 kB
import React, { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import CriiptoAuth, { OAuth2Error, OAuth2Error as OAuth2Error$1, UserCancelledError, clearPKCEState, generatePKCE, parseAuthorizeResponseFromLocation, savePKCEState } from "@criipto/auth-js"; import jwtDecode from "jwt-decode"; import { jsx, jsxs } from "react/jsx-runtime"; import { UAParser } from "@ua-parser-js/pro-enterprise"; import QRCode$1 from "qrcode"; import classNames from "classnames"; //#region src/context.ts const actions = [ "confirm", "accept", "approve", "sign", "login" ]; /** * @ignore */ const stub = () => { throw new Error("You forgot to wrap your component in <CriiptoVerifyProvider>."); }; const CriiptoVerifyContext = createContext({ loginWithRedirect: stub, loginWithPopup: stub, checkSession: stub, logout: stub, fetchOpenIDConfiguration: stub, buildAuthorizeUrl: stub, initializePAR: stub, generatePKCE: stub, buildOptions: stub, handleResponse: stub, responseType: "token", completionStrategy: "client", result: null, claims: null, domain: "", action: "login", pkce: void 0, store: null, isLoading: false, isInitializing: true, client: null, loginHint: void 0 }); //#endregion //#region package.json var version = "5.0.2"; //#endregion //#region src/i18n/en.ts const en = { loginWith: "Login with", approveWith: "Approve with", signWith: "Sign with", confirmWith: "Confirm with", acceptWith: "Accept with", sameDevice: "On this device", anotherDevice: "With your SSN", anotherDeviceQR: "On another device", mitidBusiness: "MitID Business", finnishMobileID: "Finnish Mobile Certificate", finnishBankID: "Finnish Bank ID", swedishBankID: "Swedish BankID", norwegianBankID: "Norwegian BankID", biometrics: "Biometrics", openAndScan: "Open the BankID app on your mobile device and scan the QR code.", thisIsTheQR: "This is a BankID QR Code. You can click it to view it in full screen.", completingLogin: "Completing your login." }; //#endregion //#region src/i18n/da.ts const da = { loginWith: "Login med", approveWith: "Godkend med", signWith: "Underskriv med", confirmWith: "Bekræft med", acceptWith: "Accepter med", sameDevice: "På denne enhed", anotherDevice: "Med dit personnummer", anotherDeviceQR: "På anden enhed", mitidBusiness: "MitID Erhverv", finnishMobileID: "Finsk Mobilcertifikat", finnishBankID: "Finsk Bank ID", swedishBankID: "Svensk BankID", norwegianBankID: "Norsk BankID", biometrics: "Biometri", openAndScan: "Åbn BankID appen på din telefon og scan QR koden.", thisIsTheQR: "Dette er en BankID QR-kode. Du kan klikke på den for at se den i fuld skærm.", completingLogin: "Fuldfører dit login." }; //#endregion //#region src/i18n/sv.ts const sv = { loginWith: "Logga in med", approveWith: "Godkänn med", signWith: "Signera med", confirmWith: "Bekräfta med", acceptWith: "Acceptera med", sameDevice: "På denna enhet", anotherDevice: "Med ditt personnummer", anotherDeviceQR: "På annan enhet", mitidBusiness: "MitID Erhverv", finnishMobileID: "Finskt Mobilcertifikat", finnishBankID: "Finskt Bank ID", swedishBankID: "Svenskt BankID", norwegianBankID: "Norska BankID", biometrics: "Biometri", openAndScan: "Öppna BankID-appen på din mobila enhet och skanna QR-koden.", thisIsTheQR: "Detta är en QR-kod från BankID. Du kan klicka på den för att visa den i fullskärm.", completingLogin: "Slutför din inloggning." }; //#endregion //#region src/i18n/nb.ts const nb = { loginWith: "Logg inn med", approveWith: "Godkjenne med", signWith: "Signer med", confirmWith: "Bekreft med", acceptWith: "Aksepterer med", sameDevice: "På denne enhet", anotherDevice: "Med personnummeret ditt", anotherDeviceQR: "På annan enhet", mitidBusiness: "MitID Erhverv", finnishMobileID: "Finsk Mobilsertifikat", finnishBankID: "Finsk Bank ID", swedishBankID: "Svensk BankID", norwegianBankID: "Norsk BankID", biometrics: "Biometri", openAndScan: "Åpne BankID-appen på mobilenheten din og skann QR-koden.", thisIsTheQR: "Dette er en BankID QR-kode. Du kan klikke på den for å se den i fullskjerm.", completingLogin: "Fullfører påloggingen." }; //#endregion //#region src/i18n/fi.ts const fi = { loginWith: "Kirjaudu sisään", approveWith: "Hyväksy", signWith: "Allekirjoita", confirmWith: "Vahvista", acceptWith: "Hyväksy", sameDevice: "Tällä laitteella", anotherDevice: "Henkilötunnuksellasi", anotherDeviceQR: "Toisella laitteella", mitidBusiness: "MitID Businessilla", finnishMobileID: "Suomalaisella Mobiilivarmenteella", finnishBankID: "Suomalaisella BankID:llä", swedishBankID: "Ruotsalaisella BankID:llä", norwegianBankID: "Norjalaisella BankID:llä", biometrics: "Biometrisella tunnistautumisella", openAndScan: "Avaa BankID-sovellus mobiililaitteellasi ja skannaa QR-koodi.", thisIsTheQR: "Tämä on BankID-QR-koodi. Voit napsauttaa sitä nähdäksesi sen koko näytöllä", completingLogin: "Sisäänkirjautumista viimeistellään." }; //#endregion //#region src/i18n/de.ts const de = { loginWith: "Anmelden mit", approveWith: "Genehmigen mit", signWith: "Unterschreiben mit", confirmWith: "Bestätigen mit", acceptWith: "Akzeptieren mit", sameDevice: "Auf diesem Gerät", anotherDevice: "Mit Ihrer Sozialversicherungsnummer", anotherDeviceQR: "Auf einem anderen Gerät", mitidBusiness: "MitID Business", finnishMobileID: "Finnischem Mobilfunkzertifikat", finnishBankID: "Finnische BankID", swedishBankID: "Schwedische BankID", norwegianBankID: "Norwegische BankID", biometrics: "Biometrie", openAndScan: "Öffnen Sie die BankID-App auf Ihrem mobilen Endgerät und scannen Sie den QR-Code.", thisIsTheQR: "Dies ist ein BankID QR-Code. Sie können darauf klicken, um ihn im Vollbildmodus anzuzeigen", completingLogin: "Ihre Anmeldung wird abgeschlossen." }; //#endregion //#region src/i18n/nl.ts const nl = { loginWith: "Inloggen met", approveWith: "Goedkeuren met", signWith: "Ondertekenen met", confirmWith: "Bevestigen met", acceptWith: "Accepteren met", sameDevice: "Op dit apparaat", anotherDevice: "Met uw SSN", anotherDeviceQR: "Op een ander apparaat", mitidBusiness: "MitID Business", finnishMobileID: "Finse mobiel certificaat", finnishBankID: "Finse BankID", swedishBankID: "Zweedse BankID", norwegianBankID: "Noorse BankID", biometrics: "Biometrie", openAndScan: "Open de BankID-app op uw mobiele apparaat en scan de QR-code.", thisIsTheQR: "Dit is een BankID QR-code. U kunt erop klikken om het in een volledig scherm te bekijken", completingLogin: "Uw login voltooien." }; //#endregion //#region src/i18n/index.ts function getI18n(language) { switch (language) { case "en": return en; case "da": return da; case "sv": return sv; case "nb": return nb; case "fi": return fi; case "de": return de; case "nl": return nl; default: assertUnreachable(language); } } //#endregion //#region src/utils.ts const DKMITID_PREFIX = "urn:grn:authn:dk:mitid"; const FTN_PREFIX = "urn:grn:authn:fi"; function lowestMitIDValue(input) { let dkmitidMethod = null; input.forEach((s) => { if (s.startsWith("urn:grn:authn:dk:mitid") && s !== "urn:grn:authn:dk:mitid:business") if (!dkmitidMethod) dkmitidMethod = s; else { let newLevel = s.replace(DKMITID_PREFIX, ""); let oldLevel = dkmitidMethod.replace(DKMITID_PREFIX, ""); if (newLevel === ":low" && [":substantial", ":high"].includes(oldLevel)) dkmitidMethod = s; else if (newLevel === ":substantial" && oldLevel === ":high") dkmitidMethod = s; } }); return dkmitidMethod; } function autoTitleCase(input) { return input.split(/:|-/).map((segment) => { segment = segment.replace(/id(\s|$)/, "ID"); if (segment.length === 2) return segment.toUpperCase(); return segment.substr(0, 1).toUpperCase() + segment.substr(1); }).join(" "); } function assertUnreachable(x) { throw new Error("Didn't expect to get here"); } function stringifyAction(language, action) { const i18n = getI18n(language); switch (action) { case "login": return i18n.loginWith; case "approve": return i18n.approveWith; case "sign": return i18n.signWith; case "confirm": return i18n.confirmWith; case "accept": return i18n.acceptWith; default: assertUnreachable(action); } } function acrValueToProviderPrefix(value) { value = value.replace("urn:grn:authn:", ""); if (value.startsWith("fi")) return "fi"; else if (value.startsWith("itsme")) return "itsme"; else return value.split(":").slice(0, 2).join(":"); } function acrValueToTitle(language, value, { disambiguate }) { const i18n = getI18n(language); value = value.replace("urn:grn:authn:", ""); const provider = acrValueToProviderPrefix(value); if (provider === "dk:mitid") { if (value.replace("dk:mitid:", "") === "business") return { title: i18n.mitidBusiness }; return { title: "MitID" }; } if (value === "fi:mobile-id") return { title: i18n.finnishMobileID }; if (value === "fi:bank-id") return { title: i18n.finnishBankID }; if (provider === "fi") return { title: autoTitleCase(value).replace("FI", "FTN") }; if (provider === "itsme") return { title: autoTitleCase(value).replace("me", "ME") }; if (provider === "se:bankid") { let subtitle = void 0; let suffix = value.replace("se:bankid:", ""); let title = "BankID"; if (suffix === "same-device") subtitle = i18n.sameDevice; if (suffix === "another-device") subtitle = i18n.anotherDevice; if (suffix === "another-device:qr") subtitle = i18n.anotherDeviceQR; if (disambiguate) title = i18n.swedishBankID; return { title, subtitle }; } if (provider === "no:bankid") { let subtitle = void 0; let title = "BankID"; if (value.endsWith(":substantial")) subtitle = i18n.biometrics; if (disambiguate) title = i18n.norwegianBankID; return { title, subtitle }; } if (provider === "no:vipps") return { title: autoTitleCase(value).replace("NO ", "") }; if (provider === "se:frejaid") return { title: "FrejaID" }; if (provider === "uk:oneid") return { title: "OneID" }; if (provider === "de:personalausweis") return { title: "Personalausweis" }; return { title: autoTitleCase(value) }; } function filterAcrValues(input) { let original = input.slice(); let dkmitidMethod = lowestMitIDValue(input); let reduced = input.slice(); if (dkmitidMethod) reduced = input.filter((s) => !s.startsWith("urn:grn:authn:dk:mitid") || s === "urn:grn:authn:dk:mitid:business").concat([dkmitidMethod]).sort((a, b) => original.indexOf(a) - original.indexOf(b)); if (input.includes(`urn:grn:authn:fi:all`)) { if (!reduced.includes(`urn:grn:authn:fi:bank-id`)) reduced.push(`${FTN_PREFIX}:bank-id`); if (!reduced.includes(`urn:grn:authn:fi:mobile-id`)) reduced.push(`${FTN_PREFIX}:mobile-id`); reduced = reduced.filter((s) => s !== `${FTN_PREFIX}:all`); } return reduced; } function isSingle(acrValue, acrValues) { const provider = acrValueToProviderPrefix(acrValue); return acrValues.reduce((memo, acrValue) => memo + (acrValueToProviderPrefix(acrValue) === provider ? 1 : 0), 0) === 1; } const ambigousProviders = ["se:bankid", "no:bankid"]; /** * A provider is considered ambiguous iff: * 1. It is in the list of ambiguous providers * 2. There is another ambiguous provider in the arc values list */ function isAmbiguous(acrValue, acrValues) { const provider = acrValueToProviderPrefix(acrValue); const isProviderAmbiguous = ambigousProviders.includes(provider); const otherAmbiguousProvider = acrValues.find((otherAcrValue) => { const otherProvider = acrValueToProviderPrefix(otherAcrValue); return provider != otherProvider && ambigousProviders.includes(otherProvider); }) != null; return isProviderAmbiguous && otherAmbiguousProvider; } function trySessionStorage() { try { if (typeof sessionStorage === "undefined") return null; return sessionStorage; } catch (err) { /** * Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document. * (if storage is disabled in the browser) */ return null; } } //#endregion //#region src/memory-storage.ts function createMemoryStorage() { let store = {}; return { getItem(key) { return store[key] || null; }, setItem(key, value) { store[key] = value.toString(); }, removeItem(key) { delete store[key]; }, clear() { store = {}; }, key(index) { return Object.keys(store)[index]; }, get length() { return Object.keys(store).length; } }; } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { e(n); return; } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function() { var t = this, e = arguments; return new Promise(function(r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/typeof.js function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) { return typeof o; } : function(o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/toPrimitive.js function toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/toPropertyKey.js function toPropertyKey(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/defineProperty.js function _defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/objectSpread2.js function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function(r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function(r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } //#endregion //#region src/provider.tsx const SESSION_KEY = `@criipto-verify-react/session`; const ACTION_SUPPORTING_ACR_VALUES = [ "urn:grn:authn:dk:mitid:low", "urn:grn:authn:dk:mitid:substantial", "urn:grn:authn:dk:mitid:high", "urn:grn:authn:se:bankid:same-device", "urn:grn:authn:se:bankid:another-device:qr" ]; const MESSAGE_SUPPORTING_ACR_VALUES = [ "urn:grn:authn:dk:mitid:low", "urn:grn:authn:dk:mitid:substantial", "urn:grn:authn:dk:mitid:high", "urn:grn:authn:se:bankid:same-device", "urn:grn:authn:se:bankid:another-device:qr" ]; function buildLoginHint(loginHint, params) { const { options, action, message } = params; const acrValues = (options === null || options === void 0 ? void 0 : options.acrValues) ? Array.isArray(options === null || options === void 0 ? void 0 : options.acrValues) ? options === null || options === void 0 ? void 0 : options.acrValues : [options === null || options === void 0 ? void 0 : options.acrValues] : []; let hints = (loginHint ? loginHint.split(" ") : []).concat((options === null || options === void 0 ? void 0 : options.loginHint) ? options === null || options === void 0 ? void 0 : options.loginHint.split(" ") : []).filter((hint) => !hint.startsWith("message:") && !hint.startsWith("action:")); if (action) { hints = hints.filter((h) => !h.startsWith("action:")); if (acrValues.length === 1) { if (ACTION_SUPPORTING_ACR_VALUES.includes(acrValues[0])) hints.push(`action:${action}`); } else if (acrValues.length >= 2) { if (acrValues.some((v) => ACTION_SUPPORTING_ACR_VALUES.includes(v))) hints.push(`action:${action}`); } else hints.push(`action:${action}`); } if (message) if (acrValues.length === 1) { if (MESSAGE_SUPPORTING_ACR_VALUES.includes(acrValues[0])) hints.push(`message:${btoa(message)}`); } else if (acrValues.length >= 2) { if (acrValues.some((v) => MESSAGE_SUPPORTING_ACR_VALUES.includes(v))) hints.push(`message:${btoa(message)}`); } else hints.push(`message:${btoa(message)}`); return hints.length ? hints.join(" ") : void 0; } function parseAction(input) { if (!input) return; const actionCandidate = input.split(" ").find((s) => s.startsWith("action:")); if (!actionCandidate) return; const action = actionCandidate.replace("action:", ""); if (actions.includes(action)) return action; } function tryBase64Decode(input) { try { return atob(input); } catch (_unused) { return null; } } function parseMessage(input) { var _tryBase64Decode; if (!input) return; const messageCandidate = input.split(" ").find((s) => s.startsWith("message:")); if (!messageCandidate) return; const message = messageCandidate.replace("message:", ""); return (_tryBase64Decode = tryBase64Decode(message)) !== null && _tryBase64Decode !== void 0 ? _tryBase64Decode : message; } function defaultRedirectUri(input) { if (input) return input; if (typeof window !== "undefined" && typeof window.location !== "undefined") return window.location.origin + window.location.pathname; throw new Error("Unable to determine default redirect uri, please provide a redirectUri"); } const pkceStore = (() => { const sessionStorage = trySessionStorage(); if (sessionStorage === null) { console.warn("Creating memory store for PKCE values as no sessionStorage is available. Authentication may be broken."); return createMemoryStorage(); } return sessionStorage; })(); const CriiptoVerifyProvider = (props) => { var _ref, _props$action, _props$message; const client = useMemo(() => new CriiptoAuth({ domain: props.domain, clientID: props.clientID, store: pkceStore, redirectUri: defaultRedirectUri(props.redirectUri), protocol: props.protocol }), [ props.domain, props.clientID, props.redirectUri, props.protocol ]); const [configuration, setConfiguration] = useState(null); useEffect(() => { let isSubscribed = true; _asyncToGenerator(function* () { const configuration = yield client.fetchOpenIDConfiguration(); if (isSubscribed) setConfiguration(configuration); })(); return () => { isSubscribed = false; }; }, [client]); const [result, setResult] = useState(null); useEffect(() => { let isSubscribed = true; _asyncToGenerator(function* () { try { yield client.fetchCriiptoConfiguration(); } catch (error) { if (!isSubscribed) return; const err = error instanceof Error ? error : new Error(String(error)); setResult((prev) => prev !== null && prev !== void 0 ? prev : err); } })(); return () => { isSubscribed = false; }; }, [client]); const claims = useMemo(() => { if (!result) return null; if (!("id_token" in result)) return null; return jwtDecode(result.id_token); }, [result]); const [isLoading, setIsLoading] = useState(false); const [isInitializing, setIsInitializing] = useState(true); const [pkce, setPKCE] = useState(props.pkce); const responseType = props.response || "token"; const completionStrategy = props.completionStrategy || "client"; const uiLocales = props.uiLocales; const loginHint = props.loginHint; const action = (_ref = (_props$action = props.action) !== null && _props$action !== void 0 ? _props$action : parseAction(loginHint)) !== null && _ref !== void 0 ? _ref : "login"; const message = (_props$message = props.message) !== null && _props$message !== void 0 ? _props$message : parseMessage(loginHint); const sessionStore = props.sessionStore; const refreshPKCE = function() { var _ref2 = _asyncToGenerator(function* () { if (props.pkce) return props.pkce; if (responseType !== "token" || completionStrategy !== "client") { clearPKCEState(pkceStore); setPKCE(void 0); return; } clearPKCEState(pkceStore); const pkce = yield generatePKCE(); setPKCE(pkce); return pkce; }); return function refreshPKCE() { return _ref2.apply(this, arguments); }; }(); const buildOptions = useCallback((options) => { var _props$responseType, _props$state, _props$nonce, _props$prompt, _props$scope, _options$pkce; return _objectSpread2(_objectSpread2({ redirectUri: defaultRedirectUri(props.redirectUri), responseType: (_props$responseType = props.responseType) !== null && _props$responseType !== void 0 ? _props$responseType : "code", responseMode: props.responseMode }, options || {}), {}, { state: (_props$state = props.state) !== null && _props$state !== void 0 ? _props$state : options === null || options === void 0 ? void 0 : options.state, nonce: (_props$nonce = props.nonce) !== null && _props$nonce !== void 0 ? _props$nonce : options === null || options === void 0 ? void 0 : options.nonce, prompt: (_props$prompt = props.prompt) !== null && _props$prompt !== void 0 ? _props$prompt : options === null || options === void 0 ? void 0 : options.prompt, scope: (_props$scope = props.scope) !== null && _props$scope !== void 0 ? _props$scope : options === null || options === void 0 ? void 0 : options.scope, uiLocales: uiLocales !== null && uiLocales !== void 0 ? uiLocales : options === null || options === void 0 ? void 0 : options.uiLocales, pkce: (_options$pkce = options === null || options === void 0 ? void 0 : options.pkce) !== null && _options$pkce !== void 0 ? _options$pkce : pkce, loginHint: buildLoginHint(props.loginHint, { options, action, message }), extraUrlParams: props.criiptoSdk !== void 0 ? { criipto_sdk: props.criiptoSdk } : { criipto_sdk: `@criipto/verify-react@${version}` } }); }, [ pkce, props.state, props.nonce, props.prompt, props.scope, uiLocales, action, message, props.redirectUri, props.criiptoSdk, props.loginHint, props.responseMode, props.responseType ]); const buildAuthorizeUrl = useCallback(function() { var _ref3 = _asyncToGenerator(function* (options) { return yield client.buildAuthorizeUrl(client.buildAuthorizeParams(buildOptions(options))); }); return function(_x) { return _ref3.apply(this, arguments); }; }(), [client, buildOptions]); const initializePAR = useCallback(function() { var _ref4 = _asyncToGenerator(function* (options) { return (yield client.pushAuthorizationRequest(client.buildAuthorizeParams(buildOptions(options)))).authorizeUrl; }); return function(_x2) { return _ref4.apply(this, arguments); }; }(), [client, buildOptions]); const handleResponse = useCallback(function() { var _ref5 = _asyncToGenerator(function* (response, params = {}) { if (response instanceof OAuth2Error$1) setResult(response); else if (response instanceof Error) setResult(response); else if (params.pkce && responseType === "token") { let _redirectUri = params.redirectUri || defaultRedirectUri(props.redirectUri); if (!_redirectUri) throw new Error("redirectUri must be configured globally or per authentication component"); yield client.processResponse(response, { code_verifier: params.pkce.code_verifier, redirect_uri: _redirectUri }).then((response) => { if (response === null || response === void 0 ? void 0 : response.code) setResult({ code: response.code, state: response.state, source: params.source }); else if (response === null || response === void 0 ? void 0 : response.id_token) { setResult({ id_token: response.id_token, state: response.state, source: params.source }); sessionStore === null || sessionStore === void 0 || sessionStore.setItem(SESSION_KEY, response.id_token); } else setResult(null); }).catch((err) => { setResult(err); }); } else if (response === null || response === void 0 ? void 0 : response.code) setResult({ code: response.code, state: response.state, source: params.source }); else if (response === null || response === void 0 ? void 0 : response.id_token) { setResult({ id_token: response.id_token, state: response.state, source: params.source }); sessionStore === null || sessionStore === void 0 || sessionStore.setItem(SESSION_KEY, response.id_token); } else if (response === null || response === void 0 ? void 0 : response.error) setResult(new OAuth2Error$1(response.error, response.error_description, response.state)); else setResult(null); refreshPKCE(); }); return function(_x3) { return _ref5.apply(this, arguments); }; }(), [ refreshPKCE, responseType, setResult, client, sessionStore, props.redirectUri ]); const checkSession = useCallback(_asyncToGenerator(function* () { return client.checkSession({ redirectUri: defaultRedirectUri(props.redirectUri) }).then((response) => { if (response === null || response === void 0 ? void 0 : response.code) setResult({ code: response.code }); else if (response === null || response === void 0 ? void 0 : response.id_token) { setResult({ id_token: response.id_token }); sessionStore === null || sessionStore === void 0 || sessionStore.setItem(SESSION_KEY, response.id_token); } else setResult(null); }); }), [ sessionStore, client, props.redirectUri ]); const logout = useCallback(function() { var _ref6 = _asyncToGenerator(function* (params) { var _params$redirectUri; sessionStore === null || sessionStore === void 0 || sessionStore.removeItem(SESSION_KEY); yield client.logout({ redirectUri: (_params$redirectUri = params === null || params === void 0 ? void 0 : params.redirectUri) !== null && _params$redirectUri !== void 0 ? _params$redirectUri : defaultRedirectUri(props.redirectUri), state: params === null || params === void 0 ? void 0 : params.state }); }); return function(_x4) { return _ref6.apply(this, arguments); }; }(), [ sessionStore, client, props.redirectUri ]); const context = useMemo(() => { return { loginWithRedirect: function() { var _ref7 = _asyncToGenerator(function* (params) { const pkce = yield refreshPKCE(); yield client.redirect.authorize(buildOptions(_objectSpread2(_objectSpread2({}, params), {}, { pkce }))); }); return function loginWithRedirect(_x5) { return _ref7.apply(this, arguments); }; }(), loginWithPopup: function() { var _ref8 = _asyncToGenerator(function* (params) { return client.popup.authorize(buildOptions(params)).then((response) => { if (response === null || response === void 0 ? void 0 : response.code) setResult({ code: response.code, state: response.state, source: "popup" }); else if (response === null || response === void 0 ? void 0 : response.id_token) setResult({ id_token: response.id_token, state: response.state, source: "popup" }); else setResult(null); }).catch((err) => { setResult(err); }); }); return function loginWithPopup(_x6) { return _ref8.apply(this, arguments); }; }(), checkSession, logout, fetchOpenIDConfiguration: () => client.fetchOpenIDConfiguration(), buildAuthorizeUrl, initializePAR, generatePKCE: function() { var _ref9 = _asyncToGenerator(function* () { if (responseType !== "token" || completionStrategy !== "client") return; return yield generatePKCE(); }); return function generatePKCE() { return _ref9.apply(this, arguments); }; }(), buildOptions, handleResponse, responseType, completionStrategy, result, claims, domain: client.domain, redirectUri: defaultRedirectUri(props.redirectUri), action, message, pkce, store: pkceStore, isLoading, isInitializing, acrValues: configuration ? filterAcrValues(configuration.acr_values_supported) : void 0, client, uiLocales, loginHint }; }, [ client, props.redirectUri, responseType, completionStrategy, result, claims, action, message, pkce, props.state, props.prompt, isLoading, isInitializing, handleResponse, logout, configuration, uiLocales, checkSession, loginHint ]); useEffect(() => { setIsInitializing(false); if (!client.redirect.hasMatch()) { refreshPKCE(); return; } let isSubscribed = true; setIsLoading(true); const params = parseAuthorizeResponseFromLocation(window.location); if (params.code && responseType === "code") { setIsLoading(false); setResult({ code: params.code, source: "redirect", state: params.state }); refreshPKCE(); return; } _asyncToGenerator(function* () { yield Promise.resolve(); if (!isSubscribed) return; try { const response = yield client.redirect.match(); if (!isSubscribed) return; if (response === null || response === void 0 ? void 0 : response.code) setResult({ code: response.code, source: "redirect", state: response.state }); else if (response === null || response === void 0 ? void 0 : response.id_token) { setResult({ id_token: response.id_token, source: "redirect", state: response.state }); sessionStore === null || sessionStore === void 0 || sessionStore.setItem(SESSION_KEY, response.id_token); resetRedirectState(window); } else setResult(null); } catch (err) { if (!isSubscribed) return; if (err instanceof OAuth2Error$1) setResult(err); else if (err instanceof Error) setResult(err); else { var _err$toString; setResult(new Error((_err$toString = err === null || err === void 0 ? void 0 : err.toString()) !== null && _err$toString !== void 0 ? _err$toString : "Unknown error ocurred")); } } finally { if (!isSubscribed) return; setIsLoading(false); refreshPKCE(); } })(); return () => { isSubscribed = false; }; }, [props.pkce, responseType]); useEffect(() => { if (!sessionStore || claims) return; if (!claims) { const token = sessionStore.getItem(SESSION_KEY); if (!token) return; setResult({ id_token: token }); return; } }, [sessionStore, claims]); useEffect(() => { if (!claims) return; if (!sessionStore) return; const interval = setInterval(() => { const now = Date.now(); if (claims.exp < now / 1e3) { setResult(null); sessionStore === null || sessionStore === void 0 || sessionStore.removeItem(SESSION_KEY); return; } }, 5e3); return () => clearInterval(interval); }, [ sessionStore, claims, result ]); /** * Fresh page load SSO check */ useEffect(() => { setIsInitializing(false); if (!sessionStore) return; if (client.redirect.hasMatch()) return; if (sessionStore.getItem(SESSION_KEY)) return; let isSubscribed = true; setIsLoading(true); checkSession().catch((err) => { console.error("session silent check error", err); }).finally(() => { if (isSubscribed) setIsLoading(false); }); return () => { isSubscribed = false; }; }, [sessionStore, client]); return /* @__PURE__ */ jsx(CriiptoVerifyContext.Provider, { value: context, children: props.children }); }; function resetRedirectState(window) { const url = new URL(window.location.href); url.searchParams.delete("code"); url.searchParams.delete("state"); window.history.replaceState({}, window.document.title, url.pathname + url.search); } //#endregion //#region src/device.ts function getUserAgent(userAgent) { if (!userAgent) return null; return UAParser(userAgent); } //#endregion //#region src/components/SEBankIDSameDeviceButton/usePoll.ts function usePoll$1({ shouldPoll, pollUrl, onComplete, onError }) { useEffect(() => { if (!shouldPoll) return; let isSubscribed = true; const poll = function() { var _ref = _asyncToGenerator(function* () { if (!isSubscribed) return; const response = yield fetch(pollUrl); if (response.status === 202) { setTimeout(poll, 1e3); return; } else if (response.status >= 400) { if (!isSubscribed) return; onError(yield response.text()); return; } else { if (!isSubscribed) return; const { targetUrl } = yield response.json(); onComplete(targetUrl); return; } }); return function poll() { return _ref.apply(this, arguments); }; }(); setTimeout(poll, 1e3); return () => { isSubscribed = false; }; }, [shouldPoll, pollUrl]); } //#endregion //#region src/components/SEBankIDSameDeviceButton/Poll.tsx function SEBankIDSameDevicePoll(props) { const { links, onError, onComplete, onInitiate } = props; const [initiated, setInitiated] = useState(false); const { domain } = useContext(CriiptoVerifyContext); usePoll$1(_objectSpread2(_objectSpread2({}, props), {}, { shouldPoll: initiated, pollUrl: links.pollUrl })); const handleInitiate = () => { onInitiate(); setInitiated(true); }; return React.cloneElement(props.children, _objectSpread2(_objectSpread2({}, props.children.props), {}, { onClick: handleInitiate })); } //#endregion //#region src/hooks/usePageVisibility.ts function usePageVisibility(onVisible, deps = []) { const visibilityState = useRef(document.visibilityState); const [state, setState] = useState(visibilityState.current); useEffect(() => { document.addEventListener("visibilitychange", _handleVisibilityChange); return () => { document.removeEventListener("visibilitychange", _handleVisibilityChange); }; }, deps); const _handleVisibilityChange = () => { const nextState = document.visibilityState; if (visibilityState.current === "hidden" && nextState === "visible") { if (onVisible) onVisible(); } visibilityState.current = nextState; setState(visibilityState.current); }; return state; } //#endregion //#region src/components/SEBankIDSameDeviceButton/Foreground.tsx function SEBankIDSameDeviceForeground(props) { const { links, onInitiate, onLog } = props; const [initiated, setInitiated] = useState(false); const [result, setResult] = useState(null); const onComplete = useCallback((completeUrl) => setResult({ result: completeUrl }), []); const onError = useCallback((error) => setResult({ error }), []); const visibility = usePageVisibility(() => { onLog("ForegroundStrategy", "onForeground", initiated.toString()); }, [initiated]); useEffect(() => { if (result && visibility === "visible") { if ("result" in result) props.onComplete(result.result); else if ("error" in result) props.onError(result.error); setResult(null); } }, [result, visibility]); usePoll$1({ shouldPoll: initiated, pollUrl: links.pollUrl, onComplete, onError }); const handleInitiate = () => { onLog("ForegroundStrategy", "handleInitiate"); onInitiate(); setInitiated(true); }; return React.cloneElement(props.children, _objectSpread2(_objectSpread2({}, props.children.props), {}, { onClick: handleInitiate })); } //#endregion //#region src/components/SEBankIDSameDeviceButton/shared.ts const STATE_KEY = "@criipto/verify-react:sebankid:state"; const stateStore = (() => { const sessionStorage = trySessionStorage(); if (sessionStorage === null) { console.warn("Creating memory store for PKCE values as no sessionStorage is available. Authentication may be broken."); return createMemoryStorage(); } return sessionStorage; })(); function hydrateState() { const rawState = stateStore.getItem(STATE_KEY); if (rawState) return JSON.parse(rawState); } const autoHydratedState = hydrateState(); function clearState() { stateStore.removeItem(STATE_KEY); } function saveState(input) { stateStore.setItem(STATE_KEY, JSON.stringify(input)); } //#endregion //#region src/components/SEBankIDSameDeviceButton/Reload.tsx function SEBankIDSameDeviceReload(props) { const { links, onError, onComplete, onInitiate, onLog, pkce, redirectUri } = props; const handleInitiate = () => { onLog("ReloadStrategy", "handleInitiate"); saveState({ links, redirectUri, pkce }); onInitiate(); }; useEffect(() => { const hydratedState = hydrateState(); if (!hydratedState) return; clearState(); onComplete(hydratedState.links.completeUrl); }, []); return React.cloneElement(props.children, _objectSpread2(_objectSpread2({}, props.children.props), {}, { onClick: handleInitiate })); } //#endregion //#region src/components/SEBankIDQRCode/logo@2x.png var logo_2x_default = "data:image/png;base64,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"; //#endregion //#region src/components/SEBankIDQRCode.tsx function searchParamsToPOJO$1(input) { return Array.from(input.keys()).reduce((memo, key) => { memo[key] = input.get(key); return memo; }, {}); } function SEBankIDQrCode(props) { var _ref, _props$language, _window$innerHeight, _wrapperRef$current, _wrapperRef$current2, _props$qrMargin; const wrapperRef = useRef(null); const { buildAuthorizeUrl, completionStrategy, handleResponse, generatePKCE, redirectUri: defaultRedirectURi, uiLocales } = useContext(CriiptoVerifyContext); const language = (_ref = (_props$language = props.language) !== null && _props$language !== void 0 ? _props$language : uiLocales) !== null && _ref !== void 0 ? _ref : "en"; const redirectUri = props.redirectUri || defaultRedirectURi; const [qrCode, setQrCode] = useState(null); const [pollUrl, setPollUrl] = useState(null); const [error, setError] = useState(null); const [pkce, setPKCE] = useState(void 0); const [isCompleting, setCompleting] = useState(false); const [fullscreen, setFullscreen] = useState(false); const refresh = useCallback(_asyncToGenerator(function* () { setError(null); setQrCode(null); setPollUrl(null); setPKCE(void 0); const pkce = yield generatePKCE(); buildAuthorizeUrl({ acrValues: "urn:grn:authn:se:bankid:another-device:qr", responseMode: "json", pkce, redirectUri, prompt: "login" }).then((url) => { return fetch(url).then((response) => response.json()); }).then((response) => { setPKCE(pkce); setQrCode(response.initialQrCode); setPollUrl(response.pollUrl); }).catch(console.error); }), [buildAuthorizeUrl, redirectUri]); useEffect(() => { refresh(); }, [refresh]); const handleRetry = () => { setCompleting(false); refresh(); }; const handleError = useCallback((error) => { if (error === "Collect failed: startFailed" || error === "\"Collect failed: startFailed\"") { handleRetry(); return; } setError(new Error(error)); handleResponse(new Error(error), { pkce, redirectUri, source: "SEBankIDQrCode" }); }, [pkce, redirectUri]); const handleComplete = useCallback(function() { var _ref2 = _asyncToGenerator(function* (completeUrl) { setCompleting(true); const required = { pkce }; const parsed = yield parseCompleteUrl(completeUrl); if (parsed instanceof Error) return handleError(parsed.message); const { location, response } = parsed; if (completionStrategy === "openidprovider") { window.location.href = location; return; } yield handleResponse(response, { pkce: required.pkce, redirectUri, source: "SEBankIDQrCode" }); }); return function(_x) { return _ref2.apply(this, arguments); }; }(), [completionStrategy, pkce]); /** * Render QR codes */ const qrDataURL = useDraw(qrCode, { width: fullscreen ? (_window$innerHeight = window.innerHeight) !== null && _window$innerHeight !== void 0 ? _window$innerHeight : (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.offsetWidth : (_wrapperRef$current2 = wrapperRef.current) === null || _wrapperRef$current2 === void 0 ? void 0 : _wrapperRef$current2.offsetWidth, qrMargin: (_props$qrMargin = props.qrMargin) !== null && _props$qrMargin !== void 0 ? _props$qrMargin : fullscreen ? 2 : void 0 }); /** * Poll for response */ usePoll(pollUrl, { enabled: !isCompleting, onQrCode: setQrCode, onComplete: handleComplete, onError: handleError }); const i18n = getI18n(language); const imageElement = /* @__PURE__ */ jsx("button", { "aria-label": i18n.thisIsTheQR, ref: wrapperRef, className: "criipto-se-bankid-qr-canvas", onClick: () => setFullscreen((val) => !val), children: qrDataURL ? /* @__PURE__ */ jsx("img", { src: qrDataURL }) : null }); const qrElement = /* @__PURE__ */ jsxs("div", { className: "criipto-se-bankid-qr", children: [/* @__PURE__ */ jsxs("aside", { className: "criipto-se-bankid-qr--help-text", children: [isCompleting ? i18n.completingLogin : i18n.openAndScan, /* @__PURE__ */ jsx("img", { src: logo_2x_default })] }), imageElement] }); if (!qrCode && props.fallback) return props.fallback; if (props.children) return props.children({ qrElement, imageElement, error, isCompleting, retry: () => handleRetry(), fullscreen }); if (fullscreen) return /* @__PURE__ */ jsx("dialog", { className: "criipto-se-bankid-qr-fullscreen", open: true, children: imageElement }); return qrElement; } SEBankIDQrCode.acr_values = "urn:grn:authn:se:bankid:another-device:qr"; function useDraw(qrCode, options) { const { width, qrMargin } = options; const [dataURL, setDataURL] = useState(null); useEffect(() => { if (!qrCode) return; let isSubscribed = true; _asyncToGenerator(function* () { const qrImage = yield QRCode$1.toDataURL(qrCode, { errorCorrectionLevel: "low", scale: 10, width, margin: qrMargin !== null && qrMargin !== void 0 ? qrMargin : 4 }); if (!isSubscribed) return; setDataURL(qrImage); })(); return () => { isSubscribed = false; }; }, [ qrCode, qrMargin, width ]); return dataURL; } function usePoll(pollUrl, options) { const { onQrCode, onComplete, onError, enabled } = options; useEffect(() => { if (!pollUrl) return; if (!enabled) return; const delay = 2500; let timeout; let isSubscribed = true; const poll = function() { var _ref3 = _asyncToGenerator(function* () { if (!isSubscribed) return; const response = yield fetch(pollUrl); if (!isSubscribed) return; if (response.status < 400) { const payload = yield response.json(); if (payload.qrCode) onQrCode(payload.qrCode); if (payload.targetUrl) { onComplete(payload.targetUrl); return; } } else onError(yield response.text()); if (response.status === 202) timeout = setTimeout(poll, delay); }); return function poll() { return _ref3.apply(this, arguments); }; }(); timeout = setTimeout(poll, delay); return () => { isSubscribed = false; clearTimeout(timeout); }; }, [ pollUrl, onQrCode, onComplete, onError, enabled ]); } function parseCompleteUrl(_x2) { return _parseCompleteUrl.apply(this, arguments); } function _parseCompleteUrl() { _parseCompleteUrl = _asyncToGenerator(function* (completeUrl) { /** * Handle verify response inconsistencies */ if (completeUrl.includes("error=")) return { location: completeUrl, response: searchParamsToPOJO$1(new URL(completeUrl).searchParams) }; const completeResponse = yield fetch(completeUrl); if (completeResponse.status >= 400) return new Error(yield completeResponse.text()); const { location } = yield completeResponse.json(); return { location, response: searchParamsToPOJO$1(new URL(location).searchParams) }; }); return _parseCompleteUrl.apply(this, arguments); } const AuthButtonGroupContext = createContext({ multiple: false, acrValues: [], disabled: false, setDisabled: () => {} }); function AuthButtonGroup(props) { const acrValues = React.Children.toArray(props.children).flatMap((child) => { if (!child || typeof child !== "object") return []; if ("type" in child) { if (child.type === AuthMethodButtonContainer) { const props = child.props; return props.acrValue ? [props.acrValue] : []; } if (child.type === AuthMethodButtonComponent) { const props = child.props; return props.acrValue ? [props.acrValue] : []; } if (child.type === SEBankIDQrCode) return ["urn:grn:authn:se:bankid:another-device:qr"]; } return []; }); const [disabled, setDisabled] = useState(false); const context = { multiple: acrValues.length > 1, acrValues, disabled, setDisabled }; return /* @__PURE__ */ jsx(AuthButtonGroupContext.Provider, { value: context, children: props.children }); } //#endregion //#region src/components/Spinner/Spinner.tsx function Spinner() { return /* @__PURE__ */ jsx("div", { className: "criipto-eid-loader" }); } //#endregion //#region src/components/SEBankIDSameDeviceButton.tsx function searchParamsToPOJO(input) { return Array.from(input.keys()).reduce((memo, key) => { memo[key] = input.get(key); return memo; }, {}); } function determineStrategy(input, loginHint) { var _userAgent$browser$na; const userAgent = getUserAgent(input); const mobileOS = (userAgent === null || userAgent === void 0 ? void 0 : userAgent.os.name) === "iOS" ? "iOS" : (userAgent === null || userAgent === void 0 ? void 0 : userAgent.os.name) === "Android" ? "android" : (userAgent === null || userAgent === void 0 ? void 0 : userAgent.browser.name) === "Samsung Internet" ? "android" : null; const iOSSafari = mobileOS === "iOS" && (userAgent === null || userAgent === void 0 || (_userAgent$browser$na = userAgent.browser.name) === null || _userAgent$browser$na === void 0 ? void 0 : _userAgent$browser$na.includes("Safari")) ? true : false; const androidChrome = mobileOS === "android" && (userAgent === null || userAgent === void 0 ? void 0 : userAgent.browser.name) === "Mobile Chrome" ? true : false; const redirect = iOSSafari && !(loginHint === null || loginHint === void 0 ? void 0 : loginHint.includes("appswitch:resumeUrl:disable")); return { resume: mobileOS ? iOSSafari && redirect ? "Reload" : "Foreground" : "Poll", linkType: mobileOS === "iOS" || androidChrome ? "universal" : "scheme", redirect }; } var NotDoneError = class extends Error { constructor(message) { super(message); this.name = "NotDoneError"; } }; function fetchComplete(_x) { return _fetchComplete.apply(this, arguments); } function _fetchComplete() { _fetchComplete = _asyncToGenerator(function* (completeUrl) { const completeResponse = yield fetch(completeUrl); if (completeResponse.status >= 400) { const errorMessage = yield completeResponse.text(); if (errorMessage.includes("Native-app driven authentication is not done")) return new NotDoneError(errorMessage); return new Error(errorMessage); } const { location } = yield completeResponse.json(); return { location }; }); return _fetchComplete.apply(this, arguments); } function SEBankIDSameDeviceButton(props) { var _autoHydratedState$li, _autoHydratedState$pk; const { loginHint } = useContext(CriiptoVerifyContext); const group = useContext(AuthButtonGroupContext); const rawUserAgent = typeof navigator !== "undefined" ? navigator.userAgent : props.userAgent; const strategy = useMemo(() => determineStrategy(rawUserAgent, loginHint), [rawUserAgent, loginHint]); const [href, setHref] = useState(); const [links, setLinks] = useState((_autoHydratedState$li = autoHydratedState === n