@saasbase-io/core-elements 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function M(e,t,s,o){function r(n){return n instanceof s?n:new s(function(i){i(n)})}return new(s||(s=Promise))(function(n,i){function a(m){try{u(o.next(m))}catch(f){i(f)}}function d(m){try{u(o.throw(m))}catch(f){i(f)}}function u(m){m.done?n(m.value):r(m.value).then(a,d)}u((o=o.apply(e,t||[])).next())})}class Je{constructor(){this.data=new Map}static get Instance(){return Je.instance||(Je.instance=new Je),Je.instance}get(t){return this.data.get(t)}set(t,s){this.data.set(t,s)}setConfig(t){this.data.set("config",t)}getConfig(){return this.data.get("config")}getApiBaseUrl(){const t=this.getConfig().domain;return t.startsWith("localhost")?`http://${t}`:`https://${t}`}remove(t){this.data.delete(t)}clear(){this.data.clear()}}const ft=Je.Instance;class Z extends Error{constructor(t,s,o){super(s),this.name="LoginflowError",this.code=t,this.details=o}}class te{constructor(){}static get Instance(){return te.instance||(te.instance=new te),te.instance}request(t){return M(this,arguments,void 0,function*(s,o={}){const r=ft.getConfig(),n=`${ft.getApiBaseUrl()}${s}`,i=5e4,a=new AbortController,d=setTimeout(()=>a.abort(),i);try{const u=yield fetch(n,Object.assign(Object.assign({},o),{signal:a.signal,headers:Object.assign(Object.assign(Object.assign({"Content-Type":"application/json"},r.apiKey&&{Authorization:`Bearer ${r.apiKey}`}),r.headers),o.headers)}));if(clearTimeout(d),!u.ok){const m=yield u.json().catch(()=>({}));throw new Z(m.code||"API_ERROR",m.message||`HTTP ${u.status}: ${u.statusText}`,m.details)}return yield u.json()}catch(u){throw clearTimeout(d),u instanceof Z?u:u instanceof Error?u.name==="AbortError"?new Z("TIMEOUT","Request timeout"):new Z("NETWORK_ERROR",u.message):new Z("UNKNOWN_ERROR","An unknown error occurred")}})}post(t,s){return M(this,void 0,void 0,function*(){return this.request(t,{credentials:"include",method:"POST",body:s?JSON.stringify(s):void 0})})}get(t){return M(this,void 0,void 0,function*(){return this.request(t,{credentials:"include",method:"GET"})})}}class ct{constructor(){this.AUTH_SESSION_STORAGE_KEY="saasbase_auth_session",this.refreshTimeoutId=null,this.refreshPromise=null}static get Instance(){return ct.instance||(ct.instance=new ct),ct.instance}setCurrentSession(t){localStorage.setItem(this.AUTH_SESSION_STORAGE_KEY,JSON.stringify(t))}getCurrentSession(){const t=this.getStoredSession();return t?this.isSessionExpired(t)?(this.clearSession(),null):(this.shouldRefreshToken(t)&&this.triggerBackgroundRefresh(t),t):null}isAuthenticated(){return this.getCurrentSession()!==null}getAuthenticatedUser(){const t=this.getCurrentSession();if(!t||!t.id_token)return null;const s=this.decodeJWT(t.id_token);return s||null}cleanup(){this.refreshTimeoutId&&(clearTimeout(this.refreshTimeoutId),this.refreshTimeoutId=null),this.refreshPromise=null}clearSession(){localStorage.removeItem(this.AUTH_SESSION_STORAGE_KEY)}isSessionExpired(t){return!t||!t.expires_at?!0:Date.now()>=t.expires_at}cleanExpiredSession(){const t=this.getStoredSession();t&&this.isSessionExpired(t)&&this.clearSession()}getStoredSession(){const t=localStorage.getItem(this.AUTH_SESSION_STORAGE_KEY);if(!t)return null;try{return JSON.parse(t)}catch{return this.clearSession(),null}}decodeJWT(t){try{const o=t.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),r=decodeURIComponent(atob(o).split("").map(n=>"%"+("00"+n.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(r)}catch{return null}}shouldRefreshToken(t){if(!t||!t.expires_at||!t.refresh_token)return!1;const s=Date.now(),o=t.expires_at,r=t.expires_in||3600,n=s+r*1e3*.2;return o<=n}scheduleTokenRefresh(){this.refreshTimeoutId&&(clearTimeout(this.refreshTimeoutId),this.refreshTimeoutId=null);const t=this.getCurrentSession();if(!t||!t.expires_at||!t.refresh_token)return;const s=Date.now(),o=t.expires_at,r=t.expires_in||3600,n=o-s-r*1e3*.2;n>0&&(this.refreshTimeoutId=setTimeout(()=>{this.triggerBackgroundRefresh(t)},n))}triggerBackgroundRefresh(t){this.refreshPromise||t.refresh_token&&(this.refreshPromise=this.refreshTokens(t.refresh_token).then(s=>(s&&(this.setCurrentSession(s),this.scheduleTokenRefresh()),s)).catch(s=>(console.error("Background token refresh failed:",s),null)).finally(()=>{this.refreshPromise=null}))}refreshTokens(t){return M(this,void 0,void 0,function*(){try{const s=yield te.Instance.post("/login-flow/v1/refresh",{refresh_token:t});return{id_token:s.id_token,access_token:s.access_token,refresh_token:s.refresh_token,expires_in:s.expires_in,expires_at:Date.now()+s.expires_in*1e3}}catch(s){return s instanceof Z?console.error("Token refresh failed:",s.message,s.details):console.error("Token refresh failed:",s),this.clearSession(),null}})}}class Oa{constructor(t=sessionStorage){this.storage=t,this.FLOW_STATE_KEY="saasbase_loginflow_state",this.FLOW_EXPIRY_MS=540*1e3}getFlowState(){const t=this.storage.getItem(this.FLOW_STATE_KEY);if(!t)return null;try{const s=JSON.parse(t);return this.isExpired(s)?(this.clearFlowState(),null):s}catch{return this.clearFlowState(),null}}saveFlowState(t,s,o){const r=Date.now(),n=this.getFlowState(),i={flowType:t,flow_id:s,state:o.state,startedAt:(n==null?void 0:n.startedAt)||r,lastUpdatedAt:r,render_spec:o.render_spec};this.storage.setItem(this.FLOW_STATE_KEY,JSON.stringify(i))}restoreFromHistory(t){const s={flowType:t.flowType,flow_id:t.flow_id,state:t.state,startedAt:t.timestamp,lastUpdatedAt:t.timestamp,render_spec:void 0};this.storage.setItem(this.FLOW_STATE_KEY,JSON.stringify(s))}clearFlowState(){this.storage.removeItem(this.FLOW_STATE_KEY)}isExpired(t){return Date.now()-t.lastUpdatedAt>this.FLOW_EXPIRY_MS}get expiryMs(){return this.FLOW_EXPIRY_MS}}class cs{static detect(t,s=window.location){const o=s.pathname.toLowerCase();return o.includes(t.signinUrl)?"signin":o.includes(t.signupUrl)?"signup":"custom"}}class Aa{constructor(){this.listeners=new Map}on(t,s){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(s)}off(t,s){var o;(o=this.listeners.get(t))===null||o===void 0||o.delete(s)}emit(t,s){const o=this.listeners.get(t);o&&o.forEach(r=>{try{r(s)}catch(n){console.error(`[FlowEventBus] Error in event handler for ${t}:`,n)}})}clear(){this.listeners.clear()}clearEvent(t){this.listeners.delete(t)}}class _c{getNavigationType(){const t=performance.getEntriesByType("navigation");if(t.length>0)return t[0].type;const s=performance.navigation;if(s)switch(s.type){case 1:return"reload";case 2:return"back_forward";default:return"navigate"}return"navigate"}}class un{constructor(t,s,o=window.location,r,n){this.httpClient=te.Instance,this.redirectTimeoutId=null,this.eventHandlers=new Map([["switch_to_signin",this.handleSwitchToSignin.bind(this)],["switch_to_signup",this.handleSwitchToSignup.bind(this)]]);const i=ft.getConfig();if(!i.domain)throw new Z("CONFIG_ERROR","Domain is required");if(!i.clientId)throw new Z("CONFIG_ERROR","Client ID is required");if(!i.clientSecret)throw new Z("CONFIG_ERROR","Client Secret is required");this.flowId=t,this.location=o,this.flowStateManager=r||new Oa,this.flowEventBus=s||new Aa,this.navigationTypeDetector=n||new _c,ct.Instance.cleanExpiredSession(),ct.Instance.scheduleTokenRefresh()}static init(t,s,o,r,n){return M(this,void 0,void 0,function*(){const i=new un(t,s,o,r,n),a=yield i.initializeFlowInternal();return{sdk:i,flowType:a.flowType,isResumed:a.isResumed,state:a.state,render_spec:a.render_spec,auth_result:a.auth_result}})}startFlow(t,s){return M(this,void 0,void 0,function*(){if(!s.client_id)throw new Z("VALIDATION_ERROR","Client ID is required");const o=`/login-flow/v1/${t}/start`;return this.executeFlowRequest(()=>this.httpClient.post(o,s),"START_FLOW_ERROR","Failed to start flow")})}resumeFlow(t){return M(this,void 0,void 0,function*(){const s=`/login-flow/v1/${t}/resume`;return this.executeFlowRequest(()=>this.httpClient.post(s),"RESUME_FLOW_ERROR","Failed to resume flow")})}processEvent(t,s){return M(this,void 0,void 0,function*(){if(!t.event)throw new Z("VALIDATION_ERROR","Event type is required");const o=this.eventHandlers.get(t.event);return o?yield o():yield this.processServerEvent(t,s)})}executeFlowRequest(t,s,o,r){return M(this,void 0,void 0,function*(){try{const n=yield t();return r&&(yield r(n)),this.processAuthResult(n.auth_result),n.redirection&&this.redirect(n.redirection),n}catch(n){throw n instanceof Z?n:new Z(s,o,n instanceof Error?{originalError:n.message,stack:n.stack}:n)}})}processServerEvent(t,s){return M(this,void 0,void 0,function*(){let o=this.flowStateManager.getFlowState();if(!o&&(console.warn("Flow state expired, restarting flow before processing event"),yield this.initializeFlowInternal(),o=this.flowStateManager.getFlowState(),!o))throw new Z("FLOW_RESTART_ERROR","Failed to restart flow - no flow state after initialization");const r=o.state,n=`/login-flow/v1/${o.flowType}/events`,i=yield this.executeFlowRequest(()=>this.httpClient.post(n,t),"PROCESS_EVENT_ERROR","Failed to process event",s);if(!i.auth_result){this.flowStateManager.saveFlowState(o.flowType,o.flow_id,i);const a=this.flowStateManager.getFlowState();if(a){const d=r!==i.state;this.flowEventBus.emit("flow:updated",{flowState:a,isNewStep:d})}}return i})}processAuthResult(t){if(t){const s={id_token:t.id_token,access_token:t.access_token,refresh_token:t.refresh_token,expires_in:t.expires_in,expires_at:Date.now()+t.expires_in*1e3};ct.Instance.setCurrentSession(s),ct.Instance.scheduleTokenRefresh(),this.flowStateManager.clearFlowState(),this.reloadPage()}}initializeFlowInternal(){return M(this,void 0,void 0,function*(){const t=ft.getConfig(),s=cs.detect(t,this.location);this.validateFlowType(s);const o=this.flowStateManager.getFlowState();return this.shouldResumeFlow(o,s)?yield this.resumeExistingFlow(o,s):yield this.startNewFlow(s)})}validateFlowType(t){if(t==="custom"&&!this.flowId)throw new Z("FLOW_ID_ERROR","Flow ID is required for custom flows")}shouldResumeFlow(t,s){if(!t)return!1;const r=new URLSearchParams(this.location.search).get("resume")==="true",n=this.navigationTypeDetector.getNavigationType();if(n==="navigate"&&!r)return this.flowStateManager.clearFlowState(),console.log(`Fresh navigation detected (type: ${n}), starting new flow`),!1;if(t.flowType!==s)return this.flowStateManager.clearFlowState(),console.log(`Flow type changed from ${t.flowType} to ${s}, starting new flow, flow_id: ${this.flowId}`),!1;if(s==="custom"&&t.flow_id!==this.flowId)return this.flowStateManager.clearFlowState(),console.log(`Custom flow ID changed, starting new flow, flow_id: ${this.flowId}`),!1;const i=r?"explicit resume=true":`navigation type: ${n}`;return console.log(`Resuming existing flow (${i})`),!0}resumeExistingFlow(t,s){return M(this,void 0,void 0,function*(){try{const o=yield this.resumeFlow(s);if(!o.auth_result){this.flowStateManager.saveFlowState(s,t.flow_id,o);const r=this.flowStateManager.getFlowState();r&&this.flowEventBus.emit("flow:resumed",{flowState:r})}return{flowType:s,isResumed:!0,state:o.state,render_spec:o.render_spec,auth_result:o.auth_result}}catch(o){return this.flowStateManager.clearFlowState(),console.warn("Failed to resume flow, starting fresh:",o),yield this.startNewFlow(s)}})}startNewFlow(t){return M(this,void 0,void 0,function*(){const s=ft.getConfig(),o={client_id:s.clientId,client_secret:s.clientSecret,origin_url:this.location.href,signin_url:s.signinUrl,signup_url:s.signupUrl,locale:s.locale||"en",flow_id:this.determineFlowId(t)},r=yield this.startFlow(t,o);if(!r.auth_result){this.flowStateManager.saveFlowState(t,o.flow_id,r);const n=this.flowStateManager.getFlowState();n&&this.flowEventBus.emit("flow:started",{flowState:n})}return{flowType:t,isResumed:!1,state:r.state,render_spec:r.render_spec,auth_result:r.auth_result}})}determineFlowId(t){switch(t){case"signin":return this.flowId||"signin-flow";case"signup":return this.flowId||"signup-flow";case"custom":if(!this.flowId)throw new Z("FLOW_ID_ERROR","Flow ID is required for custom flows");return this.flowId}}handleSwitchToSignin(){return M(this,void 0,void 0,function*(){const t=ft.getConfig();return this.redirect({url:t.signinUrl,redirect_delay:0}),{state:"End"}})}handleSwitchToSignup(){return M(this,void 0,void 0,function*(){const t=ft.getConfig();return this.redirect({url:t.signupUrl,redirect_delay:0}),{state:"End"}})}redirect(t){t.url&&(this.redirectTimeoutId!==null&&(console.log("Clearing existing redirect timeout before scheduling new redirect to:",t.url),clearTimeout(this.redirectTimeoutId),this.redirectTimeoutId=null),this.redirectTimeoutId=setTimeout(()=>{this.location.href=t.url},t.redirect_delay*1e3))}reloadPage(){setTimeout(()=>{this.location.reload()},0)}getEventBus(){return this.flowEventBus}cleanup(){this.redirectTimeoutId&&(clearTimeout(this.redirectTimeoutId),this.redirectTimeoutId=null),this.flowEventBus.clear()}}class Cc{constructor(t){this.delayMs=t,this.timerId=null}debounce(t){this.timerId&&clearTimeout(this.timerId),this.timerId=setTimeout(()=>{t(),this.timerId=null},this.delayMs)}cancel(){this.timerId&&(clearTimeout(this.timerId),this.timerId=null)}isPending(){return this.timerId!==null}}class Sc{pushState(t,s,o){history.pushState(t,s,o)}replaceState(t,s,o){history.replaceState(t,s,o)}get state(){return history.state}}class $c{constructor(t,s,o=!1,r=window.location,n,i){this.popstateListener=null,this.pageshowListener=null,this.isProcessing=!1,this.isHandlingNavigationEvent=!1,this.onFlowRestartNeeded=null,this.onFlowRestartNeeded=s,this.skipHistoryTracking=o,this.debouncer=new Cc(200),this.location=r,this.flowStateManager=n||new Oa,this.historyAdapter=i||new Sc,this.subscribeToFlowEvents(t),this.initializeListener()}subscribeToFlowEvents(t){t.on("flow:started",this.handleFlowStart.bind(this)),t.on("flow:updated",this.handleFlowUpdate.bind(this)),t.on("flow:resumed",this.handleFlowResume.bind(this))}initializeListener(){this.popstateListener=t=>{console.log("[NavigationManager] popstate event received"),this.debouncer.debounce(()=>{console.log("[NavigationManager] Debounce timeout fired, handling popstate"),this.handlePopState(t)})},this.pageshowListener=t=>{t.persisted&&(console.log("[NavigationManager] Page restored from bfcache"),this.debouncer.debounce(()=>{console.log("[NavigationManager] Handling bfcache restoration"),this.handleBfcacheRestoration()}))},window.addEventListener("popstate",this.popstateListener),window.addEventListener("pageshow",this.pageshowListener)}handlePopState(t){return M(this,void 0,void 0,function*(){if(this.isProcessing){console.log("[NavigationManager] Already processing popstate, skipping");return}console.log("[NavigationManager] Starting popstate processing"),this.isProcessing=!0,this.isHandlingNavigationEvent=!0;try{const s=t.state,o=this.flowStateManager.getFlowState(),r=ft.getConfig(),n=cs.detect(r,this.location);if(s&&this.restoreHistoryStateToSession(s),this.shouldRestartFlow(s,o,n)){yield this.triggerFlowRestart();return}console.log("[NavigationManager] Valid state restored from history")}finally{this.isProcessing=!1,this.isHandlingNavigationEvent=!1}})}handleBfcacheRestoration(){return M(this,void 0,void 0,function*(){if(this.isProcessing){console.log("[NavigationManager] Already processing bfcache restoration, skipping");return}console.log("[NavigationManager] Starting bfcache restoration processing"),this.isProcessing=!0,this.isHandlingNavigationEvent=!0;try{const t=this.historyAdapter.state,s=this.flowStateManager.getFlowState(),o=ft.getConfig(),r=cs.detect(o,this.location);if(t&&this.restoreHistoryStateToSession(t),this.shouldRestartFlow(t,s,r)){yield this.triggerFlowRestart();return}console.log("[NavigationManager] Valid state restored from bfcache")}finally{this.isProcessing=!1,this.isHandlingNavigationEvent=!1}})}restoreHistoryStateToSession(t){console.log("[NavigationManager] Restoring history state to session storage:",t),this.flowStateManager.restoreFromHistory(t)}shouldRestartFlow(t,s,o){return t&&t.flowType!==o?(console.log("[NavigationManager] Flow type changed via navigation:",t.flowType,"→",o),this.flowStateManager.clearFlowState(),!0):t?Date.now()-t.timestamp>this.flowStateManager.expiryMs?(console.log("[NavigationManager] History state expired, clearing and restarting flow"),this.flowStateManager.clearFlowState(),!0):(console.log("[NavigationManager] Valid history state restored, triggering restart to resume flow"),!0):(console.log("[NavigationManager] No history state, skipping"),!1)}triggerFlowRestart(){return M(this,void 0,void 0,function*(){this.onFlowRestartNeeded&&(yield this.onFlowRestartNeeded())})}createHistoryState(t){return{flowType:t.flowType,flow_id:t.flow_id,state:t.state,timestamp:Date.now()}}updateBrowserHistory(t,s){if(this.skipHistoryTracking){console.log("[NavigationManager] Skipping history tracking (created from navigation)");return}const o=this.createHistoryState(t);try{s==="push"?(this.historyAdapter.pushState(o,"",this.location.href),console.log("[NavigationManager] Pushed state to history")):(this.historyAdapter.replaceState(o,"",this.location.href),console.log("[NavigationManager] Replaced state in history"))}catch(r){console.error("[NavigationManager] Failed to update history:",r)}}handleFlowStart(t){this.updateBrowserHistory(t.flowState,"replace")}handleFlowUpdate(t){const s=this.isHandlingNavigationEvent||!t.isNewStep?"replace":"push";this.updateBrowserHistory(t.flowState,s)}handleFlowResume(t){this.updateBrowserHistory(t.flowState,"replace")}destroy(){this.debouncer.cancel(),this.popstateListener&&(window.removeEventListener("popstate",this.popstateListener),this.popstateListener=null),this.pageshowListener&&(window.removeEventListener("pageshow",this.pageshowListener),this.pageshowListener=null),this.onFlowRestartNeeded=null}}let fe=new AbortController;class Ec{constructor(){this.httpClient=te.Instance,this.capabilities={webauthn:!1},this.capabilitiesDetected=!1}detectCapabilities(){return M(this,void 0,void 0,function*(){if(!this.capabilitiesDetected)try{if(typeof window.PublicKeyCredential=="function"?this.capabilities.webauthn=!0:this.capabilities.webauthn=!1,typeof PublicKeyCredential.getClientCapabilities=="function"){const t=yield PublicKeyCredential.getClientCapabilities();this.capabilities=Object.assign(Object.assign({},this.capabilities),t);return}typeof PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable=="function"&&(this.capabilities.userVerifyingPlatformAuthenticator=yield PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()),(PublicKeyCredential==null?void 0:PublicKeyCredential.isConditionalMediationAvailable)!==void 0&&typeof PublicKeyCredential.isConditionalMediationAvailable=="function"?this.capabilities.conditionalGet=yield PublicKeyCredential.isConditionalMediationAvailable():this.capabilities.conditionalGet=!1}finally{this.capabilitiesDetected=!0}})}fetchPublicKeyCredentialRequestOptions(t,s){return M(this,void 0,void 0,function*(){try{return this.httpClient.post("/webauthn/login/options",{conditional:s,flow_type:t})}catch(o){return console.error("error",o),null}})}fetchPublicKeyCredentialCreationOptions(t,s){return M(this,void 0,void 0,function*(){try{return this.httpClient.post("/webauthn/register/options",{conditional:s,flow_type:t})}catch(o){return console.error("error",o),null}})}startRegistration(t,s){return M(this,void 0,void 0,function*(){if(yield this.detectCapabilities(),!t)return console.error("No PublicKeyCredentialCreationOptionsJSON provided"),null;fe.abort(),fe=new AbortController;const o=this.toPublicKeyCredentialCreationOptions(t),r={signal:fe.signal,publicKey:o};if(s){if(!this.isConditionalCreateAvailable())return console.error("Conditional create mediation not supported in this browser"),null;r.mediation="conditional"}try{const n=yield navigator.credentials.create(r);if(!n){console.error("no credential returned by authenticator");return}const{id:i,rawId:a,response:d,type:u}=n;let m;typeof d.getTransports=="function"&&(m=d.getTransports());let f;if(typeof d.getPublicKeyAlgorithm=="function")try{f=d.getPublicKeyAlgorithm()}catch(x){co("getPublicKeyAlgorithm()",x)}let y;if(typeof d.getPublicKey=="function")try{const x=d.getPublicKey();x!==null&&(y=this.bufferToBase64URLString(x))}catch(x){co("getPublicKey()",x)}let w;if(typeof d.getAuthenticatorData=="function")try{w=this.bufferToBase64URLString(d.getAuthenticatorData())}catch(x){co("getAuthenticatorData()",x)}return{id:i,rawId:this.bufferToBase64URLString(a),response:{attestationObject:this.bufferToBase64URLString(d.attestationObject),clientDataJSON:this.bufferToBase64URLString(d.clientDataJSON),transports:m,publicKeyAlgorithm:f,publicKey:y,authenticatorData:w},type:u,clientExtensionResults:n.getClientExtensionResults(),authenticatorAttachment:this.toAuthenticatorAttachment(n.authenticatorAttachment)}}catch(n){const i=this.identifyAuthenticationError({error:n,options:r});return i instanceof ve?console.error(`WebAuthn authentication failed with code: ${i.code}, message: ${i.message}`):console.error("WebAuthn authentication failed with error:",n),null}})}startAuthentication(t,s){return M(this,void 0,void 0,function*(){if(yield this.detectCapabilities(),!t)return console.error("No PublicKeyCredentialRequestOptionsJSON provided"),null;if(!this.isWebAuthnAvailable())return console.error("WebAuthn not supported in this browser"),null;fe.abort(),fe=new AbortController;const o=this.toPublicKeyCredentialRequestOptions(t),r={signal:fe.signal,publicKey:o};if(s){if(!this.isConditionalGetAvailable())return console.error("Conditional mediation not supported in this browser"),null;r.mediation="conditional",r.publicKey&&(r.publicKey.allowCredentials=[])}try{const n=yield navigator.credentials.get(r);return this.toPublicKeyCredentialJSON(n)}catch(n){const i=this.identifyAuthenticationError({error:n,options:r});return i instanceof ve?console.error(`WebAuthn authentication failed with code: ${i.code}, message: ${i.message}`):console.error("WebAuthn authentication failed with error:",n),null}})}signalUnknownCredential(t,s){return M(this,void 0,void 0,function*(){if(yield this.detectCapabilities(),this.isSignalUnknownCredentialAvailable())try{yield PublicKeyCredential.signalUnknownCredential({rpId:t,credentialId:s})}catch(o){console.error("Failed to signal unknown credential:",o)}else console.warn("signalUnknownCredential not supported in this browser")})}signalCurrentUserDetails(t){return M(this,void 0,void 0,function*(){if(yield this.detectCapabilities(),this.isSignalCurrentUserDetailsAvailable())try{yield PublicKeyCredential.signalCurrentUserDetails(t)}catch(s){console.error("Failed to signal current user details:",s)}else console.warn("signalCurrentUserDetails not supported. Ask user to update manually.")})}toPublicKeyCredentialDescriptor(t){const{id:s}=t;return Object.assign(Object.assign({},t),{id:this.base64URLStringToBuffer(s),type:t.type,transports:t.transports})}toAuthenticatorAttachment(t){const s=["cross-platform","platform"];if(t&&!(s.indexOf(t)<0))return t}bufferToBase64URLString(t){const s=new Uint8Array(t);let o="";for(const n of s)o+=String.fromCharCode(n);return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}base64URLStringToBuffer(t){const s=t.replace(/-/g,"+").replace(/_/g,"/"),o=(4-s.length%4)%4,r=s.padEnd(s.length+o,"="),n=atob(r),i=new ArrayBuffer(n.length),a=new Uint8Array(i);for(let d=0;d<n.length;d++)a[d]=n.charCodeAt(d);return i}toPublicKeyCredentialJSON(t){if(typeof t.toJSON=="function")return t.toJSON();const s=t.response;return{id:t.id,type:t.type,rawId:this.bufferToBase64URLString(t.rawId),authenticatorAttachment:this.toAuthenticatorAttachment(t.authenticatorAttachment),clientExtensionResults:t==null?void 0:t.getClientExtensionResults(),response:{clientDataJSON:this.bufferToBase64URLString(s.clientDataJSON),authenticatorData:this.bufferToBase64URLString(s.authenticatorData),signature:this.bufferToBase64URLString(s.signature),userHandle:s.userHandle?this.bufferToBase64URLString(s.userHandle):void 0}}}toPublicKeyCredentialCreationOptions(t){var s;if(typeof PublicKeyCredential.parseCreationOptionsFromJSON=="function")return PublicKeyCredential.parseCreationOptionsFromJSON(t);const o={challenge:this.base64URLStringToBuffer(t.challenge),rp:{id:t.rp.id,name:t.rp.name},user:{displayName:t.user.displayName,id:this.base64URLStringToBuffer(t.user.id),name:t.user.name},pubKeyCredParams:t.pubKeyCredParams.map(r=>({alg:r.alg,type:r.type})),timeout:t.timeout};if(t.attestation&&(o.attestation=t.attestation),t.extensions!==void 0&&(o.extensions=t.extensions),t.authenticatorSelection){const r={};t.authenticatorSelection.authenticatorAttachment&&(r.authenticatorAttachment=t.authenticatorSelection.authenticatorAttachment),t.authenticatorSelection.requireResidentKey&&(r.requireResidentKey=t.authenticatorSelection.requireResidentKey),t.authenticatorSelection.residentKey&&(r.residentKey=t.authenticatorSelection.residentKey),t.authenticatorSelection.userVerification&&(r.userVerification=t.authenticatorSelection.userVerification),o.authenticatorSelection=r}return!((s=t.excludeCredentials)===null||s===void 0)&&s.length&&(o.excludeCredentials=t.excludeCredentials.map(r=>this.toPublicKeyCredentialDescriptor(r))),o}toPublicKeyCredentialRequestOptions(t){var s;if(typeof PublicKeyCredential.parseRequestOptionsFromJSON=="function")return PublicKeyCredential.parseRequestOptionsFromJSON(t);const o={challenge:this.base64URLStringToBuffer(t.challenge)};return t.rpId!==void 0&&(o.rpId=t.rpId),t.timeout!==void 0&&(o.timeout=t.timeout),t.userVerification!==void 0&&(o.userVerification=t.userVerification),t.extensions!==void 0&&(o.extensions=t.extensions),!((s=t.allowCredentials)===null||s===void 0)&&s.length&&(o.allowCredentials=t.allowCredentials.map(r=>this.toPublicKeyCredentialDescriptor(r))),o}identifyAuthenticationError({error:t,options:s}){const{publicKey:o}=s;if(!o)throw Error("options was missing required publicKey property");if(t.name==="AbortError"){if(s.signal instanceof AbortSignal)return new ve({message:"Authentication ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:t})}else{if(t.name==="NotAllowedError")return new ve({message:t.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:t});if(t.name==="SecurityError"){const r=window.location.hostname;if(kc(r)){if(o.rpId!==r)return new ve({message:`The RP ID "${o.rpId}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:t})}else return new ve({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:t})}else if(t.name==="UnknownError")return new ve({message:"The authenticator was unable to process the specified options, or could not create a new assertion signature",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:t})}return t}isPublicKeyCredentialRequestOptionsJSON(t){return typeof t=="object"&&t!==null&&"challenge"in t}isWebAuthnAvailable(){return this.capabilities.webauthn}isSignalAllAcceptedCredentialsAvailable(){return!!this.capabilities.signalAllAcceptedCredentials}isSignalCurrentUserDetailsAvailable(){return!!this.capabilities.signalCurrentUserDetails}isSignalUnknownCredentialAvailable(){return!!this.capabilities.signalUnknownCredential}isConditionalGetAvailable(){return!!this.capabilities.conditionalGet}isConditionalCreateAvailable(){return!!this.capabilities.conditionalCreate}isHybridTransportAvailable(){return!!this.capabilities.hybridTransport}destroy(){fe.abort()}}function co(e,t){console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${e}. You should report this error to them.
2
- `,t)}class ve extends Error{constructor({message:t,code:s,cause:o,name:r}){super(t),this.name=r??o.name,this.code=s}}function kc(e){return e==="localhost"||/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(e)}class Qe{constructor(){this.configured=!1,this.loginflow=null,this.navigationManager=null,this.flowEventBus=null,this.webauthnService=null}static get Instance(){return Qe.instance||(Qe.instance=new Qe),Qe.instance}ensureConfigured(){if(!this.configured)throw new Z("NOT_CONFIGURED","Auth.configure() must be called before using Auth methods")}ensureLoginflowInitialized(){if(!this.loginflow)throw new Z("LOGINFLOW_NOT_INITIALIZED","Call Auth.startLoginflow() first")}startLoginflowInternal(t){return M(this,arguments,void 0,function*(s,o=!1){this.ensureConfigured(),this.webauthnService&&(console.log("[AuthService] Cleaning up and aborting ongoing webauthn flows"),this.webauthnService.destroy(),this.webauthnService=null),this.loginflow&&(console.log("[AuthService] Cleaning up old loginflow instance"),this.loginflow.cleanup(),this.loginflow=null),this.navigationManager&&(console.log("[AuthService] Cleaning up old navigation manager instance"),this.navigationManager.destroy(),this.navigationManager=null),this.webauthnService=new Ec,this.webauthnService.detectCapabilities(),this.flowEventBus=new Aa,this.navigationManager=new $c(this.flowEventBus,()=>M(this,void 0,void 0,function*(){console.log("[AuthService] Navigation restart triggered, restarting loginflow"),yield this.startLoginflowInternal(void 0,!1)}),o);const r=yield un.init(s,this.flowEventBus),n=ft.getConfig();this.startPasskeyAuthentication(r.flowType,!0),this.loginflow=r.sdk;const i={flowType:r.flowType,isResumed:r.isResumed,state:r.state,render_spec:r.render_spec,auth_result:r.auth_result};return n.onFlowStateChange&&n.onFlowStateChange({eventType:r.isResumed?"flow_resumed":"flow_started",flowType:r.flowType,state:r.state,render_spec:r.render_spec,auth_result:r.auth_result,isResumed:r.isResumed}),i})}configure(t){if(!t.domain||!t.clientId||!t.clientSecret)throw new Z("CONFIG_ERROR","domain, clientId, and clientSecret are required");ft.setConfig(t),this.configured=!0}isAuthenticated(){return this.ensureConfigured(),ct.Instance.isAuthenticated()}getCurrentSession(){return this.ensureConfigured(),ct.Instance.getCurrentSession()}getAuthenticatedUser(){return this.ensureConfigured(),ct.Instance.getAuthenticatedUser()}startLoginflow(t){return M(this,void 0,void 0,function*(){return this.startLoginflowInternal(t,!1)})}processLoginflowEvent(t){return M(this,void 0,void 0,function*(){var s,o;this.ensureConfigured(),this.ensureLoginflowInitialized();const r=ft.getConfig(),n=cs.detect(r,location);if(t.event==="signin_with_passkey"&&!(!((s=t.data)===null||s===void 0)&&s.credential_request)){const a=yield this.getPasskeyCredential(n,!1);if(!a||!a.cred)return console.error("no credential returned by browser"),{state:""};t.data={credential_request:a.cred}}if(t.event==="register_passkey"&&!(!((o=t.data)===null||o===void 0)&&o.conditional)){console.log("attempt to register a passkey");const a=yield this.createPasskeyCredential(n,!1);if(!a)return console.error("no credential created by browser"),{state:""};t.data={credential_creation_response:a,conditional:!1}}const i=yield this.loginflow.processEvent(t,a=>M(this,void 0,void 0,function*(){var d;if(!((d=a.data)===null||d===void 0)&&d.conditional_create_enabled){console.log("attempt to register a passkey conditionally");const u=yield this.registerPasskey(n,!0);u&&(u.render_spec=void 0)}}));if(console.log("render spec",i.render_spec),r.onFlowStateChange){const a=sessionStorage.getItem("saasbase_loginflow_state");let d="custom";if(a)try{d=JSON.parse(a).flowType}catch(u){console.error("Failed to parse flow state:",u)}r.onFlowStateChange({eventType:"flow_updated",flowType:d,state:i.state,render_spec:i.render_spec,auth_result:i.auth_result})}return i})}getPasskeyCredential(t,s){return M(this,void 0,void 0,function*(){var o,r;const n=yield(o=this.webauthnService)===null||o===void 0?void 0:o.fetchPublicKeyCredentialRequestOptions(t,s);if(!n)return console.error("failed to fetch public key request options"),null;try{return{cred:yield(r=this.webauthnService)===null||r===void 0?void 0:r.startAuthentication(n,s),options:n}}catch(i){return console.error("error starting authentication",i),null}})}startPasskeyAuthentication(t){return M(this,arguments,void 0,function*(s,o=!1){var r,n;const i=yield this.getPasskeyCredential(s,o);if(!i||!i.cred){console.error("no credential found");return}const{cred:a,options:d}=i;try{((r=(yield this.processLoginflowEvent({event:"signin_with_passkey",data:{credential_request:a}})).data)===null||r===void 0?void 0:r.error)==="CredentialNotFound"&&(d.rpId&&a.id?(n=this.webauthnService)===null||n===void 0||n.signalUnknownCredential(d.rpId,a.id):console.log("missing rpId or credential id for signalUnknownCredential"))}catch(u){console.error("failed to signin with passkey",u)}})}createPasskeyCredential(t,s){return M(this,void 0,void 0,function*(){var o,r;try{const n=yield(o=this.webauthnService)===null||o===void 0?void 0:o.fetchPublicKeyCredentialCreationOptions(t,s);if(!n){console.warn("no credential creation option returned by webauthn service"),console.log("skipping conditional passkey registration...");return}return(r=this.webauthnService)===null||r===void 0?void 0:r.startRegistration(n,s)}catch(n){console.log("error",n)}})}registerPasskey(t){return M(this,arguments,void 0,function*(s,o=!1){try{const r=yield this.createPasskeyCredential(s,o),n=yield this.processLoginflowEvent({event:"register_passkey",data:{credential_creation_response:r,conditional:o}});return console.log("passkey creation res",n),n}catch(r){console.log("error",r)}})}signOut(){this.ensureConfigured(),ct.Instance.clearSession(),ct.Instance.cleanup(),this.loginflow&&(this.loginflow.cleanup(),this.loginflow=null),this.navigationManager&&(this.navigationManager.destroy(),this.navigationManager=null),this.flowEventBus&&(this.flowEventBus.clear(),this.flowEventBus=null),window.location.reload(),console.log("Refreshed the page to clear any in-memory state")}}const Ft=Qe.Instance;/**
2
+ `,t)}class ve extends Error{constructor({message:t,code:s,cause:o,name:r}){super(t),this.name=r??o.name,this.code=s}}function kc(e){return e==="localhost"||/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(e)}class Qe{constructor(){this.configured=!1,this.loginflow=null,this.navigationManager=null,this.flowEventBus=null,this.webauthnService=null}static get Instance(){return Qe.instance||(Qe.instance=new Qe),Qe.instance}ensureConfigured(){if(!this.configured)throw new Z("NOT_CONFIGURED","Auth.configure() must be called before using Auth methods")}ensureLoginflowInitialized(){if(!this.loginflow)throw new Z("LOGINFLOW_NOT_INITIALIZED","Call Auth.startLoginflow() first")}startLoginflowInternal(t){return M(this,arguments,void 0,function*(s,o=!1){this.ensureConfigured(),this.webauthnService&&(console.log("[AuthService] Cleaning up and aborting ongoing webauthn flows"),this.webauthnService.destroy(),this.webauthnService=null),this.loginflow&&(console.log("[AuthService] Cleaning up old loginflow instance"),this.loginflow.cleanup(),this.loginflow=null),this.navigationManager&&(console.log("[AuthService] Cleaning up old navigation manager instance"),this.navigationManager.destroy(),this.navigationManager=null),this.webauthnService=new Ec,this.webauthnService.detectCapabilities(),this.flowEventBus=new Aa,this.navigationManager=new $c(this.flowEventBus,()=>M(this,void 0,void 0,function*(){console.log("[AuthService] Navigation restart triggered, restarting loginflow"),yield this.startLoginflowInternal(void 0,!1)}),o);const r=yield un.init(s,this.flowEventBus),n=ft.getConfig();this.startPasskeyAuthentication(r.flowType,!0),this.loginflow=r.sdk;const i={flowType:r.flowType,isResumed:r.isResumed,state:r.state,render_spec:r.render_spec,auth_result:r.auth_result};return n.onFlowStateChange&&n.onFlowStateChange({eventType:r.isResumed?"flow_resumed":"flow_started",flowType:r.flowType,state:r.state,render_spec:r.render_spec,auth_result:r.auth_result,isResumed:r.isResumed}),i})}configure(t){if(!t.domain||!t.clientId||!t.clientSecret)throw new Z("CONFIG_ERROR","domain, clientId, and clientSecret are required");ft.setConfig(t),this.configured=!0}isAuthenticated(){return this.ensureConfigured(),ct.Instance.isAuthenticated()}getCurrentSession(){return this.ensureConfigured(),ct.Instance.getCurrentSession()}getAuthenticatedUser(){return this.ensureConfigured(),ct.Instance.getAuthenticatedUser()}startLoginflow(t){return M(this,void 0,void 0,function*(){return this.startLoginflowInternal(t,!1)})}processLoginflowEvent(t){return M(this,void 0,void 0,function*(){var s,o;this.ensureConfigured(),this.ensureLoginflowInitialized();const r=ft.getConfig(),n=cs.detect(r,location);if(t.event==="signin_with_passkey"&&!(!((s=t.data)===null||s===void 0)&&s.credential_request)){const a=yield this.getPasskeyCredential(n,!1);if(!a||!a.cred)return console.error("no credential returned by browser"),{state:""};t.data={credential_request:a.cred}}if(t.event==="register_passkey"&&!(!((o=t.data)===null||o===void 0)&&o.conditional)){console.log("attempt to register a passkey");const a=yield this.createPasskeyCredential(n,!1);if(!a)return console.error("no credential created by browser"),{state:""};t.data={credential_creation_response:a,conditional:!1}}const i=yield this.loginflow.processEvent(t,a=>M(this,void 0,void 0,function*(){var d;if(!((d=a.data)===null||d===void 0)&&d.conditional_create_enabled){console.log("attempt to register a passkey conditionally");const u=yield this.registerPasskey(n,!0);u&&(u.render_spec=void 0,a.render_spec=void 0)}}));if(console.log("render spec",i.render_spec),r.onFlowStateChange){const a=sessionStorage.getItem("saasbase_loginflow_state");let d="custom";if(a)try{d=JSON.parse(a).flowType}catch(u){console.error("Failed to parse flow state:",u)}r.onFlowStateChange({eventType:"flow_updated",flowType:d,state:i.state,render_spec:i.render_spec,auth_result:i.auth_result})}return i})}getPasskeyCredential(t,s){return M(this,void 0,void 0,function*(){var o,r;const n=yield(o=this.webauthnService)===null||o===void 0?void 0:o.fetchPublicKeyCredentialRequestOptions(t,s);if(!n)return console.error("failed to fetch public key request options"),null;try{return{cred:yield(r=this.webauthnService)===null||r===void 0?void 0:r.startAuthentication(n,s),options:n}}catch(i){return console.error("error starting authentication",i),null}})}startPasskeyAuthentication(t){return M(this,arguments,void 0,function*(s,o=!1){var r,n;const i=yield this.getPasskeyCredential(s,o);if(!i||!i.cred){console.error("no credential found");return}const{cred:a,options:d}=i;try{((r=(yield this.processLoginflowEvent({event:"signin_with_passkey",data:{credential_request:a}})).data)===null||r===void 0?void 0:r.error)==="CredentialNotFound"&&(d.rpId&&a.id?(n=this.webauthnService)===null||n===void 0||n.signalUnknownCredential(d.rpId,a.id):console.log("missing rpId or credential id for signalUnknownCredential"))}catch(u){console.error("failed to signin with passkey",u)}})}createPasskeyCredential(t,s){return M(this,void 0,void 0,function*(){var o,r;try{const n=yield(o=this.webauthnService)===null||o===void 0?void 0:o.fetchPublicKeyCredentialCreationOptions(t,s);if(!n){console.warn("no credential creation option returned by webauthn service"),console.log("skipping conditional passkey registration...");return}return(r=this.webauthnService)===null||r===void 0?void 0:r.startRegistration(n,s)}catch(n){console.log("error",n)}})}registerPasskey(t){return M(this,arguments,void 0,function*(s,o=!1){try{const r=yield this.createPasskeyCredential(s,o),n=yield this.processLoginflowEvent({event:"register_passkey",data:{credential_creation_response:r,conditional:o}});return console.log("passkey creation res",n),n}catch(r){console.log("error",r)}})}signOut(){this.ensureConfigured(),ct.Instance.clearSession(),ct.Instance.cleanup(),this.loginflow&&(this.loginflow.cleanup(),this.loginflow=null),this.navigationManager&&(this.navigationManager.destroy(),this.navigationManager=null),this.flowEventBus&&(this.flowEventBus.clear(),this.flowEventBus=null),window.location.reload(),console.log("Refreshed the page to clear any in-memory state")}}const Ft=Qe.Instance;/**
3
3
  * @license
4
4
  * Copyright 2019 Google LLC
5
5
  * SPDX-License-Identifier: BSD-3-Clause