@passflow/core 0.17.0 → 0.19.0
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.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1021 -716
- package/dist/index.mjs.map +1 -1
- package/dist/lib/api/auth.d.ts +3 -1
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/model.d.ts +50 -0
- package/dist/lib/api/model.d.ts.map +1 -1
- package/dist/lib/api/two-factor.d.ts +16 -7
- package/dist/lib/api/two-factor.d.ts.map +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/passflow.d.ts +49 -4
- package/dist/lib/passflow.d.ts.map +1 -1
- package/dist/lib/pkce.d.ts +13 -0
- package/dist/lib/pkce.d.ts.map +1 -0
- package/dist/lib/services/auth-service.d.ts +56 -1
- package/dist/lib/services/auth-service.d.ts.map +1 -1
- package/dist/lib/services/two-factor-service.d.ts +40 -7
- package/dist/lib/services/two-factor-service.d.ts.map +1 -1
- package/dist/lib/store.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../lib/constants/index.ts","../lib/token/membership.ts","../lib/token/service.ts","../lib/token/token.ts","../lib/token/delivery-manager.ts","../lib/storage/index.ts","../lib/device/index.ts","../lib/platform/types.ts","../lib/platform/web-adapter.ts","../lib/upgrade/acr.ts","../lib/upgrade/challenge.ts","../lib/upgrade/markers.ts","../lib/api/model.ts","../lib/api/axios-client.ts","../lib/api/app.ts","../lib/api/auth.ts","../lib/api/cli-auth.ts","../lib/api/invitation.ts","../lib/api/setting.ts","../lib/api/tenant.ts","../lib/api/two-factor.ts","../lib/api/upgrade.ts","../lib/api/user.ts","../lib/store.ts","../lib/upgrade/errors.ts","../lib/upgrade/session.ts","../lib/utils/validation.ts","../lib/services/auth-service.ts","../lib/services/invitation-service.ts","../lib/services/logger.ts","../lib/services/tenant-user-membership.ts","../lib/services/tenant-service.ts","../lib/services/token-cache-service.ts","../lib/services/two-factor-service.ts","../lib/services/user-service.ts","../lib/upgrade/factors.ts","../lib/upgrade/select.ts","../lib/upgrade/controller.ts","../lib/upgrade/snapshot.ts","../lib/upgrade/transport.ts","../lib/upgrade/core.ts","../lib/upgrade/replay.ts","../lib/upgrade/axios-adapter.ts","../lib/upgrade/fetch-adapter.ts","../lib/upgrade/client.ts","../lib/interceptors/fetch-registry.ts","../lib/upgrade/facade.ts","../lib/passflow.ts","../lib/m2m/errors.ts","../lib/m2m/types.ts","../lib/m2m/client.ts"],"sourcesContent":["// @ts-ignore - package.json is outside the TypeScript project\nimport packageJson from '../../package.json';\n\nexport const APP_ID_HEADER_KEY = 'X-Passflow-Clientid';\nexport const AUTHORIZATION_HEADER_KEY = 'Authorization';\nexport const DEVICE_ID_HEADER_KEY = 'X-Passflow-DeviceId';\nexport const DEVICE_TYPE_HEADER_KEY = 'X-Passflow-DeviceType';\n\n/**\n * SDK version from package.json.\n * Useful for debugging and logging version information.\n */\nexport const SDK_VERSION = packageJson.version as string;\n\n/**\n * Minimal set of scopes for basic authentication.\n * Includes only essential scopes: user ID, token refresh, and OpenID Connect.\n * Use this for applications that need minimal permissions.\n */\nexport const MINIMAL_DEFAULT_SCOPES = ['id', 'offline', 'openid'];\n\n/**\n * Default scopes used by the SDK.\n * Includes comprehensive permissions: user ID, token refresh, tenant access, email, OIDC, and full tenant access.\n * Note: 'access:tenant:all' is a very permissive scope and may not be appropriate for all applications.\n * Consider using MINIMAL_DEFAULT_SCOPES or custom scopes for production applications.\n */\nexport const DEFAULT_SCOPES = ['id', 'offline', 'tenant', 'email', 'oidc', 'openid', 'access:tenant:all'];\n\nexport const PASSFLOW_CLOUD_URL = 'https://auth.passflow.cloud';\nexport const DEFAULT_GROUP_NAME = 'default';\n\n// Popup configuration\nexport const POPUP_WIDTH = 500;\nexport const POPUP_HEIGHT = 600;\nexport const POPUP_POLL_INTERVAL_MS = 100;\nexport const POPUP_TIMEOUT_MS = 60000; // 60 seconds\n\n// Token configuration\nexport const TOKEN_EXPIRY_BUFFER_SECONDS = 30; // Buffer time to consider token expired early\n\n// Validation constraints\nexport const USERNAME_MIN_LENGTH = 3;\nexport const USERNAME_MAX_LENGTH = 30;\nexport const ERROR_MESSAGE_MAX_LENGTH = 200;\n","export type Tenant = {\n id: string;\n name: string;\n};\n\nexport type TenantMembership = {\n tenant: Tenant;\n tenantRoles?: GroupMembership;\n groups?: GroupMembership[];\n};\n\nexport type Group = {\n id: string;\n name: string;\n};\n\nexport type GroupMembership = {\n group: Group;\n roles: string[];\n};\n\nexport type RawUserMembership = {\n [key: string]: {\n tenant_id: string;\n tenant_name: string;\n tenant_roles?: string[];\n root_group_id: string;\n groups: {\n [key: string]: string[];\n };\n group_names: { [key: string]: string };\n };\n};\n\nexport type UserMembership = {\n raw: RawUserMembership;\n tenants: TenantMembership[];\n};\n\nexport const parseMembership = (raw: RawUserMembership): UserMembership => {\n const tenants: TenantMembership[] = [];\n let k: string;\n for (k in raw) {\n const v = raw[k];\n if (v === undefined) {\n continue;\n }\n const tnt: TenantMembership = { tenant: { id: v.tenant_id, name: v.tenant_name } };\n tnt.groups = v.groups\n ? Object.keys(v.groups).map((gk) => {\n const roles = v.groups[gk] || [];\n return { group: { id: gk, name: v.group_names?.[gk] ?? 'unknown' }, roles };\n })\n : [];\n tnt.tenantRoles = tnt.groups?.find((g) => g.group.id === v.root_group_id);\n tenants.push(tnt);\n }\n return { raw, tenants };\n};\n","/**\n * Token Service\n *\n * JWT token parsing, validation, and utility functions.\n * Provides platform-agnostic Base64 decoding for SSR/Node.js support.\n * Handles token expiry calculations with configurable buffer time.\n *\n * @module token\n */\n\nimport { TOKEN_EXPIRY_BUFFER_SECONDS } from '../constants';\nimport { StorageManager } from '../storage';\n\nimport { parseMembership } from './membership';\nimport { Token, TokenType } from './token';\n\n/**\n * Decodes a Base64-encoded string in a platform-agnostic way.\n * Works in both browser (using atob) and Node.js (using Buffer) environments.\n *\n * @param base64 - The Base64 string to decode\n * @returns Decoded string\n */\nfunction decodeBase64(base64: string): string {\n // Browser environment\n if (typeof window !== 'undefined' && typeof window.atob === 'function') {\n return window.atob(base64);\n }\n\n // Node.js environment\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(base64, 'base64').toString('utf-8');\n }\n\n throw new Error('No Base64 decoding method available in this environment');\n}\n\nexport class TokenService {\n protected storageManager: StorageManager;\n\n constructor(storageManager: StorageManager) {\n this.storageManager = storageManager;\n }\n\n /**\n * Checks if a token is not exists or expired.\n *\n * @param {TokenType} ttype - The token type to check.\n * @returns {boolean} Returns true if the token is expired or not exists, false otherwise.\n */\n isTokenTypeExpired(ttype: TokenType): boolean {\n const tokenString = this.storageManager.getToken(ttype);\n if (!tokenString) return true;\n\n const token = parseToken(tokenString);\n return token ? isTokenExpired(token) : true;\n }\n\n /**\n * Parse token from storage by type.\n * Please be aware that this method does not check if the token signature and if the token is valid.\n *\n * @param {TokenType} tokenType - The token type to check.\n * @returns {Token | undefined} Returns token with parsed user membership or undefined.\n */\n parseTokenType(tokenType: TokenType): Token | undefined {\n const token = this.storageManager.getToken(tokenType);\n if (!token) return undefined;\n return parseToken(token);\n }\n}\n\n/**\n * Checks if a token is expired.\n *\n * @param {Token} token - The token to check.\n * @param {number} bufferSeconds - Time buffer in seconds to consider token expired early\n * This prevents race conditions where token expires during request\n * @returns {boolean} Returns true if the token is expired or will expire within buffer time, false otherwise.\n */\nexport function isTokenExpired(token: Token, bufferSeconds = TOKEN_EXPIRY_BUFFER_SECONDS): boolean {\n const currentUnixTime = Math.floor(Date.now() / 1000);\n return currentUnixTime + bufferSeconds > token.exp;\n}\n\n/**\n * Parse token from string. Please be aware that this method does not check if the token signature and if the token is valid.\n *\n * @param {string} tokenString - The token string representation.\n * @returns {Token } Returns token with parsed user membership or undefined.\n */\nexport function parseToken(tokenString: string): Token {\n const base64Url = tokenString.split('.')[1];\n\n if (!base64Url) throw new Error('Invalid token string');\n\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if necessary (some JWTs don't include padding)\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n\n // Use the platform-agnostic decoder\n const decoded = decodeBase64(padded);\n\n const jsonPayload = decodeURIComponent(\n decoded\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n\n const parsedToken = JSON.parse(jsonPayload) as Token;\n parsedToken.membership =\n parsedToken.passflow_tm && parsedToken.type !== 'invite' ? parseMembership(parsedToken.passflow_tm) : undefined;\n return parsedToken;\n}\n","import { RawUserMembership, UserMembership } from './membership';\n\nexport type Token = {\n aud: string[];\n exp: number;\n iat: number;\n iss: string;\n jti: string;\n sub: string;\n type: string;\n email?: string;\n phonenumber?: string;\n passflow_tm?: RawUserMembership;\n payload?: unknown;\n membership?: UserMembership;\n};\n\nexport type InvitationToken = Token & {\n email: string;\n inviter_id: string;\n inviter_name: string;\n redirect_url: string;\n tenant_name: string;\n};\n\n/**\n * Token types used in the Passflow SDK.\n *\n * Note: Some enum values intentionally differ from keys for API compatibility.\n * The values match what the Passflow API returns in token payloads.\n *\n * @example\n * // access_token key maps to 'access' value (API response format)\n * TokenType.access_token === 'access' // true\n */\nexport enum TokenType {\n /** ID token - contains user identity claims */\n id_token = 'id_token',\n /** Access token - for API authorization. Maps to 'access' in API responses */\n access_token = 'access',\n /** Refresh token - for obtaining new access tokens. Maps to 'refresh' in API responses */\n refresh_token = 'refresh',\n /** Invitation token - for accepting user invitations */\n invite_token = 'invite',\n /** Password reset token - for password reset flows */\n reset_token = 'reset',\n /** Web cookie token - for web-based session management */\n web_cookie = 'web-cookie',\n /** Management token - for administrative operations */\n management = 'management',\n /** Sign-in token - for authentication flows */\n signin = 'signin',\n /** Actor token - for impersonation and delegated access */\n actor = 'actor',\n /** Two-factor authentication challenge token - for 2FA flows */\n two_factor = '2fa',\n}\n","/**\n * Token Delivery Manager\n *\n * Manages token delivery mode state and session validity for the SDK.\n * Supports three delivery modes: JSON body, cookie, and mobile.\n * Persists delivery mode to storage for session continuity.\n *\n * @module token/delivery-manager\n */\n\nimport type { StorageManager } from '../storage';\n\nexport enum TokenDeliveryMode {\n JsonBody = 'json_body',\n Cookie = 'cookie',\n Mobile = 'mobile',\n /**\n * BFF (Backend-for-Frontend) mode.\n * Tokens are sent to BFF server which stores them in httpOnly cookies.\n * Only ID token is kept locally for user info display.\n */\n BFF = 'bff',\n}\n\nexport enum SessionState {\n Unknown = 'unknown', // Initial state, not yet determined\n Valid = 'valid', // Session is valid (cookie mode trust state)\n Invalid = 'invalid', // Received 401, session expired\n}\n\nexport class TokenDeliveryManager {\n private mode: TokenDeliveryMode = TokenDeliveryMode.JsonBody;\n private sessionState: SessionState = SessionState.Unknown;\n private isInitializedFlag = false;\n\n private readonly STORAGE_PREFIX = 'passflow_';\n private readonly DELIVERY_MODE_KEY = `${this.STORAGE_PREFIX}delivery_mode`;\n private readonly SESSION_STATE_KEY = `${this.STORAGE_PREFIX}session_state`;\n\n constructor(private storageManager: StorageManager) {\n this.loadPersistedMode();\n this.loadPersistedSessionState();\n }\n\n /**\n * Set the token delivery mode\n */\n setMode(mode: TokenDeliveryMode): void {\n this.mode = mode;\n this.isInitializedFlag = true;\n this.persistMode();\n }\n\n /**\n * Get the current token delivery mode\n */\n getMode(): TokenDeliveryMode {\n return this.mode;\n }\n\n /**\n * Check if currently in cookie mode\n */\n isCookieMode(): boolean {\n return this.mode === TokenDeliveryMode.Cookie;\n }\n\n /**\n * Check if currently in JSON body mode\n */\n isJsonMode(): boolean {\n return this.mode === TokenDeliveryMode.JsonBody;\n }\n\n /**\n * Check if currently in mobile mode\n */\n isMobileMode(): boolean {\n return this.mode === TokenDeliveryMode.Mobile;\n }\n\n /**\n * Check if currently in BFF mode\n */\n isBFFMode(): boolean {\n return this.mode === TokenDeliveryMode.BFF;\n }\n\n /**\n * Check if delivery mode has been initialized from a server response\n */\n isInitialized(): boolean {\n return this.isInitializedFlag;\n }\n\n /**\n * Mark session as valid (successful authentication or token refresh)\n */\n setSessionValid(): void {\n this.sessionState = SessionState.Valid;\n this.persistSessionState();\n }\n\n /**\n * Mark session as invalid (received 401 or logout)\n */\n setSessionInvalid(): void {\n this.sessionState = SessionState.Invalid;\n this.persistSessionState();\n }\n\n /**\n * Reset session state to unknown (used during authentication flows)\n */\n setSessionUnknown(): void {\n this.sessionState = SessionState.Unknown;\n this.persistSessionState();\n }\n\n /**\n * Check if session is valid\n */\n isSessionValid(): boolean {\n return this.sessionState === SessionState.Valid;\n }\n\n /**\n * Check if session state is unknown (not yet determined)\n */\n isSessionUnknown(): boolean {\n return this.sessionState === SessionState.Unknown;\n }\n\n /**\n * Check if session is invalid\n */\n isSessionInvalid(): boolean {\n return this.sessionState === SessionState.Invalid;\n }\n\n /**\n * Get current session state\n */\n getSessionState(): SessionState {\n return this.sessionState;\n }\n\n /**\n * Reset delivery manager to initial state\n */\n reset(): void {\n this.mode = TokenDeliveryMode.JsonBody;\n this.sessionState = SessionState.Unknown;\n this.isInitializedFlag = false;\n this.clearPersistedMode();\n this.clearPersistedSessionState();\n }\n\n /**\n * Load persisted delivery mode from storage\n */\n private loadPersistedMode(): void {\n try {\n const persistedMode = this.storageManager['storage'].getItem(this.DELIVERY_MODE_KEY);\n if (persistedMode) {\n // Validate that the persisted mode is a valid enum value\n if (Object.values(TokenDeliveryMode).includes(persistedMode as TokenDeliveryMode)) {\n this.mode = persistedMode as TokenDeliveryMode;\n this.isInitializedFlag = true;\n }\n }\n } catch (_error) {\n // Silently ignore storage errors during load\n }\n }\n\n /**\n * Load persisted session state from storage\n */\n private loadPersistedSessionState(): void {\n try {\n const persistedState = this.storageManager['storage'].getItem(this.SESSION_STATE_KEY);\n if (persistedState) {\n // Validate that the persisted state is a valid enum value\n if (Object.values(SessionState).includes(persistedState as SessionState)) {\n this.sessionState = persistedState as SessionState;\n }\n }\n } catch (_error) {\n // Silently ignore storage errors during load\n }\n }\n\n /**\n * Persist delivery mode to storage\n */\n private persistMode(): void {\n try {\n this.storageManager['storage'].setItem(this.DELIVERY_MODE_KEY, this.mode);\n } catch (_error) {\n // Silently ignore storage errors during persist\n }\n }\n\n /**\n * Persist session state to storage\n */\n private persistSessionState(): void {\n try {\n this.storageManager['storage'].setItem(this.SESSION_STATE_KEY, this.sessionState);\n } catch (_error) {\n // Silently ignore storage errors during persist\n }\n }\n\n /**\n * Clear persisted delivery mode from storage\n */\n private clearPersistedMode(): void {\n try {\n this.storageManager['storage'].removeItem(this.DELIVERY_MODE_KEY);\n } catch (_error) {\n // Silently ignore storage errors during clear\n }\n }\n\n /**\n * Clear persisted session state from storage\n */\n private clearPersistedSessionState(): void {\n try {\n this.storageManager['storage'].removeItem(this.SESSION_STATE_KEY);\n } catch (_error) {\n // Silently ignore storage errors during clear\n }\n }\n}\n","/**\n * Storage Manager\n *\n * Abstraction layer over browser localStorage for token and data persistence.\n * Supports custom storage implementations for testing and alternative platforms.\n * Handles key prefixing for multi-app scenarios.\n *\n * @module storage\n */\n\nimport { TokenType } from '../token';\nimport { TokenDeliveryMode, Tokens } from '../types';\n\nexport type Storage = {\n setItem: (key: string, value: string) => void;\n getItem: (key: string) => string | null;\n removeItem: (key: string) => void;\n};\n\nexport interface StorageManagerParams {\n storage?: Storage;\n prefix?: string;\n}\n\nexport class StorageManager {\n private keyStoragePrefix = '';\n readonly scopes = `${this.keyStoragePrefix}tokens_scopes`;\n readonly deviceId = `${this.keyStoragePrefix}passflowDeviceId`;\n readonly invitationToken = `${this.keyStoragePrefix}passflowInvitationToken`;\n readonly previousRedirectUrl = `${this.keyStoragePrefix}passflowPreviousRedirectUrl`;\n\n // Namespaced keys for cookie mode support\n private readonly STORAGE_PREFIX = 'passflow_';\n private readonly ID_TOKEN_KEY = `${this.STORAGE_PREFIX}id_token`;\n private readonly CSRF_TOKEN_KEY = `${this.STORAGE_PREFIX}csrf_token`;\n private readonly DELIVERY_MODE_KEY = `${this.STORAGE_PREFIX}delivery_mode`;\n\n private storage: Storage;\n\n constructor({ storage, prefix }: StorageManagerParams = {}) {\n this.storage = storage ?? localStorage;\n this.keyStoragePrefix = prefix ? `${prefix}_` : '';\n }\n\n /**\n * Save tokens to storage with conditional logic based on delivery mode\n * In cookie/BFF mode: ONLY save ID token (not access/refresh tokens)\n * In JSON mode: save all tokens (existing behavior)\n */\n saveTokens(tokens: Tokens, deliveryMode?: TokenDeliveryMode): void {\n const { id_token, access_token, refresh_token, scopes } = tokens;\n\n if (deliveryMode === TokenDeliveryMode.Cookie || deliveryMode === TokenDeliveryMode.BFF) {\n // Cookie/BFF mode: ONLY save ID token (access/refresh in HttpOnly cookies)\n if (id_token) {\n this.storage.setItem(this.ID_TOKEN_KEY, id_token);\n }\n // Do NOT save access_token or refresh_token in localStorage\n } else {\n // JSON mode: save all tokens (existing behavior)\n if (id_token) this.storage.setItem(this.getKeyForTokenType(TokenType.id_token), id_token);\n if (access_token) this.storage.setItem(this.getKeyForTokenType(TokenType.access_token), access_token);\n if (refresh_token) this.storage.setItem(this.getKeyForTokenType(TokenType.refresh_token), refresh_token);\n if (scopes) this.storage.setItem(this.scopes, scopes.join(','));\n }\n }\n\n getToken(tokenType: TokenType): string | undefined {\n const key = this.getKeyForTokenType(tokenType);\n return this.storage.getItem(key) ?? undefined;\n }\n\n /**\n * Get tokens from storage with conditional logic based on delivery mode\n * In cookie/BFF mode: return ID token only (access/refresh in HttpOnly cookies)\n * In JSON mode: return all stored tokens (existing behavior)\n */\n getTokens(): Tokens | undefined {\n const mode = this.getDeliveryMode();\n\n if (mode === TokenDeliveryMode.Cookie || mode === TokenDeliveryMode.BFF) {\n // Cookie/BFF mode: return ID token only (access/refresh in HttpOnly cookies)\n const idToken = this.storage.getItem(this.ID_TOKEN_KEY);\n if (!idToken) return undefined;\n return {\n id_token: idToken,\n // access_token and refresh_token are in HttpOnly cookies, not localStorage\n };\n }\n\n // JSON mode: return all stored tokens (existing behavior)\n const access = this.storage.getItem(this.getKeyForTokenType(TokenType.access_token));\n if (!access) return undefined;\n return {\n access_token: access,\n id_token: this.storage.getItem(this.getKeyForTokenType(TokenType.id_token)) ?? undefined,\n refresh_token: this.storage.getItem(this.getKeyForTokenType(TokenType.refresh_token)) ?? undefined,\n scopes: this.storage.getItem(this.scopes)?.split(',') ?? undefined,\n };\n }\n\n getScopes(): string[] | undefined {\n return this.storage.getItem(this.scopes)?.split(',') ?? undefined;\n }\n\n /**\n * Check if JSON mode tokens exist in storage (ignores delivery mode)\n * Used to detect stale state where delivery_mode is set but JSON tokens exist\n */\n hasJsonModeTokens(): boolean {\n const accessToken = this.storage.getItem(this.getKeyForTokenType(TokenType.access_token));\n return !!accessToken;\n }\n\n /**\n * Check if cookie mode ID token exists in storage\n * Used to detect legitimate cookie/BFF mode sessions\n */\n hasCookieModeIdToken(): boolean {\n const idToken = this.storage.getItem(this.ID_TOKEN_KEY);\n return !!idToken;\n }\n\n deleteToken(tokenType: TokenType): void {\n const key = this.getKeyForTokenType(tokenType);\n this.storage.removeItem(key);\n }\n\n deleteTokens(): void {\n // Clear JSON mode tokens\n this.storage.removeItem(this.getKeyForTokenType(TokenType.id_token));\n this.storage.removeItem(this.getKeyForTokenType(TokenType.access_token));\n this.storage.removeItem(this.getKeyForTokenType(TokenType.refresh_token));\n this.storage.removeItem(this.scopes);\n\n // Clear cookie mode ID token\n this.clearIdToken();\n\n // Clear delivery mode and CSRF token to ensure clean state for next session\n this.clearDeliveryMode();\n this.clearCsrfToken();\n }\n\n getDeviceId(): string | undefined {\n return this.storage.getItem(this.deviceId) ?? undefined;\n }\n\n setDeviceId(deviceId: string): void {\n this.storage.setItem(this.deviceId, deviceId);\n }\n\n deleteDeviceId(): void {\n this.storage.removeItem(this.deviceId);\n }\n\n setInvitationToken(token: string): void {\n this.storage.setItem(this.invitationToken, token);\n }\n\n getInvitationToken(): string | undefined {\n return this.storage.getItem(this.invitationToken) ?? undefined;\n }\n\n deleteInvitationToken(): void {\n this.storage.removeItem(this.invitationToken);\n }\n\n setPreviousRedirectUrl(url: string): void {\n this.storage.setItem(this.previousRedirectUrl, url);\n }\n\n getPreviousRedirectUrl(): string | undefined {\n return this.storage.getItem(this.previousRedirectUrl) ?? undefined;\n }\n\n deletePreviousRedirectUrl(): void {\n this.storage.removeItem(this.previousRedirectUrl);\n }\n\n // Delivery mode storage methods\n\n /**\n * Set the token delivery mode in storage\n */\n setDeliveryMode(mode: TokenDeliveryMode): void {\n try {\n this.storage.setItem(this.DELIVERY_MODE_KEY, mode);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n /**\n * Get the token delivery mode from storage\n */\n getDeliveryMode(): TokenDeliveryMode | undefined {\n try {\n const mode = this.storage.getItem(this.DELIVERY_MODE_KEY);\n if (mode && Object.values(TokenDeliveryMode).includes(mode as TokenDeliveryMode)) {\n return mode as TokenDeliveryMode;\n }\n } catch (_error) {\n // Silently ignore storage errors\n }\n return undefined;\n }\n\n /**\n * Clear the delivery mode from storage\n */\n clearDeliveryMode(): void {\n try {\n this.storage.removeItem(this.DELIVERY_MODE_KEY);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n // ID token storage methods (for cookie mode)\n\n /**\n * Get the ID token from storage (cookie mode)\n */\n getIdToken(): string | undefined {\n try {\n return this.storage.getItem(this.ID_TOKEN_KEY) ?? undefined;\n } catch (_error) {\n // Silently ignore storage errors\n return undefined;\n }\n }\n\n /**\n * Set the ID token in storage (cookie mode)\n */\n setIdToken(token: string): void {\n try {\n this.storage.setItem(this.ID_TOKEN_KEY, token);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n /**\n * Clear the ID token from storage\n */\n clearIdToken(): void {\n try {\n this.storage.removeItem(this.ID_TOKEN_KEY);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n // CSRF token storage methods\n\n /**\n * Get the CSRF token from storage\n */\n getCsrfToken(): string | undefined {\n try {\n return this.storage.getItem(this.CSRF_TOKEN_KEY) ?? undefined;\n } catch (_error) {\n // Silently ignore storage errors\n return undefined;\n }\n }\n\n /**\n * Set the CSRF token in storage\n */\n setCsrfToken(token: string): void {\n try {\n this.storage.setItem(this.CSRF_TOKEN_KEY, token);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n /**\n * Clear the CSRF token from storage\n */\n clearCsrfToken(): void {\n try {\n this.storage.removeItem(this.CSRF_TOKEN_KEY);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n private getKeyForTokenType(tokenType: TokenType): string {\n return `${this.keyStoragePrefix}${tokenType}`;\n }\n}\n","/**\n * Device Service\n *\n * Manages device identification for security and tracking purposes.\n * Generates and persists unique device IDs using UUID v4.\n * Used for device-based authentication and session management.\n *\n * @module device\n */\n\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { StorageManager } from '../storage';\n\nexport class DeviceService {\n private storageManager: StorageManager;\n\n constructor(storageManager?: StorageManager) {\n this.storageManager = storageManager ?? new StorageManager();\n }\n\n getDeviceId(): string {\n const deviceId = this.storageManager.getDeviceId();\n if (!deviceId) {\n const newDeviceId = this.generateUniqueDeviceId();\n this.storageManager.setDeviceId(newDeviceId);\n return newDeviceId;\n }\n return deviceId;\n }\n\n generateUniqueDeviceId(): string {\n return uuidv4();\n }\n}\n","import type {\n AuthenticationResponseJSON,\n PublicKeyCredentialCreationOptionsJSON,\n PublicKeyCredentialRequestOptionsJSON,\n RegistrationResponseJSON,\n} from '@simplewebauthn/types';\n\nimport type { Storage } from '../storage';\n\n// ----------------------------------------------------------------\n// Storage\n// ----------------------------------------------------------------\n\n/**\n * Synchronous key-value storage interface.\n * Compatible with localStorage (web) and react-native-mmkv (RN).\n * Alias of the Storage type in lib/storage/index.ts to maintain a single source of truth.\n */\nexport type PlatformStorage = Storage;\n\n// ----------------------------------------------------------------\n// Navigation\n// ----------------------------------------------------------------\n\n/**\n * Result returned by getCurrentUrl().\n * Provides structured access to URL components so adapters don't\n * need to parse a URL string themselves.\n */\nexport type CurrentUrlInfo = {\n /** Full URL string, e.g. \"https://app.com/callback?access_token=abc#hash\" */\n href: string;\n /** Origin, e.g. \"https://app.com\" */\n origin: string;\n /** Pathname, e.g. \"/callback\" */\n pathname: string;\n /** Query string including \"?\", e.g. \"?access_token=abc\" */\n search: string;\n /** Hash including \"#\", e.g. \"#access_token=abc\" */\n hash: string;\n /** Hostname without port, e.g. \"app.com\" */\n hostname: string;\n};\n\n// ----------------------------------------------------------------\n// Auth UI\n// ----------------------------------------------------------------\n\n/**\n * Options passed to openAuthUrl(). Adapters can use these to decide\n * whether to open a popup, redirect, or use a deep link.\n */\nexport type OpenAuthUrlOptions = {\n /**\n * Preferred presentation mode. Adapters may ignore this and use\n * whatever makes sense for the platform.\n * - \"popup\": Open in a popup window (web default)\n * - \"redirect\": Full-page redirect (web fallback)\n * - \"deeplink\": Open with a deep link (React Native)\n */\n mode?: 'popup' | 'redirect' | 'deeplink';\n /** Popup width hint (web only) */\n width?: number;\n /** Popup height hint (web only) */\n height?: number;\n /** Origin to watch for the popup to return to (web popup mode only) */\n expectedReturnOrigin?: string;\n};\n\n/**\n * Result returned when the auth flow completes.\n * The adapter is responsible for detecting when auth is done and\n * returning the tokens from the callback URL.\n */\nexport type AuthCallbackResult = {\n /** Raw query string from the callback URL, e.g. \"?access_token=abc\" */\n callbackSearch: string;\n /** Raw hash from the callback URL, e.g. \"#access_token=abc\" */\n callbackHash: string;\n};\n\n// ----------------------------------------------------------------\n// Passkeys (optional)\n// ----------------------------------------------------------------\n\n/**\n * Optional passkey support. Adapters that do not support passkeys\n * return undefined from these methods. The calling code must check\n * for undefined before using the result.\n */\nexport type PasskeySupport = {\n /**\n * Start a passkey registration ceremony.\n * @returns Registration response, or undefined if not supported.\n */\n startRegistration(options: PublicKeyCredentialCreationOptionsJSON): Promise<RegistrationResponseJSON | undefined>;\n\n /**\n * Start a passkey authentication ceremony.\n * @returns Authentication response, or undefined if not supported.\n */\n startAuthentication(options: PublicKeyCredentialRequestOptionsJSON): Promise<AuthenticationResponseJSON | undefined>;\n};\n\n// ----------------------------------------------------------------\n// Errors\n// ----------------------------------------------------------------\n\n/**\n * Error thrown when the authentication popup is closed by the user\n * before authentication completes. Use `instanceof PopupClosedError`\n * for type-safe error discrimination instead of string matching.\n */\nexport class PopupClosedError extends Error {\n constructor(message = 'Authentication popup was closed') {\n super(message);\n this.name = 'PopupClosedError';\n }\n}\n\n// ----------------------------------------------------------------\n// Main Interface\n// ----------------------------------------------------------------\n\n/**\n * PlatformAdapter abstracts all platform-specific APIs that the\n * Passflow SDK needs. Implement this interface to add support for\n * a new platform (e.g. React Native).\n *\n * All methods have sensible defaults in WebAdapter. You only need\n * to implement what your platform supports.\n */\nexport interface PlatformAdapter {\n // ------ Storage ------\n\n /**\n * Synchronous key-value storage for tokens and SDK state.\n * Must be synchronous. Use localStorage on web, MMKV on React Native.\n */\n readonly storage: PlatformStorage;\n\n // ------ Navigation ------\n\n /**\n * Returns the current URL of the application.\n * On web: wraps window.location.\n * On React Native: implement using your routing library (e.g. Linking.getInitialURL).\n *\n * May return null in environments where URL is not meaningful (e.g. during SSR).\n */\n getCurrentUrl(): CurrentUrlInfo | null;\n\n /**\n * Navigates to the given URL, replacing the current history entry.\n * On web: window.history.replaceState(...)\n * On React Native: no-op or use your router's replace method.\n */\n replaceUrl(url: string): void;\n\n /**\n * Navigates to the given URL, performing a full navigation.\n * On web: window.location.href = url\n * On React Native: use Linking.openURL or your router.\n */\n navigateTo(url: string): void;\n\n // ------ Lifecycle ------\n\n /**\n * Register a callback invoked when the app comes to the foreground\n * (page becomes visible / app resumes).\n * Returns an unsubscribe function.\n *\n * On web: document.addEventListener('visibilitychange', ...)\n * On React Native: AppState.addEventListener('change', ...)\n */\n onForeground(callback: () => void): () => void;\n\n /**\n * Register a callback invoked when the app goes to the background\n * (page hidden / app suspended).\n * Returns an unsubscribe function.\n *\n * On web: document.addEventListener('visibilitychange', ...)\n * On React Native: AppState.addEventListener('change', ...)\n *\n * NOTE: This method is intentionally not called by the SDK internally.\n * It is part of the interface to support React Native adapters which need\n * to perform cleanup (e.g., flush queues) when the app is backgrounded.\n * Web adapters may implement it as a no-op or use visibilitychange.\n */\n onBackground(callback: () => void): () => void;\n\n /**\n * Register a callback invoked just before the page/app is closed.\n * Returns an unsubscribe function.\n *\n * On web: window.addEventListener('beforeunload', ...)\n * On React Native: implement as a no-op (RN handles cleanup differently).\n */\n onBeforeUnload(callback: () => void): () => void;\n\n /**\n * Returns true if the app is currently in the foreground (page visible).\n * On web: !document.hidden\n * On React Native: AppState.currentState === 'active'\n */\n isInForeground(): boolean;\n\n // ------ Auth UI ------\n\n /**\n * Opens a URL for federated/OAuth authentication.\n *\n * Web implementation:\n * - \"popup\" mode: Opens a popup via window.open(), polls for the callback,\n * resolves when the popup returns to expectedReturnOrigin.\n * - \"redirect\" mode: Full-page redirect (window.location.href = url).\n * Returns a Promise that never resolves (page navigates away).\n *\n * React Native implementation:\n * - Opens the URL via Linking.openURL() with a deep link scheme.\n * - Resolves when the deep link callback is received.\n *\n * @returns Promise that resolves with the callback URL tokens,\n * or rejects if auth fails or is cancelled.\n * In redirect mode on web, the promise may never resolve.\n */\n openAuthUrl(url: string, options?: OpenAuthUrlOptions): Promise<AuthCallbackResult>;\n\n // ------ Passkeys ------\n\n /**\n * Optional passkey support. If the platform does not support passkeys,\n * this property is undefined. The SDK checks for undefined before calling.\n */\n readonly passkeys?: PasskeySupport;\n\n // ------ Base64 ------\n\n /**\n * Encodes a string to Base64.\n * On web: btoa(str)\n * On React Native: use base64-js or Buffer.\n */\n btoa(str: string): string;\n\n /**\n * Decodes a Base64 string.\n * On web: atob(str) or Buffer fallback for SSR.\n * On React Native: use base64-js or Buffer.\n */\n atob(str: string): string;\n\n // ------ Cookies ------\n\n /**\n * Returns true if the platform supports HTTP cookies.\n * On web: true (with a quick write/read test).\n * On React Native: false. Cookie mode is not available.\n *\n * When this returns false, cookie token delivery mode must not be used.\n * The SDK will throw a descriptive error if cookie mode is configured\n * on a platform where cookies are not supported.\n */\n cookiesSupported(): boolean;\n\n // ------ Device ------\n\n /**\n * Returns the platform/device type.\n * Used to set the X-Device-Type header on API requests.\n */\n getDeviceType(): 'web' | 'ios' | 'android';\n}\n","import { startAuthentication, startRegistration } from '@simplewebauthn/browser';\nimport { POPUP_HEIGHT, POPUP_POLL_INTERVAL_MS, POPUP_TIMEOUT_MS, POPUP_WIDTH } from '../constants';\nimport {\n type AuthCallbackResult,\n type CurrentUrlInfo,\n type OpenAuthUrlOptions,\n type PasskeySupport,\n type PlatformAdapter,\n type PlatformStorage,\n PopupClosedError,\n} from './types';\n\nexport class WebAdapter implements PlatformAdapter {\n // ----------------------------------------------------------------\n // Storage\n // Replaces: StorageManager constructor default `storage ?? localStorage`\n // File: lib/storage/index.ts line 41\n // ----------------------------------------------------------------\n get storage(): PlatformStorage {\n if (typeof localStorage === 'undefined') {\n // SSR/Node environment: return a no-op storage that never throws\n return {\n getItem: () => null,\n // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op for SSR\n setItem: () => {},\n // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op for SSR\n removeItem: () => {},\n };\n }\n return localStorage;\n }\n\n // ----------------------------------------------------------------\n // Navigation\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * window.location.origin (passflow.ts:95, axios-client.ts:49)\n * window.location.search (passflow.ts:338,416, auth-service.ts:610,826)\n * window.location.hash (passflow.ts:342)\n * window.location.href (auth-service.ts:826)\n * window.location.pathname (passflow.ts:429,441,443)\n * window?.location?.hostname (user-service.ts:57)\n */\n getCurrentUrl(): CurrentUrlInfo | null {\n if (typeof window === 'undefined') return null;\n return {\n href: window.location.href,\n origin: window.location.origin,\n pathname: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n hostname: window.location.hostname,\n };\n }\n\n /**\n * Replaces:\n * window.history.replaceState({}, document.title, ...)\n * (passflow.ts:429, 441, 443)\n */\n replaceUrl(url: string): void {\n if (typeof window === 'undefined') return;\n window.history.replaceState({}, document.title, url);\n }\n\n /**\n * Replaces:\n * window.location.href = url\n * (auth-service.ts:795, 813, 845, passflow.ts indirectly)\n */\n navigateTo(url: string): void {\n if (typeof window === 'undefined') return;\n window.location.href = url;\n }\n\n // ----------------------------------------------------------------\n // Lifecycle\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * document.addEventListener('visibilitychange', handler) where !document.hidden\n * (token-cache-service.ts:134-145)\n */\n onForeground(callback: () => void): () => void {\n if (typeof document === 'undefined')\n return () => {\n /* no-op in SSR */\n };\n const handler = () => {\n if (!document.hidden) callback();\n };\n document.addEventListener('visibilitychange', handler);\n return () => document.removeEventListener('visibilitychange', handler);\n }\n\n /**\n * Replaces:\n * document.addEventListener('visibilitychange', handler) where document.hidden\n * (token-cache-service.ts:134-145, complementary to onForeground)\n *\n * NOTE: Not currently called by the SDK. Included in the interface for\n * React Native adapter compatibility (future use).\n */\n onBackground(callback: () => void): () => void {\n if (typeof document === 'undefined')\n return () => {\n /* no-op in SSR */\n };\n const handler = () => {\n if (document.hidden) callback();\n };\n document.addEventListener('visibilitychange', handler);\n return () => document.removeEventListener('visibilitychange', handler);\n }\n\n /**\n * Replaces:\n * window.addEventListener('beforeunload', ...)\n * (token-cache-service.ts:151)\n */\n onBeforeUnload(callback: () => void): () => void {\n if (typeof window === 'undefined')\n return () => {\n /* no-op in SSR */\n };\n window.addEventListener('beforeunload', callback);\n return () => window.removeEventListener('beforeunload', callback);\n }\n\n /**\n * Replaces:\n * typeof document !== 'undefined' && document.hidden\n * (token-cache-service.ts:112)\n */\n isInForeground(): boolean {\n if (typeof document === 'undefined') return true;\n return !document.hidden;\n }\n\n // ----------------------------------------------------------------\n // Auth UI\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * window.open(passflowURL, '_blank', `width=...,height=...`)\n * + polling loop with popupWindow.location.href/search\n * + window.location.href = origin (post-auth redirect)\n * (auth-service.ts:741-805)\n *\n * window.location.href = passflowURL (redirect mode)\n * (auth-service.ts:813)\n */\n openAuthUrl(url: string, options: OpenAuthUrlOptions = {}): Promise<AuthCallbackResult> {\n const mode = options.mode ?? 'popup';\n\n if (mode === 'redirect') {\n // Full-page redirect — page will reload; promise never resolves\n window.location.href = url;\n return new Promise(() => {\n /* intentionally never resolves — page navigates away */\n });\n }\n\n if (mode === 'deeplink') {\n return Promise.reject(\n new Error(\n 'openAuthUrl: \"deeplink\" mode is not supported on web. ' +\n 'Use \"popup\" or \"redirect\" instead, or provide a native platform adapter.',\n ),\n );\n }\n\n // Popup mode (default)\n const width = options.width ?? POPUP_WIDTH;\n const height = options.height ?? POPUP_HEIGHT;\n const expectedReturnOrigin = options.expectedReturnOrigin ?? window.location.origin;\n\n const popupWindow = window.open(url, '_blank', `width=${width},height=${height}`);\n\n if (!popupWindow) {\n // Popup blocked — fall back to redirect\n window.location.href = url;\n return new Promise(() => {\n /* intentionally never resolves — falling back to redirect */\n });\n }\n\n return new Promise<AuthCallbackResult>((resolve, reject) => {\n const startTime = Date.now();\n\n const checkInterval = setInterval(() => {\n if (popupWindow.closed) {\n clearInterval(checkInterval);\n reject(new PopupClosedError());\n return;\n }\n\n if (Date.now() - startTime > POPUP_TIMEOUT_MS) {\n clearInterval(checkInterval);\n popupWindow.close();\n reject(new Error('Authentication popup timed out'));\n return;\n }\n\n try {\n if (popupWindow.location.href.startsWith(expectedReturnOrigin)) {\n const callbackSearch = popupWindow.location.search;\n const callbackHash = popupWindow.location.hash;\n clearInterval(checkInterval);\n popupWindow.close();\n resolve({ callbackSearch, callbackHash });\n }\n } catch {\n // Expected cross-origin error — continue polling\n }\n }, POPUP_POLL_INTERVAL_MS);\n });\n }\n\n // ----------------------------------------------------------------\n // Passkeys\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * startRegistration({ optionsJSON: publicKey }) from @simplewebauthn/browser\n * (auth-service.ts:645-646, user-service.ts:65)\n *\n * startAuthentication({ optionsJSON: publicKey }) from @simplewebauthn/browser\n * (auth-service.ts:681)\n */\n readonly passkeys: PasskeySupport = {\n startRegistration: (options) => startRegistration({ optionsJSON: options }),\n startAuthentication: (options) => startAuthentication({ optionsJSON: options }),\n };\n\n // ----------------------------------------------------------------\n // Base64\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * btoa(publicKey.user.id)\n * (auth-service.ts:644, user-service.ts:64)\n */\n btoa(str: string): string {\n return btoa(str);\n }\n\n /**\n * Replaces:\n * window.atob(base64) / Buffer.from(base64, 'base64').toString('utf-8')\n * (token/service.ts:26-27)\n */\n atob(str: string): string {\n if (typeof window !== 'undefined' && typeof window.atob === 'function') {\n return window.atob(str);\n }\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n throw new Error('No Base64 decoding method available in this environment');\n }\n\n // ----------------------------------------------------------------\n // Cookies\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * document.cookie write/read test\n * (axios-client.ts:221-223)\n */\n cookiesSupported(): boolean {\n if (typeof document === 'undefined') return false;\n try {\n document.cookie = 'passflow_test=1; SameSite=Lax';\n const enabled = document.cookie.indexOf('passflow_test=1') !== -1;\n document.cookie = 'passflow_test=; expires=Thu, 01 Jan 1970 00:00:00 UTC';\n return enabled;\n } catch {\n return false;\n }\n }\n\n // ----------------------------------------------------------------\n // Device\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * Hardcoded 'web' string in DEVICE_TYPE_HEADER_KEY header\n * (axios-client.ts:91)\n * OS.web enum value (auth-service.ts:244, 358, 637, 676, user-service.ts:55)\n */\n getDeviceType(): 'web' | 'ios' | 'android' {\n return 'web';\n }\n}\n\n/**\n * Singleton instance used as the default adapter.\n * Exported so consuming code can reference it without allocating a new instance.\n */\nexport const webAdapter = new WebAdapter();\n","/**\n * The one implementation of \"what level does this `acr_values` string demand\".\n *\n * `challenge.ts` and `select.ts` both import it, so there is exactly one answer\n * in this package (§13.2.1b). It is consistent with the Go model *where the Go\n * model parses a single value* (`Level(\"2\") === 2`) and deliberately diverges\n * where Go parses a list as a scalar: `token_acr.go`'s `Level()` is\n * `strconv.Atoi` over the whole string, so `Level(\"2 3\")` is 0 — the weakest\n * possible reading of the stronger demand, which inverts channel precedence in\n * exactly the third-party interop case the precedence rule exists for.\n *\n * ## The lexical contract\n *\n * `acr_values` is a space-delimited list of values the server will accept (OIDC\n * Core §3.1.2.1, RFC 9470 §3), and satisfying ANY listed value suffices. So the\n * bar to clear is the list's MINIMUM participating level — the weakest value\n * that satisfies the server, which is by definition what it is demanding.\n *\n * A token PARTICIPATES iff the whole token is decimal digits (`/^[0-9]+$/`).\n * `\"02\"` participates as 2, matching `strconv.Atoi`. Everything else does not:\n * `\"2gold\"`, `\"gold\"`, `\"2.0\"`, `\"-1\"`, `\"+2\"`, `\"1e2\"`, `\"urn:example:gold\"`,\n * the empty token. Two deliberate narrowings:\n *\n * - **Whole-token only.** `Number.parseInt(\"2gold\", 10)` reads 2, which would\n * contradict the rule that opaque values do not participate — and would let\n * a hostile or buggy channel smuggle a bar out of a value nobody meant as a\n * level.\n * - **Negatives excluded**, even though `strconv.Atoi` accepts them. A\n * negative assurance level has no meaning in the Passflow scale or in OIDC,\n * and admitting one would hand an attacker a bar below every real level.\n *\n * Every non-negative integer is in domain. Passflow's scale is 0..3, but a\n * third-party server may legally demand 4; the SDK never refuses a demand\n * client-side (§13.4) — it ranks factors for the highest bar it knows and lets\n * the server's verdict on the replay decide.\n *\n * ## \"unknown\" is not zero, and zero is not absent\n *\n * An opaque issuer-defined value (`urn:…`, `\"gold\"`) is legal OIDC and is NOT a\n * level at all: scoring it 0 would make it the weakest possible requirement, so\n * the other channel would always win and the anti-downgrade rule would itself\n * produce a downgrade. A literal `\"0\"`, by contrast, IS a participating level\n * that happens to demand nothing — OIDC defines `acr` 0 as authentication not\n * meeting even ISO/IEC 29115 level 1, so a server listing it is stating it\n * accepts unelevated authentication. Same net contribution (no constraint),\n * different diagnostics.\n */\n\n/** A token participates iff the WHOLE token is decimal digits. */\nconst PARTICIPATING_TOKEN = /^[0-9]+$/;\n\n/** What an `acr_values` list demands, plus what it discarded getting there. */\nexport type AcrReading = {\n /**\n * The bar this list demands: the minimum participating level, when that is a\n * real constraint. `undefined` means this list contributes no `acr`\n * constraint — it was absent, every token was non-participating, or the\n * minimum participating level was `0`.\n */\n level?: number;\n /** The raw list, verbatim, when one was present at all. */\n raw?: string;\n /** True when a list was present but no token participated (`\"urn:example:gold\"`). */\n unorderable: boolean;\n /** True when the minimum participating level was `0` — a demand of nothing. */\n zero: boolean;\n};\n\n/** The empty reading: no list, no constraint, nothing to report. */\nconst ABSENT: AcrReading = Object.freeze({ unorderable: false, zero: false });\n\n/**\n * Parses one `acr_values` token. Returns `undefined` for every value that does\n * not participate, which is the only distinction the comparator needs.\n */\nexport const parseAcrToken = (token: string): number | undefined =>\n PARTICIPATING_TOKEN.test(token) ? Number.parseInt(token, 10) : undefined;\n\n/**\n * Reduces an `acr_values` list to the bar it demands.\n *\n * The reducer is `min` over participating tokens; the cross-channel comparator\n * (`challenge.ts`) then takes the `max` over per-channel minima, which is the\n * monotonic lattice join and therefore can never resolve below a correctly\n * parsed channel.\n */\nexport const readAcrValues = (raw: string | undefined | null): AcrReading => {\n if (!raw) {\n return ABSENT;\n }\n\n const tokens = raw.split(/\\s+/).filter((token) => token !== '');\n if (tokens.length === 0) {\n return ABSENT;\n }\n\n const levels: number[] = [];\n for (const token of tokens) {\n const level = parseAcrToken(token);\n if (level !== undefined) {\n levels.push(level);\n }\n }\n\n if (levels.length === 0) {\n // A list of opaque values: unknown, not zero. It contributes no constraint\n // and must be reported, because only something that saw and discarded it\n // can say so.\n return { raw, unorderable: true, zero: false };\n }\n\n const bar = Math.min(...levels);\n // `acr_values=\"0\"` or `\"0 2\"`: the server accepts unelevated authentication,\n // so this list is identical in effect to an absent one. Reported rather than\n // left inert, so an accidental `\"0\"` is visible.\n return bar === 0 ? { raw, unorderable: false, zero: true } : { level: bar, raw, unorderable: false, zero: false };\n};\n\n/**\n * The level an `acr_values` list demands, or `undefined` for no constraint.\n *\n * The convenience form `select.ts` wants: it ranks factors and has no use for\n * the diagnostics.\n */\nexport const requiredAcrLevel = (raw: string | undefined | null): number | undefined => readAcrValues(raw).level;\n","/**\n * RFC 9470 step-up challenge parsing.\n *\n * A upgrade challenge is the server saying \"this session is valid, but not\n * strong enough\". It arrives in two shapes, both carrying the same\n * `WWW-Authenticate` vocabulary:\n *\n * - `401` from a protected resource (RFC 9470 §3), emitted by the\n * RequireAuthLevel / RequireFreshAuth middleware.\n * - `403` from `/auth/session/exchange`, which keeps its ADR-2 JSON body\n * (`{\"error\": \"upgrade_required\", ...}`) alongside the same header.\n *\n * Telling this apart from an ordinary `401` matters more than it looks: an\n * expired session and an insufficient one are the same status code with\n * opposite remedies. Clearing tokens on a upgrade challenge logs the user out\n * at the exact moment they were asked to prove *more*.\n */\n\nimport { type AcrReading, readAcrValues } from './acr';\n\n/** The error code RFC 9470 §3 defines for an insufficient authentication event. */\nexport const INSUFFICIENT_USER_AUTHENTICATION = 'insufficient_user_authentication';\n\n/** The ADR-2 error code `/auth/session/exchange` returns in its 403 body. */\nexport const UPGRADE_REQUIRED = 'upgrade_required';\n\n/**\n * What the server demands before it will serve the original request.\n *\n * Both fields are optional because the two middlewares challenge on different\n * axes: RequireAuthLevel names an `acrValues` and omits `maxAge`, while\n * RequireFreshAuth does the reverse. A challenge carrying neither is still\n * valid — it means \"re-prove a factor\" without further qualification.\n */\nexport type UpgradeChallenge = {\n /**\n * Required authentication context class (`acr`), e.g. `'2'`.\n *\n * After a two-channel merge this is the raw list of the channel that SET THE\n * BAR, with the header preferred on a numeric tie. A channel that contributed\n * no level never supplies it — showing `\"2gold\"` as the bar would\n * misrepresent what the server demanded.\n */\n acrValues?: string;\n /**\n * The parsed bar: `max` over each channel's `min` participating level\n * (§13.2.1a). `undefined` means unknown or unconstrained on this axis.\n *\n * Optional, and absent from the single-channel parsers' output, so\n * `parseChallengeHeader` / `parseChallengeBody` keep the exact shape they\n * ship with. `readUpgradeChallenge` always sets it when a level was parsed.\n */\n requiredLevel?: number;\n /** Maximum permitted age of the authentication event, in seconds. */\n maxAge?: number;\n /** The status that carried the challenge: 401 (protected resource) or 403 (session exchange). */\n status: number;\n /**\n * Every channel that PARSED AS A CHALLENGE in any field — not only the\n * channels that won a comparison. An opaque header contributes no constraint\n * yet must still be counted, because only something that saw and discarded it\n * can report it. `length === 2` means \"both channels carried a challenge\".\n *\n * Set by `readUpgradeChallenge`; absent from the single-channel parsers.\n */\n channels?: ChallengeChannel[];\n};\n\n/** Which wire channel a challenge was read from. */\nexport type ChallengeChannel = 'header' | 'body';\n\n/**\n * Matches one `auth-param` (RFC 9110 §11.2): a token, `=`, then either a\n * quoted-string or a bare token. Quoted values are captured separately from\n * bare ones so an embedded comma (legal inside quotes) does not split a value.\n */\nconst AUTH_PARAM = /([A-Za-z0-9!#$%&'*+\\-.^_`|~]+)\\s*=\\s*(?:\"((?:[^\"\\\\]|\\\\.)*)\"|([^\\s,]*))/g;\n\n/** Unescapes a quoted-string body per RFC 9110 §5.6.4 (`\\X` → `X`). */\nconst unescapeQuoted = (value: string): string => value.replace(/\\\\(.)/g, '$1');\n\n/**\n * Parses the auth-params out of a `WWW-Authenticate` value.\n *\n * Scans params across the whole header rather than isolating a single\n * challenge. Passflow only ever emits one `Bearer` challenge, and scanning\n * broadly means an unexpected additional scheme degrades to \"we still found\n * the Bearer params\" instead of failing shut.\n */\nconst parseAuthParams = (header: string): Record<string, string> => {\n const params: Record<string, string> = {};\n // `matchAll` needs the regex reset; AUTH_PARAM is module-level and stateful.\n AUTH_PARAM.lastIndex = 0;\n for (const match of header.matchAll(AUTH_PARAM)) {\n const name = match[1];\n if (!name) {\n continue;\n }\n const quoted = match[2];\n const bare = match[3];\n params[name.toLowerCase()] = quoted !== undefined ? unescapeQuoted(quoted) : (bare ?? '');\n }\n return params;\n};\n\n/**\n * `max_age` has three outcomes, not two, so it gets a three-valued return.\n *\n * - **absent** — the field was not sent. Silent; it contributes nothing and\n * there is nothing to report.\n * - **invalid** — `<= 0`, or non-numeric. It contributes nothing AND raises\n * `'zero-constraint-ignored'`, because a literal `0` cannot be both \"absent\"\n * and \"invalid\" while the parser is required to tell them apart.\n * - **valid** — a positive number of seconds.\n *\n * Treating `0` as invalid is a deliberate narrowing of RFC 9470 §3, which\n * permits `max_age=0` as a maximally strict freshness demand. Two reasons: this\n * parser already accepted only values `> 0` before §13, so preserving zero\n * would be a behaviour change on a shipped parser rather than a gap in it; and\n * under \"smaller positive wins\" a literal `0` from a buggy serializer or a\n * hostile body would force re-proof on EVERY request — an unauthenticated\n * denial-of-service against the user's own session. Recorded as a known\n * deviation so a later reader does not \"fix\" it.\n */\nexport type MaxAgeReading = { kind: 'absent' } | { kind: 'invalid'; raw: unknown } | { kind: 'valid'; value: number };\n\n/** Reads `max_age` into its three outcomes. See `MaxAgeReading`. */\nexport const parseMaxAge = (raw: unknown): MaxAgeReading => {\n if (raw === undefined || raw === null || raw === '') {\n return { kind: 'absent' };\n }\n const n = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);\n return Number.isFinite(n) && n > 0 ? { kind: 'valid', value: n } : { kind: 'invalid', raw };\n};\n\n/** The valid-only view, for the two single-channel parsers whose shape is shipped. */\nconst maxAgeValue = (raw: unknown): number | undefined => {\n const reading = parseMaxAge(raw);\n return reading.kind === 'valid' ? reading.value : undefined;\n};\n\n/**\n * Reads a upgrade challenge out of a `WWW-Authenticate` header, or returns\n * `undefined` when the header is absent or describes some other failure.\n *\n * The header must both use the `Bearer` scheme and carry\n * `error=\"insufficient_user_authentication\"`. A bare `WWW-Authenticate: Bearer`\n * (what an expired token produces) is deliberately NOT a upgrade challenge.\n */\nexport const parseChallengeHeader = (header: string | undefined | null, status: number): UpgradeChallenge | undefined => {\n if (!header || !/(^|[\\s,])Bearer\\b/i.test(header)) {\n return undefined;\n }\n\n const params = parseAuthParams(header);\n if (params.error !== INSUFFICIENT_USER_AUTHENTICATION) {\n return undefined;\n }\n\n return {\n acrValues: params.acr_values || undefined,\n maxAge: maxAgeValue(params.max_age),\n status,\n };\n};\n\n/**\n * Reads a upgrade challenge out of the ADR-2 `403` body. Used as a fallback\n * for `/auth/session/exchange`, whose JSON body is the documented SDK contract\n * even though it also sets the standard header.\n */\nexport const parseChallengeBody = (body: unknown, status: number): UpgradeChallenge | undefined => {\n if (!body || typeof body !== 'object') {\n return undefined;\n }\n\n const data = body as Record<string, unknown>;\n if (data.error !== UPGRADE_REQUIRED) {\n return undefined;\n }\n\n return {\n acrValues: typeof data.acr_values === 'string' && data.acr_values ? data.acr_values : undefined,\n maxAge: maxAgeValue(data.max_age),\n status,\n };\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Both channels, merged (§13.2)\n *\n * The shipped parser chained header → body and returned the first hit: safe by\n * accident, not by design. Accepting two channels for a security-relevant value\n * creates a DOWNGRADE surface — from Passflow the two cannot disagree, both\n * being rendered from one `model.PolicyResult`, but a third-party resource\n * server, a header-rewriting intermediary or a CDN can make them disagree — so\n * first-match is withdrawn and replaced by STRONGEST-WINS:\n *\n * - a channel's bar is the MINIMUM participating level of its `acr_values`\n * list (`acr.ts`), and the winner is the LARGER bar: `max` over per-channel\n * `min`, which is the monotonic lattice join;\n * - `max_age` merges independently, smaller positive winning;\n * - absence and invalidity are the identity element: a value that contributes\n * nothing leaves the other channel's value standing, and a contributing\n * value can only tighten.\n *\n * Per-attribute composition is intended: the strongest `acr` from one channel\n * plus the smallest positive `max_age` from the other yields a requirement\n * neither channel stated in full, which is a hybrid stricter than either.\n * Tightening is the safe direction for a client-side hint whose enforcement is\n * server-side. The normal case is exactly this — `RequireAuthLevel` emits\n * `acr_values` and omits `max_age`, `RequireFreshAuth` the reverse.\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** The bar a challenge demanded, or an elevation reached, on both axes. */\nexport type AssuranceBar = {\n /** The raw winning `acr_values` list, for display and diagnostics. */\n acrValues?: string;\n /** The parsed bar. `undefined` = unknown, or not constrained on this axis. */\n level?: number;\n /** The freshness constraint, when that is what was demanded. */\n maxAge?: number;\n};\n\n/** The reasons the SDK can decline to act on a challenge, or act on a suspect one. */\nexport type UpgradeDiagnosticReason =\n | 'no-channel'\n | 'header-required-but-absent'\n | 'channels-disagree'\n | 'channels-unorderable'\n | 'zero-constraint-ignored'\n | 'challenge-out-of-scope'\n | 'elevated-insufficient'\n | 'replay-unsupported';\n\n/**\n * One diagnostic record.\n *\n * A diagnostic is the only observable signal of the response-rewriting and the\n * misconfiguration this design warns about, so it routes through a callback\n * whose DEFAULT sink is `console.warn` — never `debug`, which is invisible in\n * every default configuration. A deployment where step-up silently never fires\n * must be distinguishable from one where it was never required.\n */\nexport type UpgradeDiagnostic = {\n status: number;\n /**\n * The request's URL. The parser does not know it — it is handed a status, a\n * header reader and a body — so it emits `''` and the core, which owns the\n * request context, fills it in before forwarding to the sink.\n */\n url: string;\n reason: UpgradeDiagnosticReason;\n detail?: {\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n /** Raw channel inputs, including values discarded as unorderable. */\n header?: string;\n body?: string;\n /** Which field was discarded, for `'zero-constraint-ignored'`. */\n field?: 'acr_values' | 'max_age';\n /** Which operation, for per-waiter reasons. */\n method?: string;\n /** A human-readable sentence naming the fix. The default sink prints it. */\n message?: string;\n };\n};\n\n/** Where diagnostics go. `console.warn` is the default sink, not the only one. */\nexport type UpgradeDiagnosticSink = (diagnostic: UpgradeDiagnostic) => void;\n\n/** What `readUpgradeChallenge` is given. The CALLER decides whether reading the body is in scope. */\nexport type ChallengeInput = {\n status: number;\n getHeader: (name: string) => string | undefined | null;\n /** Pre-read. Omit it — do not pass `undefined` lazily — when the body must not be read. */\n body?: unknown;\n};\n\nexport type ChallengeReadOptions = {\n /**\n * `'header'` takes the challenge from the header ONLY: the body is not read\n * for challenge purposes at all, and a 401/403 with no parseable header is a\n * non-challenge. For an integrator whose compliance rule names RFC 9470, or\n * who does not consider a third-party server's body authoritative. Two\n * intended consequences: a strictly stronger body is ignored, and since no\n * body is resolved the disagreement diagnostic never fires.\n *\n * There is deliberately no `'body'` value. Excluding a third-party API's\n * standard header is what `shouldIntercept` is for, and a `'body'` setting\n * would mandate a non-standard channel, which §13.1a forbids.\n */\n requireChallengeChannel?: 'header';\n};\n\n/**\n * What a parse found, and what it had to discard finding it.\n *\n * A pure parser returning its findings beats a sink parameter: it is\n * side-effect-free and directly testable against a fixture corpus, and\n * `'channels-disagree'`, `'zero-constraint-ignored'` and\n * `'channels-unorderable'` are knowable ONLY inside the parse — the shipped\n * parser discarded them before returning, which is why they could not be\n * surfaced at all.\n */\nexport type ChallengeReading = {\n challenge?: UpgradeChallenge;\n /** `[]` when there is nothing to report. Never `undefined`. */\n diagnostics: UpgradeDiagnostic[];\n};\n\n/** Reads one channel's fields, keeping the raw values for the diagnostics. */\ntype ChannelReading = {\n channel: ChallengeChannel;\n acr: AcrReading;\n maxAge: MaxAgeReading;\n /** The raw `acr_values` list as it arrived, for `detail.header` / `detail.body`. */\n raw?: string;\n};\n\nconst readChannel = (channel: ChallengeChannel, challenge: UpgradeChallenge, rawMaxAge: unknown): ChannelReading => ({\n channel,\n acr: readAcrValues(challenge.acrValues),\n maxAge: parseMaxAge(rawMaxAge),\n raw: challenge.acrValues,\n});\n\n/** Picks the `acr_values` auth-param out of a header, without re-parsing semantics. */\nconst rawHeaderParams = (header: string | undefined | null): Record<string, string> => (header ? parseAuthParams(header) : {});\n\n/**\n * Reads a step-up challenge from both channels at once, merging them field-wise\n * and reporting everything it discarded.\n *\n * The additive sibling of `parseUpgradeChallenge`, which keeps its shipped\n * three-parameter signature and delegates here. It keeps the `getHeader`\n * callback form — the reason the function works from both the fetch surface and\n * the axios surface, where `AxiosError.response` hands back a plain object\n * rather than a DOM `Headers`.\n */\nexport const readUpgradeChallenge = (input: ChallengeInput, options?: ChallengeReadOptions): ChallengeReading => {\n const { status } = input;\n if (status !== 401 && status !== 403) {\n return { diagnostics: [] };\n }\n\n const headerOnly = options?.requireChallengeChannel === 'header';\n const rawHeader = input.getHeader('www-authenticate');\n const headerChallenge = parseChallengeHeader(rawHeader, status);\n // Under `'header'` the body is not read for challenge purposes AT ALL — not\n // read-then-ignored. The caller is expected not to have read it either.\n const bodyChallenge = headerOnly ? undefined : parseChallengeBody(input.body, status);\n\n const diagnostics: UpgradeDiagnostic[] = [];\n const report = (reason: UpgradeDiagnosticReason, detail?: UpgradeDiagnostic['detail']): void => {\n diagnostics.push({ status, url: '', reason, ...(detail ? { detail } : {}) });\n };\n\n if (!headerChallenge && !bodyChallenge) {\n if (headerOnly) {\n // The flag asserts `Access-Control-Expose-Headers: WWW-Authenticate` is in\n // place. When it is not, this is what says so — warn, never block.\n report('header-required-but-absent', rawHeader ? { header: rawHeader } : undefined);\n }\n return { diagnostics };\n }\n\n const channels: ChannelReading[] = [];\n if (headerChallenge) {\n channels.push(readChannel('header', headerChallenge, rawHeaderParams(rawHeader).max_age));\n }\n if (bodyChallenge) {\n const body = input.body as Record<string, unknown> | undefined;\n channels.push(readChannel('body', bodyChallenge, body?.max_age));\n }\n\n const rawOf = (channel: ChallengeChannel): 'header' | 'body' => channel;\n\n for (const reading of channels) {\n if (reading.acr.unorderable) {\n // An opaque issuer-defined ACR is legal OIDC and contributes nothing. The\n // discarded value travels in the detail, which is what that field is for.\n report('channels-unorderable', { [rawOf(reading.channel)]: reading.raw });\n }\n if (reading.acr.zero) {\n report('zero-constraint-ignored', { field: 'acr_values', [rawOf(reading.channel)]: reading.raw });\n }\n if (reading.maxAge.kind === 'invalid') {\n report('zero-constraint-ignored', { field: 'max_age', [rawOf(reading.channel)]: String(reading.maxAge.raw) });\n }\n }\n\n // ── the `acr` axis: max over per-channel min, header preferred on a tie ──\n const contributing = channels.filter((reading) => reading.acr.level !== undefined);\n let requiredLevel: number | undefined;\n let acrValues: string | undefined;\n for (const reading of contributing) {\n const level = reading.acr.level as number;\n if (requiredLevel === undefined || level > requiredLevel) {\n requiredLevel = level;\n acrValues = reading.raw;\n }\n // On a numeric tie the HEADER's raw string is preserved: it is the standards\n // channel and the one a server operator can be held to. The parsed level is\n // identical either way, so the choice is only about which string a developer\n // sees in a diagnostic.\n else if (level === requiredLevel && reading.channel === 'header') {\n acrValues = reading.raw;\n }\n }\n\n // ── the freshness axis: smaller positive wins, independently of `acr` ──\n let maxAge: number | undefined;\n for (const reading of channels) {\n if (reading.maxAge.kind === 'valid' && (maxAge === undefined || reading.maxAge.value < maxAge)) {\n maxAge = reading.maxAge.value;\n }\n }\n\n // A disagreement is compared FIELD-WISE, so two channels constraining\n // different axes — the normal case — is never reported as one.\n if (channels.length === 2) {\n const [first, second] = channels as [ChannelReading, ChannelReading];\n const acrDisagrees =\n first.acr.level !== undefined && second.acr.level !== undefined && first.acr.level !== second.acr.level;\n const freshnessDisagrees =\n first.maxAge.kind === 'valid' && second.maxAge.kind === 'valid' && first.maxAge.value !== second.maxAge.value;\n if (acrDisagrees || freshnessDisagrees) {\n report('channels-disagree', { header: first.raw ?? second.raw, body: second.raw ?? first.raw });\n }\n }\n\n return {\n challenge: {\n acrValues,\n requiredLevel,\n maxAge,\n status,\n channels: channels.map((reading) => reading.channel),\n },\n diagnostics,\n };\n};\n\n/**\n * Resolves a upgrade challenge from a response, from either channel.\n *\n * The SHIPPED export: same name, same three parameters, same return, published\n * in `@passflow/core@0.14.1`. It is now a thin delegate over\n * `readUpgradeChallenge`, so there is one merge rule rather than two parsers\n * that can drift — but it discards the diagnostics, which is why a caller that\n * needs them (the core) calls the sibling directly.\n *\n * Returns `undefined` for every response that is not a upgrade challenge —\n * including ordinary `401`s, which callers must keep treating as \"session\n * expired\".\n */\nexport const parseUpgradeChallenge = (\n status: number,\n getHeader: (name: string) => string | undefined | null,\n body?: unknown,\n): UpgradeChallenge | undefined => readUpgradeChallenge({ status, getHeader, body }).challenge;\n","/**\n * Per-request markers for the upgrade transaction.\n *\n * Two 401s look identical on the wire and mean opposite things. A protected\n * resource's 401 says \"this session is not strong enough\"; the elevation's own\n * `POST /auth/refresh` 401 says \"that TOTP code was wrong, try again\". Neither\n * carries RFC 9470 vocabulary on the commit path — `refreshErrorStatus` maps the\n * four recoverable upgrade errors to a bare 401 with no `WWW-Authenticate` and no\n * `upgrade_required` body — so the response alone cannot tell them apart from an\n * expired session.\n *\n * The evidence that CAN tell them apart is the request: we know we issued it.\n * Both rounds of the elevation (`POST /auth/upgrade/start` and the committing\n * `POST /auth/refresh`) carry a marker on their axios config, set at the call\n * site immediately before dispatch. The response interceptor reads it off\n * `error.config`, which axios hands back unchanged, and suppresses\n * `setSessionInvalid()` for that one request only. An unrelated background 401\n * arriving while a prompt is open is unmarked and invalidates exactly as before.\n *\n * A second marker records that a request has already been replayed after an\n * elevation, so an adapter that re-issues through the same interceptor cannot\n * elevate twice for one challenge.\n *\n * ## Why these are enumerable, against the design's \"non-enumerable\"\n *\n * The design asks for non-enumerable properties, reasoning that they must never\n * reach the wire. Non-enumerable does not survive the trip: `AxiosClient.send`\n * spreads the caller's config into a fresh object (`{ method, url, ...options }`)\n * and axios' own `mergeConfig` iterates `Object.keys`, so both drop a\n * non-enumerable property before the request is dispatched — verified against\n * `axios@1.13` with a stub adapter. An enumerable key reaches both the adapter\n * and `error.config`.\n *\n * The property the design actually wanted is preserved regardless: axios\n * serializes only `data` (and `params`/`headers`), never unknown config keys, so\n * a marker is never transmitted. The same mechanism already carries\n * `_retryCount` through the rate-limit retry path.\n */\n\n/** Config key marking a request as one of the upgrade transaction's two rounds. */\nexport const UPGRADE_TRANSACTION_MARKER = '__passflowUpgradeTransaction';\n\n/** Config key marking a request as the single post-elevation replay (FR3). */\nexport const UPGRADE_REPLAYED_MARKER = '__passflowUpgradeReplayed';\n\n/** The markers as they appear on a request config. Both are absent unless set. */\nexport type UpgradeRequestMarkers = {\n /** Set by the upgrade transport on `prepare` and `commit`. */\n __passflowUpgradeTransaction?: true;\n /** Set by the replaying adapter before it re-issues the original request. */\n __passflowUpgradeReplayed?: true;\n};\n\nconst readMarker = (config: unknown, key: string): boolean =>\n typeof config === 'object' && config !== null && (config as Record<string, unknown>)[key] === true;\n\n/**\n * Returns a copy of `config` marked as part of the upgrade transaction.\n *\n * Copies rather than mutates: the caller may be reusing a config object, and a\n * marker that outlives its request would suppress invalidation for an unrelated\n * 401 — the exact over-broad behaviour the per-request rule replaces.\n */\nexport const markUpgradeTransaction = <T extends object>(config?: T): T & UpgradeRequestMarkers => ({\n ...((config ?? {}) as T),\n [UPGRADE_TRANSACTION_MARKER]: true,\n});\n\n/** Reports whether a request config carries the upgrade-transaction marker. */\nexport const hasUpgradeTransactionMarker = (config: unknown): boolean => readMarker(config, UPGRADE_TRANSACTION_MARKER);\n\n/** Returns a copy of `config` marked as the one permitted post-elevation replay. */\nexport const markUpgradeReplayed = <T extends object>(config: T): T & UpgradeRequestMarkers => ({\n ...config,\n [UPGRADE_REPLAYED_MARKER]: true,\n});\n\n/** Reports whether a request config has already been replayed after an elevation. */\nexport const hasUpgradeReplayedMarker = (config: unknown): boolean => readMarker(config, UPGRADE_REPLAYED_MARKER);\n","import type {\n AuthenticationResponseJSON,\n PublicKeyCredentialCreationOptionsJSON,\n PublicKeyCredentialRequestOptionsJSON,\n RegistrationResponseJSON,\n} from '@simplewebauthn/types';\nimport type { AxiosRequestConfig } from 'axios';\n\nimport type { PlatformAdapter } from '../platform';\nimport type { Tokens } from '../types';\nimport type { UpgradeAssertion, UpgradeFactor } from '../upgrade/factors';\n\nexport type RequestOptions<D> = {\n data?: D;\n config?: AxiosRequestConfig;\n};\n\nexport enum RequestMethod {\n GET = 'get',\n POST = 'post',\n PUT = 'put',\n PATCH = 'patch',\n DELETE = 'delete',\n}\n\nexport enum PassflowEndpointPaths {\n signin = '/auth/login',\n signup = '/auth/register',\n signInWithProvider = '/auth/federated/start/',\n passwordless = '/auth/passwordless/start',\n passwordlessComplete = '/auth/passwordless/complete',\n logout = '/user/logout',\n refresh = '/auth/refresh',\n validateSession = '/user/me',\n sendPasswordResetEmail = '/auth/password/reset',\n resetPassword = '/auth/password/change',\n appSettings = '/app/settings',\n passkeyRegisterStart = '/auth/passkey/register/start',\n passkeyRegisterComplete = '/auth/passkey/register/complete',\n passkeyAuthenticateStart = '/auth/passkey/authenticate/start',\n passkeyAuthenticateComplete = '/auth/passkey/authenticate/complete',\n passkeyValidate = '/auth/validate',\n settingsAll = '/settings',\n settingsPasswordPolicy = '/settings/password',\n settingsPasskey = '/settings/passkey',\n userPasskey = '/user/passkey',\n addUserPasskey = `${PassflowEndpointPaths.userPasskey}/add/start`,\n completeAddUserPasskey = `${PassflowEndpointPaths.userPasskey}/add/complete`,\n joinInvitation = '/user/tenant/join',\n tenantPath = '/user/tenant',\n invitationsPath = '/user/tenant/:tenantID/invitations',\n requestInvitation = '/user/invite',\n invitationDelete = '/user/invite/:invitationID',\n invitationResend = '/user/invite/:invitationID/resend',\n invitationGetLink = '/user/invite/:invitationID/link',\n twoFactor = '/user/2fa',\n twoFactorStatus = '/user/2fa/status',\n twoFactorSetupBegin = '/user/2fa/setup/begin',\n twoFactorSetupConfirm = '/user/2fa/setup/confirm',\n twoFactorVerify = '/auth/2fa/verify',\n twoFactorRecovery = '/auth/2fa/recovery',\n twoFactorRegenerateCodes = '/user/2fa/recovery-codes/regenerate',\n twoFactorSetupMagicLink = '/auth/2fa-setup', // :token param appended in API call\n // v2 2FA endpoints\n TwoFactorMethodsAvailable = '/v2/user/2fa/methods/available',\n TwoFactorMethodsRegistered = '/v2/user/2fa/methods',\n TwoFactorSetupStart = '/v2/user/2fa/setup/start',\n TwoFactorSetupComplete = '/v2/user/2fa/setup/complete',\n TwoFactorMethodRemove = '/v2/user/2fa/methods/:id',\n TwoFactorChallenge = '/v2/auth/2fa/challenge',\n TwoFactorVerifyV2 = '/v2/auth/2fa/verify',\n TwoFactorAlternative = '/v2/auth/2fa/alternative',\n TwoFactorTrustedDevices = '/v2/user/2fa/trusted-devices',\n TwoFactorTrustedDeviceRevoke = '/v2/user/2fa/trusted-devices/:id',\n // CLI Auth endpoints\n cliAuthStatus = '/cli/auth/status/:sessionId',\n cliAuthComplete = '/cli/auth/complete',\n // Assurance upgrade (step-up) endpoints. The commit round reuses `refresh`.\n upgradeStart = '/auth/upgrade/start',\n}\n\nexport enum PassflowAdminEndpointPaths {\n passkeyRegisterStart = '/admin/auth/passkey/register/start',\n passkeyRegisterComplete = '/admin/auth/passkey/register/complete',\n passkeyAuthenticateStart = '/admin/auth/passkey/authenticate/start',\n passkeyAuthenticateComplete = '/admin/auth/passkey/authenticate/complete',\n passkeyValidate = '/admin/auth/validate',\n logout = '/admin/auth/logout',\n}\n\n/**\n * BFF (Backend-for-Frontend) configuration for secure token storage.\n * When enabled, tokens are sent to the BFF server which stores them in httpOnly cookies.\n */\nexport type TokenExchangeConfig = {\n /**\n * Enable token exchange mode. When true, authorization code is sent to your server\n * for token exchange instead of being exchanged directly from the browser.\n */\n enabled: boolean;\n /**\n * URL to send authorization code for server-side token exchange.\n * Your server exchanges the code for tokens and stores them in httpOnly cookies.\n * @example '/api/auth/callback'\n */\n callbackUrl: string;\n /**\n * URL to call for token refresh. BFF reads refresh_token from httpOnly cookie\n * and returns new tokens (also stored in cookies).\n * @example '/api/auth/refresh'\n */\n refreshUrl?: string;\n /**\n * URL to call for logout. BFF clears httpOnly cookies.\n * @example '/api/auth/logout'\n */\n logoutUrl?: string;\n /**\n * URL to check authentication status (whether httpOnly cookies are valid).\n * @example '/api/auth/status'\n */\n statusUrl?: string;\n};\n\n/** @deprecated Use TokenExchangeConfig instead */\nexport type BFFConfig = TokenExchangeConfig;\n\nexport type PassflowConfig = {\n url?: string;\n appId?: string;\n scopes?: string[];\n createTenantForNewUser?: boolean;\n parseQueryParams?: boolean;\n keyStoragePrefix?: string;\n /**\n * Token exchange configuration for secure server-side token handling.\n * When enabled, authorization code is sent to your server which exchanges it\n * for tokens and stores them in httpOnly cookies. Tokens never touch the browser.\n */\n tokenExchange?: TokenExchangeConfig;\n /**\n * Platform adapter for cross-platform compatibility.\n * Defaults to WebAdapter (browser APIs) if not provided.\n * Pass a custom adapter to support React Native or other platforms.\n */\n platform?: PlatformAdapter;\n /**\n * Assurance upgrade (step-up) configuration. Construction-time only: the\n * prompt handler is deliberately NOT here, because it is a lifetime-scoped\n * resource that has to be torn down, which configuration cannot express.\n */\n upgrade?: PassflowUpgradeConfig;\n};\n\nexport type PassflowUpgradeConfig = {\n /** Relying-party id forwarded verbatim to `POST /auth/upgrade/start` for passkey upgrades. */\n relyingPartyId?: string;\n /** Overrides the factor set offered during an upgrade. Defaults to what this SDK can complete. */\n factors?: readonly UpgradeFactor[];\n};\n\n/**\n * Passflow's own API, as an interception scope — \"domain 1\".\n *\n * A challenge arriving from here is handled with zero configuration, because\n * `url` is an input the SDK already takes and whatever it points at is\n * unambiguously ours.\n */\nexport type UpgradeDomain = {\n /** The API origin, e.g. `https://api.passflow.dev`. */\n origin: string;\n /** The path prefix when `url` carries one, normalised without a trailing slash. */\n pathPrefix?: string;\n};\n\n/**\n * Resolves domain 1 from `PassflowConfig.url`.\n *\n * `url` is OPTIONAL, and the absent case is specified rather than guessed: the\n * domain is EMPTY (`null`), and nothing throws. Throwing would break apps that\n * never enabled step-up, and substituting a default origin — `PASSFLOW_CLOUD_URL`,\n * the document origin, the origin the first 401 came from — would be the SDK\n * inferring a trust boundary it has no information about. An empty domain is not\n * silent: the first parseable challenge on an attached surface must raise a\n * diagnostic naming `url` as the fix.\n *\n * Note this deliberately does NOT mirror `AxiosClient`'s `url || PASSFLOW_CLOUD_URL`\n * fallback. That fallback decides where to send our own traffic; this decides\n * whose 401s may drive a security prompt, and the two questions do not share an\n * answer.\n */\nexport const resolveUpgradeDomain = (url?: string): UpgradeDomain | null => {\n if (!url) {\n return null;\n }\n try {\n const parsed = new URL(url);\n const prefix = parsed.pathname.replace(/\\/+$/, '');\n return { origin: parsed.origin, pathPrefix: prefix === '' ? undefined : prefix };\n } catch {\n // A `url` the platform's own URL parser rejects cannot be matched against a\n // response URL either. Same treatment as absent: empty, and never a throw.\n return null;\n }\n};\n\n/** The raw `POST /auth/upgrade/start` body, snake_case exactly as the server sends it. */\nexport type UpgradeStartWire = {\n challenge_id: string;\n method: string;\n /** RFC 3339. The server enforces the TTL; the SDK carries this for display only. */\n expires_at: string;\n /** WebAuthn assertion options, present only for a passkey upgrade. */\n webauthn?: PublicKeyCredentialRequestOptionsJSON;\n /** Masked delivery target (`j***@x.com`), present only for email / SMS OTP. */\n code_sent_to?: string;\n};\n\n/**\n * The `upgrade` field of `POST /auth/refresh` — the commit round.\n *\n * The server types `assertion` as `json.RawMessage`, so a new factor can extend\n * the payload without a wire-format change.\n */\nexport type UpgradeRefreshField = {\n type: string;\n assertion: UpgradeAssertion;\n};\n\nexport type PassflowAuthorizationResponse = Tokens & {\n requires_2fa?: boolean;\n challenge_id?: string;\n tfa_token?: string;\n token_delivery?: 'json_body' | 'cookie' | 'mobile';\n cookies?: string[];\n csrf_token?: string;\n};\n\nexport type PassflowValidationResponse = Tokens & {\n redirect_url: string;\n};\n\nexport type PassflowSuccessResponse = {\n result: 'ok';\n};\n\nexport type PassflowLogoutResponse = {\n status: 'ok';\n};\n\nexport interface PassflowSessionValidationResponse {\n valid: boolean;\n user?: {\n id: string;\n email?: string;\n username?: string;\n [key: string]: unknown;\n };\n expires_at?: number;\n}\n\nexport type PassflowResponseError = {\n error: {\n id: string;\n message: string;\n status: number;\n location: string;\n time: string;\n };\n};\n\nexport class PassflowError extends Error {\n id: string;\n message: string;\n status: number;\n location: string;\n time: string;\n\n constructor(error: PassflowResponseError['error']) {\n super();\n this.id = error?.id ?? 'unknown';\n this.message = error?.message ?? error ?? 'Something went wrong';\n this.status = error?.status ?? 500;\n this.location = error?.location ?? 'unknown';\n this.time = error?.time ?? new Date().toISOString();\n }\n}\n\nexport type PassflowSignInPayload = {\n password: string;\n scopes?: string[];\n invite_token?: string;\n} & (\n | { email: string; phone?: never; username?: never }\n | { phone: string; email?: never; username?: never }\n | { username: string; email?: never; phone?: never }\n);\n\nexport type PassflowSignInExtendedPayload = PassflowSignInPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowAddressPayload = {\n formatted?: string;\n street_address?: string;\n locality?: string;\n region?: string;\n postal_code?: string;\n country?: string;\n};\n\nexport type PassflowUserPayload = {\n password: string;\n username?: string;\n email?: string;\n given_name?: string;\n family_name?: string;\n middle_name?: string;\n nickname?: string;\n preferred_username?: string;\n phone_number?: string;\n profile?: string;\n picture?: string;\n website?: string;\n gender?: string;\n birthday?: Date;\n timezone?: string;\n locale?: string;\n addresses?: PassflowAddressPayload;\n} & ({ email: string } | { phone_number: string });\n\nexport type PassflowSignUpPayload = {\n user: PassflowUserPayload;\n scopes?: string[];\n create_tenant?: boolean;\n anonymous?: boolean;\n invite_token?: string;\n};\n\nexport type PassflowPasswordlessSignInPayload = {\n challenge_type: InternalStrategyChallenge;\n redirect_url: string;\n scopes?: string[];\n create_tenant?: boolean;\n invite_token?: string;\n} & ({ email: string; phone?: never } | { phone: string; email?: never });\n\nexport type PassflowPasswordlessResponse = {\n challenge_id: string;\n expires_at: Date | string;\n};\n\nexport type PassflowPasswordlessSignInExtendedPayload = PassflowPasswordlessSignInPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowPasswordlessSignInCompletePayload = {\n challenge_id: string;\n otp: string;\n device?: string;\n scopes?: string[];\n challenge_type?: InternalStrategyChallenge;\n};\n\nexport enum Providers {\n google = 'google',\n facebook = 'facebook',\n}\n\nexport type FimStrategy = {\n fim_type: Providers;\n};\n\nexport type InternalStrategyIdentity = 'id' | 'email' | 'phone' | 'username' | 'anonymous' | 'none';\nexport type InternalStrategyChallenge = 'password' | 'otp' | 'magic_link' | 'recovery_codes' | 'guardian' | 'none';\nexport type InternalStrategyTransport = 'email' | 'sms' | 'push' | 'socket' | 'authenticator' | 'none';\n\nexport type InternalStrategy = {\n identity: InternalStrategyIdentity;\n challenge: InternalStrategyChallenge;\n transport: InternalStrategyTransport;\n};\n\nexport type OtherStrategy = Record<string, never>;\n\nexport type AuthTypeStrategy = 'internal' | 'passkey' | 'webauthn' | 'fim' | 'pkce' | 'anonymous';\n\nexport type AuthStrategies =\n | { type: Extract<AuthTypeStrategy, 'internal'>; strategy: InternalStrategy }\n | { type: Extract<AuthTypeStrategy, 'fim'>; strategy: FimStrategy }\n | {\n type: Exclude<AuthTypeStrategy, 'internal' | 'fim'>;\n strategy: OtherStrategy;\n };\n\nexport type AppType = 'web' | 'spa' | 'bff' | 'android' | 'ios' | 'desktop' | 'm2m' | 'other';\n\nexport type TokenDeliveryMethod = 'json_body' | 'cookie' | '';\n\nexport type AppSettings = {\n id: string;\n secret: string;\n active: boolean;\n name: string;\n description: string;\n offline: boolean;\n type: AppType;\n redirect_urls: string[] | null;\n origins: string[] | null;\n custom_email_templates: boolean;\n auth_strategies: AuthStrategies[];\n force_passwordless_login: boolean;\n pkce_enabled: boolean;\n custom_sms_messages: boolean;\n registration_allowed: boolean;\n invite_only_registration: boolean;\n passwordless_registration_allowed: boolean;\n anonymous_registration_allowed: boolean;\n create_tenant_on_registration: 'never' | 'always' | 'optional';\n fim_merge_by_email_allowed: boolean;\n debug_otp_code_allowed: boolean;\n debug_otp_code_for_registration: string;\n defaults: DefaultAppSettings;\n login_app_theme: LoginWebAppTheme;\n login_app_settings?: unknown;\n token_delivery_method?: TokenDeliveryMethod;\n};\n\nexport type DefaultAppSettings = {\n app_id: string;\n redirect: string;\n scopes: string[];\n create_tenant_for_new_user: boolean;\n};\n\nexport enum OS {\n web = 'web',\n ios = 'ios',\n android = 'android',\n}\n\n/**\n * Maps platform.getDeviceType() return values to the OS enum.\n * The OS enum values match the getDeviceType() strings exactly,\n * so this is a safe cast.\n */\nexport function getOSFromDeviceType(deviceType: 'web' | 'ios' | 'android'): OS {\n // OS enum values happen to match getDeviceType() string values exactly\n return deviceType as OS;\n}\n\nexport type PassflowPasskeyRegisterStartPayload = {\n passkey_display_name?: string;\n passkey_username?: string;\n invite_token?: string;\n\n scopes: string[];\n create_tenant?: boolean;\n\n relying_party_id: string;\n redirect_url: string;\n};\n\nexport type PassflowPasskeyRegisterStartExtendedPayload = PassflowPasskeyRegisterStartPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowPasskeyStart = {\n challenge_id: string;\n publicKey: PublicKeyCredentialCreationOptionsJSON;\n};\n\nexport type PassflowPasskeyCompleteMessageWithTokens = Tokens;\n\nexport type PassflowPasskeyPayload = {\n device: string;\n challenge_id: string;\n};\n\nexport type PassflowPasskeyRegisterPayload = PassflowPasskeyPayload & {\n passkey_data: RegistrationResponseJSON;\n};\n\nexport type PassflowPasskeyAuthenticatePayload = PassflowPasskeyPayload & {\n passkey_data: AuthenticationResponseJSON;\n};\n\nexport type PassflowPasskeyAuthenticateStartPayload = {\n relying_party_id: string;\n scopes?: string[];\n user_id?: string;\n invite_token?: string;\n};\n\nexport type PassflowPasskeyAuthenticateStartExtendedPayload = PassflowPasskeyAuthenticateStartPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowFederatedAuthPayload = {\n provider: Providers;\n redirect_url: string;\n scopes?: string[];\n invite_token?: string;\n create_tenant?: boolean;\n};\n\nexport type PassflowFederatedAuthExtendedPayload = PassflowFederatedAuthPayload & {\n device?: string;\n};\n\nexport type PassflowValidatePayload = {\n otp: string;\n device: string;\n challenge_id: string;\n};\n\n// SETTINGS\nexport type PassflowPasskeyProviderOption = 'none' | 'required' | 'preferred' | 'discouraged';\n\nexport type PassflowSettingsAll = {\n password_policy: PassflowPasswordPolicySettings;\n passkey_provider: PassflowPasskeySettings;\n};\n\nexport type PassflowPasswordPolicySettings = {\n restrict_min_password_length: boolean;\n min_password_length: number;\n reject_compromised: boolean;\n enforce_password_strength: 'none' | 'weak' | 'average' | 'strong';\n require_lowercase: boolean;\n require_uppercase: boolean;\n require_number: boolean;\n require_symbol: boolean;\n};\n\nexport type PassflowPasskeySettings = {\n // name: string;\n id: string;\n display_name: string;\n // id_field: 'email' | 'phone' | 'username';\n // validation: InternalStrategyChallenge;\n registration?: {\n user_verification: PassflowPasskeyProviderOption;\n authenticator_attachment: 'platform' | 'cross-platform' | 'any';\n discoverable_key: PassflowPasskeyProviderOption;\n attestation_metadata: PassflowPasskeyProviderOption;\n extensions: unknown;\n };\n authentication?: {\n user_verification: PassflowPasskeyProviderOption;\n attestation_metadata: PassflowPasskeyProviderOption;\n extensions: unknown;\n };\n};\n\ntype PassflowCredentialFlags = {\n user_present: boolean;\n user_verified: boolean;\n backup_eligible: boolean;\n backup_state: boolean;\n};\n\ntype PassflowEnrolmentAuthenticator = {\n aaguid: string;\n sign_count: number;\n clone_warning: boolean;\n attachment: 'platform' | 'cross-platform';\n};\n\nexport type PassflowUserPasskey = {\n id: string;\n user_id: string;\n name: string;\n strategy: InternalStrategy;\n challenge_type: InternalStrategyChallenge;\n strategy_hash: string;\n enrolled_at: Date | string;\n enrollment_challenge_id: string;\n confirmed_at: Date | string;\n last_auth_at: Date | string;\n public_key: string;\n attestation_type: string;\n transport: string[];\n flags: PassflowCredentialFlags;\n authenticator: PassflowEnrolmentAuthenticator;\n archived: boolean;\n archived_at: Date | string;\n count?: number;\n enrolled_with_app_id?: string;\n};\n\nexport type PassflowSendPasswordResetEmailPayload = {\n reset_page_url?: string;\n redirect_url?: string;\n} & (\n | { email: string; phone?: never; username?: never }\n | { phone: string; email?: never; username?: never }\n | { username: string; email?: never; phone?: never }\n);\n\nexport type PassflowInviteResponse = {\n link: string;\n};\n\nexport type PassflowInvitePayload = {\n invite_token: string;\n scopes: string[];\n};\n\nexport type PassflowUserWithRoles = {\n user_id: string;\n username: string;\n email: string;\n phone_number: string;\n tenant_id: string;\n group_id: string;\n role_id: string;\n preferred_username?: string;\n given_name?: string;\n family_name?: string;\n nickname?: string;\n picture?: string;\n roles: {\n [role_id: string]: string; // Maps role_id to role_name\n };\n};\n\nexport type PassflowGroup = {\n id: string;\n name: string;\n default: boolean;\n updated_at: string;\n created_at: string;\n};\n\nexport type PassflowRole = {\n id: string;\n tenant_id: string;\n name: string;\n};\n\nexport type PassflowTenantResponse = {\n tenant_id: string;\n tenant_name: string;\n users_in_groups?: PassflowUserInGroup[];\n groups?: PassflowGroup[];\n roles?: PassflowRole[];\n};\n\n/**\n * Represents a user's membership in a group with their assigned roles\n */\nexport type PassflowUserInGroup = {\n user: {\n id: string;\n name?: string | null;\n email?: string | null;\n phone?: string | null;\n };\n group_id: string;\n roles?: {\n id: string;\n }[];\n};\n\nexport type PassflowCreateTenantPayload = {\n name: string;\n};\n\nexport type LoginWebAppTemplateType = 'default' | 'simple' | 'extendable';\n\nexport type LoginWebAppTemplateColorScheme = 'system' | 'light' | 'dark';\n\nexport type LoginWebAppStyle = {\n primary_color: string;\n text_color: string;\n secondary_text_color: string;\n background_color: string;\n card_color: string;\n input_background_color: string;\n input_border_color: string;\n button_text_color: string;\n divider_color: string;\n federated_button_background_color: string;\n federated_button_text_color: string;\n logo_url: string;\n passkey_button_background_color: string;\n passkey_button_text_color: string;\n background_image: string;\n custom_css: string;\n};\n\nexport type LoginWebAppTheme = {\n template_type: LoginWebAppTemplateType;\n application_name: string;\n remove_passflow_logo: boolean;\n description: string;\n color_scheme: LoginWebAppTemplateColorScheme;\n light_style: LoginWebAppStyle;\n dark_style: LoginWebAppStyle;\n};\n\nexport type PassflowCreateTokenResponse = PassflowTenantResponse;\n\n// Helper function to create paths with parameters\nexport function pathWithParams(template: string, params: Record<string, string>): string {\n let result = template;\n Object.entries(params).forEach(([key, value]) => {\n result = result.replace(`:${key}`, value);\n });\n return result;\n}\n\n// Usage example:\n// const invitationsUrl = pathWithParams(PassflowEndpointPaths.invitationsPath, { tenantID: '123' });\n\n// ============================================\n// Two-Factor Authentication Types\n// ============================================\n\n/**\n * Two-Factor authentication policy\n */\nexport enum TwoFactorPolicy {\n Disabled = 'disabled',\n Optional = 'optional',\n Required = 'required',\n}\n\n/**\n * Two-Factor error codes\n */\nexport type TwoFactorErrorCode =\n | 'INVALID_CODE'\n | 'CODE_EXPIRED'\n | 'TOO_MANY_ATTEMPTS'\n | 'SETUP_NOT_STARTED'\n | 'ALREADY_ENABLED'\n | 'NOT_ENABLED'\n | 'INVALID_RECOVERY_CODE'\n | 'NO_RECOVERY_CODES_REMAINING'\n | 'INVALID_CHALLENGE'\n | 'TIME_DRIFT_DETECTED';\n\n// Request Types\nexport type TwoFactorConfirmRequest = {\n code: string;\n};\n\nexport type TwoFactorVerifyRequest = {\n code: string;\n tfa_token: string;\n};\n\nexport type TwoFactorRecoveryRequest = {\n recovery_code: string;\n tfa_token: string;\n};\n\nexport type TwoFactorDisableRequest = {\n code: string;\n};\n\nexport type TwoFactorRegenerateRequest = {\n code: string;\n};\n\n// Response Types\nexport type TwoFactorStatusResponse = {\n enabled: boolean;\n policy: TwoFactorPolicy;\n recovery_codes_remaining: number;\n totp_digits?: 6 | 8; // Optional for backward compatibility, defaults to 6 if not provided\n};\n\nexport type TwoFactorSetupResponse = {\n secret: string;\n qr_code: string;\n totp_digits?: 6 | 8; // Optional for backward compatibility, defaults to 6 if not provided\n};\n\nexport type TwoFactorConfirmResponse = {\n success: true;\n recovery_codes: string[];\n};\n\nexport type TwoFactorVerifyResponse = Tokens & {\n success: true;\n};\n\nexport type TwoFactorRecoveryResponse = Tokens & {\n success: true;\n remaining_recovery_codes: number;\n};\n\nexport type TwoFactorDisableResponse = {\n success: true;\n};\n\nexport type TwoFactorRegenerateResponse = {\n success: true;\n recovery_codes: string[];\n};\n\n// ============================================\n// Two-Factor Magic Link Setup Types\n// ============================================\n\n/**\n * Two-Factor magic link error codes\n */\nexport type TwoFactorSetupMagicLinkErrorCode =\n | 'INVALID_TOKEN'\n | 'EXPIRED_TOKEN'\n | 'REVOKED_TOKEN'\n | 'RATE_LIMITED'\n | 'SERVER_ERROR';\n\n/**\n * Two-Factor magic link error details\n */\nexport type TwoFactorSetupMagicLinkError = {\n code: TwoFactorSetupMagicLinkErrorCode;\n message: string;\n retryAfter?: number; // Seconds until retry allowed (for RATE_LIMITED)\n};\n\n/**\n * Response from magic link validation endpoint\n * Backend returns scoped session token for 2FA setup operations\n */\nexport type TwoFactorSetupMagicLinkValidationResponse = {\n success: boolean;\n sessionToken?: string; // JWT with scope \"2fa_setup\"\n userId?: string; // Target user ID\n expiresIn?: number; // Session expiration in seconds (3600 = 1 hour)\n appId?: string | null; // Optional associated app ID\n error?: TwoFactorSetupMagicLinkError;\n};\n\n/**\n * Internal session state for magic link 2FA setup\n * Stored in memory only, not persisted across page reloads\n */\nexport type TwoFactorSetupMagicLinkSession = {\n sessionToken: string;\n userId: string;\n appId?: string | null;\n scope: '2fa_setup'; // Immutable scope\n timestamp: number;\n expiresAt: number;\n};\n\n// ============================================\n// Two-Factor v2 Multi-Method Types\n// ============================================\n\n/**\n * Two-Factor method types\n */\nexport type TwoFactorMethod = 'totp' | 'email_otp' | 'sms_otp' | 'passkey' | 'push_fcm' | 'push_webpush' | 'recovery_codes';\n\n/**\n * Challenge Request for v2 2FA flow\n */\nexport interface TwoFactorChallengeRequest {\n first_factor_method?: string;\n trust_device?: boolean;\n}\n\n/**\n * Challenge Response from v2 2FA flow\n */\nexport interface TwoFactorChallengeResponse {\n challenge_id: string;\n method: TwoFactorMethod;\n alternative_methods: TwoFactorMethod[];\n expires_at: string;\n code_sent_to?: string; // Masked email/phone for OTP\n webauthn_options?: unknown; // For passkey\n}\n\n/**\n * Verify Request for v2 2FA flow\n */\nexport interface TwoFactorVerifyRequestV2 {\n challenge_id: string;\n method: TwoFactorMethod;\n response: string; // OTP code or WebAuthn response\n trust_device?: boolean;\n}\n\n/**\n * Verify Response from v2 2FA flow\n */\nexport interface TwoFactorVerifyResponseV2 {\n success: boolean;\n access_token?: string;\n refresh_token?: string;\n device_trusted?: boolean;\n}\n\n/**\n * Registered Two-Factor Method\n */\nexport interface RegisteredTwoFactorMethod {\n id: string;\n method: TwoFactorMethod;\n name: string;\n created_at: string;\n last_used_at?: string;\n}\n\n/**\n * Alternative Method Request\n */\nexport interface TwoFactorAlternativeRequest {\n challenge_id: string;\n method: TwoFactorMethod;\n}\n\n// ============================================\n// CLI Authentication Types\n// ============================================\n\nexport interface CLIAuthStatusResponse {\n status: 'pending' | 'completed' | 'expired' | 'failed';\n expires_at?: string;\n challenge?: Record<string, unknown>; // WebAuthn challenge - raw PublicKeyCredentialRequestOptions\n access_token?: string;\n refresh_token?: string;\n user_id?: string;\n expires_in?: number;\n error?: string;\n}\n\nexport interface CLIAuthCompleteRequest {\n session_id: string;\n access_token: string;\n refresh_token: string;\n user_id: string;\n expires_in: number;\n}\n","import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';\nimport {\n APP_ID_HEADER_KEY,\n AUTHORIZATION_HEADER_KEY,\n DEVICE_ID_HEADER_KEY,\n DEVICE_TYPE_HEADER_KEY,\n PASSFLOW_CLOUD_URL,\n TOKEN_EXPIRY_BUFFER_SECONDS,\n} from '../constants';\n\nimport { DeviceService } from '../device';\nimport type { PlatformAdapter } from '../platform';\nimport { webAdapter } from '../platform';\nimport { StorageManager } from '../storage';\nimport { TokenService, isTokenExpired, parseToken } from '../token';\nimport { TokenDeliveryManager } from '../token/delivery-manager';\nimport type { UpgradeChallenge } from '../upgrade/challenge';\nimport { parseUpgradeChallenge } from '../upgrade/challenge';\nimport { hasUpgradeTransactionMarker } from '../upgrade/markers';\n\nimport {\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowError,\n PassflowResponseError,\n RequestMethod,\n RequestOptions,\n type UpgradeDomain,\n resolveUpgradeDomain,\n} from './model';\n\nexport enum HttpStatuses {\n badRequest = 400,\n unauthorized = 401,\n tooManyRequests = 429,\n internalServerError = 500,\n success = 200,\n created = 201,\n}\n\n// Rate limiting retry configuration\nconst MAX_RETRIES = 3;\nconst INITIAL_RETRY_DELAY_MS = 1000;\n\n/**\n * Reports whether a response is an RFC 9470 step-up challenge rather than a\n * plain authentication failure. Adapts the axios response shape onto the\n * transport-agnostic parser in `../upgrade/challenge`.\n */\nconst axiosHeaderReader =\n (response: AxiosResponse) =>\n (name: string): string | undefined => {\n const value = response.headers?.[name] ?? response.headers?.[name.toLowerCase()];\n return Array.isArray(value) ? value.join(', ') : (value as string | undefined);\n };\n\nconst readUpgradeChallengeFrom = (response: AxiosResponse): UpgradeChallenge | undefined =>\n parseUpgradeChallenge(response.status, axiosHeaderReader(response), response.data);\n\nconst isUpgradeChallengeResponse = (response: AxiosResponse): boolean => readUpgradeChallengeFrom(response) !== undefined;\n\n/**\n * Handles a challenge the Passflow instance itself received, resolving with a\n * replayed response once the session has been elevated.\n *\n * Consulted INSIDE the existing error interceptor rather than registered as a\n * later one: axios runs response interceptors FIFO and the handler below\n * converts the `AxiosError` into a `PassflowError`, which has no headers and no\n * config, so anything registered afterwards is structurally too late.\n */\nexport type AxiosUpgradeHandler = (error: AxiosError, challenge: UpgradeChallenge) => Promise<AxiosResponse>;\n\nexport class AxiosClient {\n private instance: AxiosInstance;\n protected storageManager: StorageManager;\n protected deviceService: DeviceService;\n protected tokenDeliveryManager: TokenDeliveryManager;\n private refreshPromise: Promise<AxiosResponse<PassflowAuthorizationResponse>> | null = null;\n private isRefreshing = false;\n private _platform: PlatformAdapter;\n\n tokenService: TokenService;\n\n get origin(): string {\n return this._platform.getCurrentUrl()?.origin ?? '';\n }\n url: string;\n appId?: string;\n\n protected defaultHeaders: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n };\n\n private readonly nonAccessTokenEndpoints = ['/auth/', '/settings', '/settings/'];\n /**\n * Endpoints under `/auth/` that DO need credentials.\n *\n * `'upgrade'` is here because `/auth/upgrade/start` is access-token-gated.\n * Without it the path matches `isNonAuthEndpoint()` and the request\n * interceptor returns before either the cookie-mode branch or the JSON-mode\n * token branch runs: no `Authorization`, no `withCredentials`, no CSRF token,\n * and a prepare round that 401s in every delivery mode.\n */\n private readonly protectedEndpoints = ['logout', 'refresh', 'upgrade'];\n\n /** Set when a consumer wires the upgrade feature. Absent by default. */\n private upgradeHandler?: AxiosUpgradeHandler;\n\n /**\n * Passflow's own API as an interception scope, resolved ONCE here from\n * `PassflowConfig.url` so no wrapper or adapter ever derives it. `null` when\n * no `url` was configured — see `resolveUpgradeDomain`.\n */\n readonly upgradeDomain: UpgradeDomain | null;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n const { url, appId, keyStoragePrefix } = config;\n this._platform = config.platform ?? webAdapter;\n\n this.url = url || PASSFLOW_CLOUD_URL;\n this.upgradeDomain = resolveUpgradeDomain(url);\n\n // Use provided instances or create new ones (backward compatibility)\n this.storageManager =\n storageManager ??\n new StorageManager({\n prefix: keyStoragePrefix ?? '',\n });\n this.deviceService = deviceService ?? new DeviceService(this.storageManager);\n this.tokenService = new TokenService(this.storageManager);\n this.tokenDeliveryManager = new TokenDeliveryManager(this.storageManager);\n\n if (appId) {\n this.appId = appId;\n\n this.defaultHeaders = {\n ...this.defaultHeaders,\n [APP_ID_HEADER_KEY]: appId,\n };\n }\n\n // Add device headers\n const deviceId = this.deviceService.getDeviceId();\n this.defaultHeaders = {\n ...this.defaultHeaders,\n [DEVICE_ID_HEADER_KEY]: deviceId,\n [DEVICE_TYPE_HEADER_KEY]: this._platform.getDeviceType(),\n };\n\n // Detect cookie capability\n this.detectCookieSupport();\n\n this.instance = axios.create({\n baseURL: this.url,\n headers: { ...this.defaultHeaders },\n });\n\n this.instance.interceptors.request.use(async (axiosConfig: InternalAxiosRequestConfig) => {\n // Request to non-access token endpoints\n if (this.isNonAuthEndpoint(axiosConfig.url)) {\n return axiosConfig;\n }\n\n // Cookie mode handling\n if (this.tokenDeliveryManager.isCookieMode()) {\n // Browser automatically sends HttpOnly cookies\n // Do NOT add Authorization header\n axiosConfig.withCredentials = true;\n\n // Add CSRF token if available\n const csrfToken = this.storageManager.getCsrfToken();\n if (csrfToken) {\n axiosConfig.headers['X-CSRF-Token'] = csrfToken;\n }\n\n return axiosConfig;\n }\n\n // JSON mode: existing token handling\n // Request to refresh token endpoint\n if (axiosConfig.url?.includes('refresh')) {\n if (this.isRefreshing) {\n // Abort duplicate refresh requests\n const controller = new AbortController();\n controller.abort();\n axiosConfig.signal = controller.signal;\n return axiosConfig;\n }\n return axiosConfig;\n }\n\n // Request to access token endpoints\n const tokens = this.storageManager.getTokens();\n\n if (tokens?.access_token) {\n const access = parseToken(tokens.access_token);\n\n // Check if token is expired with buffer\n if (isTokenExpired(access, TOKEN_EXPIRY_BUFFER_SECONDS) && tokens.refresh_token) {\n try {\n // Single-flight pattern: reuse in-flight refresh\n if (this.refreshPromise) {\n const response = await this.refreshPromise;\n // After refresh completes, get new token\n if (response?.data?.access_token) {\n axiosConfig.headers[AUTHORIZATION_HEADER_KEY] = `Bearer ${response.data.access_token}`;\n }\n return axiosConfig;\n }\n\n // Start new refresh using single-flight pattern\n this.refreshPromise = this.refreshTokens();\n\n try {\n const response = await this.refreshPromise;\n // After refresh completes, get new token\n if (response?.data?.access_token) {\n axiosConfig.headers[AUTHORIZATION_HEADER_KEY] = `Bearer ${response.data.access_token}`;\n }\n return axiosConfig;\n } finally {\n this.refreshPromise = null;\n }\n } catch (error) {\n // On failure, clear refresh state immediately so future requests can retry\n this.refreshPromise = null;\n this.isRefreshing = false;\n // Clear tokens on auth failure\n this.storageManager.deleteTokens();\n return Promise.reject(error);\n }\n }\n\n axiosConfig.headers[AUTHORIZATION_HEADER_KEY] = `Bearer ${tokens.access_token}`;\n\n return axiosConfig;\n }\n return axiosConfig;\n });\n\n this.instance.interceptors.response.use(\n (response: AxiosResponse) => response,\n async (e: AxiosError) => {\n // Mark the session invalid on a 401 — unless this particular request\n // carries positive evidence that the 401 is part of an elevation.\n //\n // Evidence, both per-request and both necessary:\n //\n // 1. the failed request is one of the upgrade transaction's two rounds\n // (`/auth/upgrade/start`, or the committing `/auth/refresh`). Those\n // 401s mean \"wrong code\" or \"spent challenge\" and carry NO RFC 9470\n // vocabulary at all, so the response cannot be used to recognise\n // them. Without this limb a mistyped TOTP digit logs the user out\n // mid-prompt, which is the defect this feature exists to fix.\n // 2. the response itself carried a parseable challenge, which covers a\n // challenge arriving outside any elevation.\n //\n // On neither, the 401 invalidates exactly as it always has. That matters\n // as much as the suppression: keying on the ABSENCE of challenge\n // vocabulary would mean an expired or revoked session never invalidates,\n // leaving the user in a broken \"authenticated\" UI — the mirror image of\n // the same bug. Scoping to the one request that failed also keeps a\n // genuine expiry on an unrelated background request invalidating\n // normally while a prompt is open.\n if (e.response?.status === HttpStatuses.unauthorized && !this.isUpgradeTransactionError(e)) {\n this.tokenDeliveryManager.setSessionInvalid();\n }\n\n // An elevation seam, consulted here because a later-registered\n // interceptor would only ever see the `PassflowError` below.\n if (e.response && this.upgradeHandler) {\n const challenge = readUpgradeChallengeFrom(e.response);\n if (challenge) {\n try {\n return await this.upgradeHandler(e, challenge);\n } catch (upgradeError) {\n // The core DECLINED to act — `not-a-challenge`, `out-of-scope` or\n // `replay-unsupported` all settle by re-rejecting the error we\n // handed in. Returning that rejection would hand an SDK caller a\n // raw `AxiosError` where every existing consumer pattern-matches\n // on `PassflowError.id`, and it would skip the rate-limit path\n // below as well. The identity comparison is the whole test: the\n // same object back means \"not mine\", anything else is a genuine\n // step-up outcome and belongs to the caller untouched.\n //\n // Reachable in a configuration, not just in theory: with no `url`\n // configured, domain 1 is `null`, so Passflow's own challenges are\n // all out of scope and every one of them took this path.\n if (upgradeError !== e) {\n throw upgradeError;\n }\n }\n }\n }\n\n // Handle rate limiting with retry logic\n if (e.response?.status === HttpStatuses.tooManyRequests) {\n return await this.handleRateLimitError(e);\n }\n return this.handleAxiosError(e);\n },\n );\n }\n\n /**\n * The `setSessionInvalid()` suppression predicate, keyed on the request that\n * failed and on the response that came back — never on \"this response does not\n * speak upgrade vocabulary\", which is unsound in both directions.\n */\n private isUpgradeTransactionError(e: AxiosError): boolean {\n return hasUpgradeTransactionMarker(e.config) || (e.response !== undefined && isUpgradeChallengeResponse(e.response));\n }\n\n /**\n * Installs (or clears) the handler consulted when a response carries an\n * upgrade challenge. Passing `undefined` detaches it.\n */\n setUpgradeInterceptor(handler: AxiosUpgradeHandler | undefined): void {\n this.upgradeHandler = handler;\n }\n\n /**\n * Re-issues a request after an elevation. Exposed for the upgrade handler,\n * which owns the replay-once decision; the marker preventing a second\n * elevation travels on the config the caller passes in.\n */\n replay<T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {\n return this.instance.request<T>(config);\n }\n\n private isProtectedEndpoint(url?: string): boolean {\n return this.protectedEndpoints.some((endpoint) => url?.includes(endpoint));\n }\n\n private isNonAuthEndpoint(url?: string): boolean {\n return this.nonAccessTokenEndpoints.some((endpoint) => url?.includes(endpoint)) && !this.isProtectedEndpoint(url);\n }\n\n /**\n * Detect if cookies are supported/enabled in the browser.\n * If cookie mode is already active (restored from storage) and cookies are not\n * supported, throws immediately to prevent silent misconfiguration.\n *\n * Note: If cookie mode is set later (e.g., server responds with token_delivery=cookie\n * on a non-cookie platform), this check will not catch it. The server should not\n * configure cookie mode for platforms where cookiesSupported() returns false.\n */\n private detectCookieSupport(): void {\n const supported = this._platform.cookiesSupported();\n\n // Enforce immediately if cookie mode was persisted from a previous session.\n if (!supported && this.tokenDeliveryManager.isCookieMode()) {\n throw new Error(\n 'Passflow SDK: Cookie delivery mode is configured but cookies are not ' +\n 'supported in this environment. Use a platform adapter that supports ' +\n 'cookies or configure the server to use JSON token delivery.',\n );\n }\n }\n\n /**\n * Refresh tokens using single-flight pattern to prevent race conditions\n * Supports both cookie mode and JSON mode\n */\n private async refreshTokens(): Promise<AxiosResponse<PassflowAuthorizationResponse>> {\n if (this.tokenDeliveryManager.isCookieMode()) {\n // Cookie mode: call /auth/refresh with credentials:'include'\n // Server reads refresh token from HttpOnly cookie\n const response = await this.instance.post<PassflowAuthorizationResponse>(\n PassflowEndpointPaths.refresh,\n {}, // Empty body\n { withCredentials: true },\n );\n\n // Mark session as valid after successful refresh\n this.tokenDeliveryManager.setSessionValid();\n\n // Extract CSRF token if present\n if (response.data.csrf_token) {\n this.storageManager.setCsrfToken(response.data.csrf_token);\n }\n\n // Save ID token if present (other tokens are in HttpOnly cookies)\n if (response.data.id_token) {\n this.storageManager.setIdToken(response.data.id_token);\n }\n\n return response;\n } else {\n // JSON mode: existing refresh logic\n const tokens = this.storageManager.getTokens();\n const scopes = this.storageManager.getScopes();\n\n if (!tokens?.refresh_token) {\n throw new Error('No refresh token available');\n }\n\n this.isRefreshing = true;\n const payload = {\n refresh_token: tokens.refresh_token,\n scopes,\n };\n\n const response = await this.instance.post<PassflowAuthorizationResponse>(PassflowEndpointPaths.refresh, payload, {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${tokens.refresh_token}`,\n },\n });\n\n if (response.data) {\n // Update storage BEFORE processing queued requests\n this.storageManager.saveTokens(response.data);\n }\n\n this.isRefreshing = false;\n\n return response;\n }\n }\n\n private async handleRateLimitError(e: AxiosError): Promise<AxiosResponse> {\n const config = e.config;\n if (!config) {\n return Promise.reject(e);\n }\n\n // Only retry idempotent requests to avoid duplicate operations\n const method = config.method?.toUpperCase();\n const isIdempotent = ['GET', 'HEAD', 'OPTIONS'].includes(method || '');\n\n if (!isIdempotent) {\n // Don't retry non-idempotent requests - could cause duplicates\n return Promise.reject(e);\n }\n\n // Track retry attempts on the config object\n const retryCount = (config as AxiosRequestConfig & { _retryCount?: number })._retryCount || 0;\n\n if (retryCount >= MAX_RETRIES) {\n // Max retries exceeded, reject with original error\n return Promise.reject(e);\n }\n\n // Calculate delay with exponential backoff\n let delayMs = INITIAL_RETRY_DELAY_MS * Math.pow(2, retryCount);\n\n // Check for Retry-After header (can be in seconds or HTTP date)\n const retryAfter = e.response?.headers?.['retry-after'];\n if (retryAfter) {\n const retryAfterNum = Number.parseInt(retryAfter, 10);\n if (!Number.isNaN(retryAfterNum)) {\n // Retry-After is in seconds\n delayMs = retryAfterNum * 1000;\n } else {\n // Retry-After is an HTTP date\n const retryDate = new Date(retryAfter);\n if (!Number.isNaN(retryDate.getTime())) {\n delayMs = Math.max(0, retryDate.getTime() - Date.now());\n }\n }\n }\n\n // Wait for the calculated delay\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n\n // Increment retry count and retry the request\n (config as AxiosRequestConfig & { _retryCount?: number })._retryCount = retryCount + 1;\n return this.instance.request(config);\n }\n\n // eslint-disable-next-line complexity\n // biome-ignore lint/suspicious/useAwait: <explanation>\n private async handleAxiosError(e: AxiosError): Promise<unknown> {\n // Handle network\n if (!e.response) {\n return Promise.reject(e);\n }\n\n const status = e.response.status as HttpStatuses;\n const errorData = e.response.data as Record<string, unknown>;\n\n // If we have a response with error data in Passflow format\n if ('error' in errorData && typeof errorData.error === 'object' && errorData.error !== null) {\n const { error } = errorData as PassflowResponseError;\n\n return Promise.reject(new PassflowError(error));\n }\n\n // For non-Passflow format errors, create a generic PassflowError\n return Promise.reject(\n new PassflowError({\n id: `error.http.${status}`,\n message: e.message || 'An error occurred',\n status: status,\n location: e.config?.url || 'unknown',\n time: new Date().toISOString(),\n }),\n );\n }\n\n private async send<T, D>(method: RequestMethod, path: string, options?: RequestOptions<D>): Promise<T> {\n const response = await this.instance.request<T>({\n method,\n url: path,\n ...options,\n });\n return response.data;\n }\n\n get<T>(path: string, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.GET, path, config);\n }\n\n post<T, D>(path: string, data?: D, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.POST, path, { data, ...config });\n }\n\n put<T, D>(path: string, data?: D, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.PUT, path, { data, ...config });\n }\n\n patch<T, D>(path: string, data?: D, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.PATCH, path, { data, ...config });\n }\n\n delete<T>(path: string, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.DELETE, path, config);\n }\n\n /**\n * Update the appId and propagate it to axios headers.\n * This ensures that the APP_ID_HEADER_KEY is updated in all future requests.\n *\n * @param appId - The new application ID to set\n */\n setAppId(appId: string): void {\n this.appId = appId;\n\n // Update default headers\n this.defaultHeaders = {\n ...this.defaultHeaders,\n [APP_ID_HEADER_KEY]: appId,\n };\n\n // Update axios instance headers\n this.instance.defaults.headers.common[APP_ID_HEADER_KEY] = appId;\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport { type AppSettings, type PassflowConfig, PassflowEndpointPaths } from './model';\n\nexport class AppAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getAppSettings(): Promise<AppSettings> {\n return this.axiosClient.get<AppSettings>(PassflowEndpointPaths.appSettings);\n }\n}\n","import { AuthenticationResponseJSON, RegistrationResponseJSON } from '@simplewebauthn/types';\nimport type { AxiosRequestConfig } from 'axios';\nimport { APP_ID_HEADER_KEY, AUTHORIZATION_HEADER_KEY } from '../constants';\n\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\nimport { markUpgradeTransaction } from '../upgrade/markers';\n\nimport { AxiosClient } from './axios-client';\nimport {\n OS,\n PassflowAdminEndpointPaths,\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowLogoutResponse,\n PassflowPasskeyAuthenticatePayload,\n PassflowPasskeyAuthenticateStartExtendedPayload,\n PassflowPasskeyAuthenticateStartPayload,\n PassflowPasskeyRegisterPayload,\n PassflowPasskeyRegisterStartExtendedPayload,\n PassflowPasskeyRegisterStartPayload,\n PassflowPasskeyStart,\n PassflowPasswordlessResponse,\n PassflowPasswordlessSignInCompletePayload,\n PassflowPasswordlessSignInExtendedPayload,\n PassflowPasswordlessSignInPayload,\n PassflowSendPasswordResetEmailPayload,\n PassflowSessionValidationResponse,\n PassflowSignInExtendedPayload,\n PassflowSignInPayload,\n PassflowSignUpPayload,\n PassflowSuccessResponse,\n PassflowValidatePayload,\n PassflowValidationResponse,\n UpgradeRefreshField,\n} from './model';\n\nexport class AuthAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Refreshes the session, optionally committing an assurance upgrade.\n *\n * `upgrade` carries `{type, assertion}` — the proof the user just produced.\n * The server scores the new `acr` from the durable session record plus the\n * verified factor and rotates the tokens, so this one endpoint is both the\n * refresh and the elevation.\n *\n * When `upgrade` is present the request is MARKED as part of the upgrade\n * transaction. The server answers a wrong code, a spent challenge, a foreign\n * session and an exhausted budget with a bare 401 carrying no RFC 9470\n * vocabulary, which is indistinguishable from an expired session on the\n * response alone; the marker is what tells the response interceptor not to\n * invalidate the session over a mistyped digit.\n */\n refreshToken(\n refreshToken: string,\n scopes: string[],\n accessToken?: string,\n upgrade?: UpgradeRefreshField,\n ): Promise<PassflowAuthorizationResponse> {\n const payload = {\n access: accessToken,\n scopes,\n // Omitted entirely rather than sent as `null` when absent: the server\n // decodes non-strictly and an explicit null would be a value, not a gap.\n ...(upgrade ? { upgrade } : {}),\n };\n\n const config: AxiosRequestConfig = {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${refreshToken}`,\n },\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, typeof payload>(\n PassflowEndpointPaths.refresh,\n payload,\n upgrade ? markUpgradeTransaction(config) : config,\n );\n }\n\n signIn(payload: PassflowSignInPayload, deviceId: string, os: OS): Promise<PassflowAuthorizationResponse> {\n const defaultPayload: PassflowSignInExtendedPayload = {\n ...payload,\n device: deviceId,\n os,\n };\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowSignInExtendedPayload>(\n PassflowEndpointPaths.signin,\n defaultPayload,\n );\n }\n\n signUp(payload: PassflowSignUpPayload): Promise<PassflowAuthorizationResponse> {\n const { create_tenant, anonymous } = payload;\n const defaultPayload: PassflowSignUpPayload = {\n ...payload,\n create_tenant: create_tenant ?? false,\n anonymous: anonymous ?? false,\n };\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowSignUpPayload>(\n PassflowEndpointPaths.signup,\n defaultPayload,\n );\n }\n\n passwordlessSignIn(\n payload: PassflowPasswordlessSignInPayload,\n deviceId: string,\n os: OS,\n ): Promise<PassflowPasswordlessResponse> {\n const { create_tenant } = payload;\n const defaultPayload: PassflowPasswordlessSignInExtendedPayload = {\n ...payload,\n create_tenant: create_tenant ?? false,\n device: deviceId,\n os,\n };\n return this.axiosClient.post<PassflowPasswordlessResponse, PassflowPasswordlessSignInExtendedPayload>(\n PassflowEndpointPaths.passwordless,\n defaultPayload,\n );\n }\n\n passwordlessSignInComplete(payload: PassflowPasswordlessSignInCompletePayload): Promise<PassflowValidationResponse> {\n return this.axiosClient.post<PassflowValidationResponse, PassflowPasswordlessSignInCompletePayload>(\n PassflowEndpointPaths.passwordlessComplete,\n payload,\n );\n }\n\n logOut(deviceId?: string, refreshToken?: string, isAdmin = false): Promise<PassflowLogoutResponse> {\n const payload = !isAdmin ? { refresh_token: refreshToken, device: deviceId } : undefined;\n const endpoint = isAdmin ? PassflowAdminEndpointPaths.logout : PassflowEndpointPaths.logout;\n\n return this.axiosClient.post<PassflowLogoutResponse, typeof payload>(endpoint, payload);\n }\n\n validateSession(): Promise<PassflowSessionValidationResponse> {\n return this.axiosClient.get<PassflowSessionValidationResponse>(PassflowEndpointPaths.validateSession);\n }\n\n sendPasswordResetEmail(payload: PassflowSendPasswordResetEmailPayload): Promise<PassflowSuccessResponse> {\n return this.axiosClient.post<PassflowSuccessResponse, typeof payload>(\n PassflowEndpointPaths.sendPasswordResetEmail,\n payload,\n );\n }\n\n resetPassword(newPassword: string, scopes: string[], resetToken?: string): Promise<PassflowAuthorizationResponse> {\n const payload = {\n password: newPassword,\n scopes,\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, typeof payload>(PassflowEndpointPaths.resetPassword, payload, {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${resetToken}`,\n [APP_ID_HEADER_KEY]: undefined,\n },\n });\n }\n\n passkeyRegisterStart(\n payload: PassflowPasskeyRegisterStartPayload,\n deviceId: string,\n os: OS,\n isAdmin = false,\n ): Promise<PassflowPasskeyStart> {\n const { create_tenant } = payload;\n const defaultPayload: PassflowPasskeyRegisterStartExtendedPayload = {\n ...payload,\n create_tenant: create_tenant ?? false,\n device: deviceId,\n os,\n };\n\n const endpoint = isAdmin ? PassflowAdminEndpointPaths.passkeyRegisterStart : PassflowEndpointPaths.passkeyRegisterStart;\n\n return this.axiosClient.post<PassflowPasskeyStart, PassflowPasskeyRegisterStartExtendedPayload>(endpoint, defaultPayload);\n }\n\n passkeyRegisterComplete(\n passkeyData: RegistrationResponseJSON,\n deviceId: string,\n challengeId: string,\n isAdmin = false,\n ): Promise<PassflowAuthorizationResponse> {\n const payload: PassflowPasskeyRegisterPayload = {\n challenge_id: challengeId,\n device: deviceId,\n passkey_data: passkeyData,\n };\n\n const endpoint = isAdmin\n ? PassflowAdminEndpointPaths.passkeyRegisterComplete\n : PassflowEndpointPaths.passkeyRegisterComplete;\n\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowPasskeyRegisterPayload>(endpoint, payload);\n }\n\n passkeyAuthenticateStart(\n payload: PassflowPasskeyAuthenticateStartPayload,\n deviceId: string,\n os: OS,\n isAdmin = false,\n ): Promise<PassflowPasskeyStart> {\n const defaultPayload: PassflowPasskeyAuthenticateStartExtendedPayload = {\n ...payload,\n user_id: payload.user_id ?? '',\n device: deviceId,\n os,\n };\n\n const endpoint = isAdmin\n ? PassflowAdminEndpointPaths.passkeyAuthenticateStart\n : PassflowEndpointPaths.passkeyAuthenticateStart;\n\n return this.axiosClient.post<PassflowPasskeyStart, PassflowPasskeyAuthenticateStartExtendedPayload>(\n endpoint,\n defaultPayload,\n );\n }\n\n passkeyAuthenticateComplete(\n passkeyData: AuthenticationResponseJSON,\n deviceId: string,\n challengeId: string,\n isAdmin = false,\n ): Promise<PassflowAuthorizationResponse> {\n const payload: PassflowPasskeyAuthenticatePayload = {\n challenge_id: challengeId,\n device: deviceId,\n passkey_data: passkeyData,\n };\n\n const endpoint = isAdmin\n ? PassflowAdminEndpointPaths.passkeyAuthenticateComplete\n : PassflowEndpointPaths.passkeyAuthenticateComplete;\n\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowPasskeyAuthenticatePayload>(endpoint, payload);\n }\n\n passkeyValidate(\n otp: string,\n deviceId: string,\n challengeId: string,\n isAdmin = false,\n appId?: string,\n ): Promise<PassflowValidationResponse> {\n const payload: PassflowValidatePayload = {\n otp,\n device: deviceId,\n challenge_id: challengeId,\n };\n\n let endpoint: PassflowEndpointPaths.passkeyValidate | PassflowAdminEndpointPaths.passkeyValidate =\n PassflowEndpointPaths.passkeyValidate;\n if (!appId && isAdmin) {\n endpoint = PassflowAdminEndpointPaths.passkeyValidate;\n }\n\n const headers = appId ? { [APP_ID_HEADER_KEY]: appId } : {};\n\n return this.axiosClient.post<PassflowValidationResponse, PassflowValidatePayload>(endpoint, payload, { headers });\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport { CLIAuthCompleteRequest, CLIAuthStatusResponse, PassflowConfig, PassflowEndpointPaths, pathWithParams } from './model';\n\nexport class CLIAuthAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getCLIAuthStatus(sessionId: string): Promise<CLIAuthStatusResponse> {\n const url = pathWithParams(PassflowEndpointPaths.cliAuthStatus, { sessionId });\n return this.axiosClient.get<CLIAuthStatusResponse>(url);\n }\n\n completeCLIAuth(request: CLIAuthCompleteRequest): Promise<void> {\n return this.axiosClient.post<void, CLIAuthCompleteRequest>(PassflowEndpointPaths.cliAuthComplete, request);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport { type PassflowConfig, PassflowEndpointPaths, type PassflowSuccessResponse, pathWithParams } from './model';\n\nexport interface RequestInviteLinkPayload {\n email?: string;\n tenant?: string;\n group?: string;\n role?: string;\n callback?: string;\n send_to_email?: boolean;\n data?: Record<string, unknown>;\n}\n\nexport interface InviteLinkResponse {\n link: string;\n token?: string;\n}\n\nexport interface Invitation {\n id: string;\n archived?: boolean;\n app_id: string;\n inviter_id: string;\n inviter_name: string;\n token: string;\n email?: string;\n role?: string;\n tenant?: string;\n tenant_name?: string;\n group?: string;\n created_by?: string;\n created_at: string;\n expires_at: string;\n callback?: string;\n data?: Record<string, unknown>;\n}\n\n// Internal API response type\ninterface InvitationsAPIResponse {\n invites: Invitation[];\n next_page_skip?: string;\n}\n\n// Public interface for external use\nexport interface InvitationsPaginatedList {\n invites: Invitation[];\n nextPageSkip?: string;\n}\n\nexport class InvitationAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Requests an invitation link that can be used to invite users\n * @param payload Request invitation payload\n * @returns Promise with invitation link and token\n */\n requestInviteLink(payload: RequestInviteLinkPayload): Promise<InviteLinkResponse> {\n return this.axiosClient.post<InviteLinkResponse, RequestInviteLinkPayload>(\n PassflowEndpointPaths.requestInvitation,\n payload,\n );\n }\n\n /**\n * Gets a list of active invitations\n * @param options Optional parameters for filtering and pagination\n * @returns Promise with paginated list of invitations\n */\n getInvitations(options: {\n tenantID: string;\n groupID?: string;\n skip?: number | string;\n limit?: number | string;\n }): Promise<InvitationsPaginatedList> {\n const params: Record<string, string> = {};\n\n if (options.groupID) params.group_id = options.groupID.toString();\n if (options.skip !== undefined) params.skip = options.skip.toString();\n if (options.limit !== undefined) params.limit = options.limit.toString();\n\n const path = pathWithParams(PassflowEndpointPaths.invitationsPath, {\n tenantID: options.tenantID,\n });\n\n return this.axiosClient.get<InvitationsAPIResponse>(path, { params }).then((response) => ({\n invites: response.invites,\n nextPageSkip: response.next_page_skip,\n }));\n }\n\n /**\n * Deletes an invitation by token\n * @param invitationID The invitation ID to delete\n * @returns Promise with success response\n */\n deleteInvitation(invitationID: string): Promise<PassflowSuccessResponse> {\n const path = pathWithParams(PassflowEndpointPaths.invitationDelete, {\n invitationID,\n });\n return this.axiosClient.delete<PassflowSuccessResponse>(path);\n }\n\n /**\n * Resend an invitation by token\n * @param invitationID The invitation ID to resend\n * @returns Promise with success response\n */\n resendInvitation(invitationID: string): Promise<PassflowSuccessResponse> {\n const path = pathWithParams(PassflowEndpointPaths.invitationResend, {\n invitationID,\n });\n return this.axiosClient.post<PassflowSuccessResponse, unknown>(path, {});\n }\n\n /**\n * Get a link to an invitation by id\n * @param invitationID The invitation ID to get link\n * @returns Promise with the link\n */\n getInvitationLink(invitationID: string): Promise<InviteLinkResponse> {\n const path = pathWithParams(PassflowEndpointPaths.invitationGetLink, {\n invitationID,\n });\n return this.axiosClient.get<InviteLinkResponse>(path);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n type PassflowConfig,\n PassflowEndpointPaths,\n type PassflowPasskeySettings,\n type PassflowPasswordPolicySettings,\n type PassflowSettingsAll,\n} from './model';\n\nexport class SettingAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getSettingsAll(): Promise<PassflowSettingsAll> {\n return this.axiosClient.get<PassflowSettingsAll>(PassflowEndpointPaths.settingsAll);\n }\n\n getPasswordPolicySettings(): Promise<PassflowPasswordPolicySettings> {\n return this.axiosClient.get<PassflowPasswordPolicySettings>(PassflowEndpointPaths.settingsPasswordPolicy);\n }\n\n getPasskeySettings(): Promise<PassflowPasskeySettings> {\n return this.axiosClient.get<PassflowPasskeySettings>(PassflowEndpointPaths.settingsPasskey);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowCreateTenantPayload,\n PassflowEndpointPaths,\n PassflowInvitePayload,\n PassflowTenantResponse,\n} from './model';\n\n// Response types\nexport type PassflowStatusResponse = {\n status: string;\n};\n\nexport type PassflowGroupResponse = {\n id: string;\n name: string;\n default?: boolean;\n updated_at: string;\n created_at: string;\n};\n\nexport type PassflowRoleResponse = {\n id: string;\n tenant_id: string;\n name: string;\n};\n\nexport type PassflowUserTenantMembershipResponse = Record<\n string, // tenant_id\n {\n tenant_id: string;\n tenant_name: string;\n groups: Record<string, string[]>; // group_id -> role_ids[]\n group_names: Record<string, string>; // group_id -> group_name\n }\n>;\n\nexport type PassflowInvitationItem = {\n id: string;\n archived: boolean;\n app_id: string;\n inviter_id: string;\n inviter_name: string;\n token: string;\n email: string;\n role: string;\n tenant: string;\n tenant_name: string;\n group: string;\n created_by: string;\n created_at: string;\n expires_at: string;\n};\n\nexport type PassflowInvitationsResponse = {\n invites: PassflowInvitationItem[];\n next_page_skip: string;\n};\n\n// Request payload types\nexport type PassflowUpdateTenantPayload = {\n name: string;\n};\n\nexport type PassflowCreateGroupPayload = {\n name: string;\n};\n\nexport type PassflowUpdateGroupPayload = {\n name: string;\n};\n\nexport type PassflowAddUserToGroupPayload = {\n user_id: string;\n role: string;\n};\n\nexport type PassflowRemoveUserRolesPayload = {\n user_id: string;\n roles: string[];\n};\n\nexport type PassflowChangeUserRolesPayload = {\n user_id: string;\n roles: string[];\n};\n\nexport type PassflowCreateRolePayload = {\n name: string;\n};\n\nexport type PassflowUpdateRolePayload = {\n name: string;\n};\n\nexport class TenantAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n joinInvitation(token: string, scopes: string[]): Promise<PassflowAuthorizationResponse> {\n const payload = {\n invite_token: token,\n scopes,\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowInvitePayload>(\n PassflowEndpointPaths.joinInvitation,\n payload,\n );\n }\n\n createTenant(name: string): Promise<PassflowTenantResponse> {\n const payload = {\n name,\n };\n return this.axiosClient.post<PassflowTenantResponse, PassflowCreateTenantPayload>(\n PassflowEndpointPaths.tenantPath,\n payload,\n );\n }\n\n // 1. Tenant Management\n\n /**\n * Get tenant details\n * @param tenantId Tenant ID\n */\n getTenantDetails(tenantId: string): Promise<PassflowTenantResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}`;\n return this.axiosClient.get<PassflowTenantResponse>(path);\n }\n\n /**\n * Update tenant name\n * @param tenantId Tenant ID\n * @param name New tenant name\n */\n updateTenant(tenantId: string, name: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}`;\n const payload: PassflowUpdateTenantPayload = { name };\n return this.axiosClient.put<PassflowStatusResponse, PassflowUpdateTenantPayload>(path, payload);\n }\n\n /**\n * Delete a tenant\n * @param tenantId Tenant ID\n */\n deleteTenant(tenantId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n /**\n * Get user's tenant memberships\n */\n getUserTenantMembership(): Promise<PassflowUserTenantMembershipResponse> {\n return this.axiosClient.get<PassflowUserTenantMembershipResponse>(PassflowEndpointPaths.tenantPath);\n }\n\n // 2. Group Management\n\n /**\n * Create a group in a tenant\n * @param tenantId Tenant ID\n * @param name Group name\n */\n createGroup(tenantId: string, name: string): Promise<PassflowGroupResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group`;\n const payload: PassflowCreateGroupPayload = { name };\n return this.axiosClient.post<PassflowGroupResponse, PassflowCreateGroupPayload>(path, payload);\n }\n\n /**\n * Get group information\n * @param tenantId Tenant ID\n * @param groupId Group ID\n */\n getGroupInfo(tenantId: string, groupId: string): Promise<PassflowGroupResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}`;\n return this.axiosClient.get<PassflowGroupResponse>(path);\n }\n\n /**\n * Update a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param name New group name\n */\n updateGroup(tenantId: string, groupId: string, name: string): Promise<PassflowGroupResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}`;\n const payload: PassflowUpdateGroupPayload = { name };\n return this.axiosClient.put<PassflowGroupResponse, PassflowUpdateGroupPayload>(path, payload);\n }\n\n /**\n * Delete a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n */\n deleteGroup(tenantId: string, groupId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n /**\n * Add a user to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param role Role to assign\n */\n addUserToGroup(tenantId: string, groupId: string, userId: string, role: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/add`;\n const payload: PassflowAddUserToGroupPayload = { user_id: userId, role };\n return this.axiosClient.post<PassflowStatusResponse, PassflowAddUserToGroupPayload>(path, payload);\n }\n\n /**\n * Remove user roles from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles Roles to remove\n */\n removeUserRolesFromGroup(\n tenantId: string,\n groupId: string,\n userId: string,\n roles: string[],\n ): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/remove_roles`;\n const payload: PassflowRemoveUserRolesPayload = { user_id: userId, roles };\n return this.axiosClient.post<PassflowStatusResponse, PassflowRemoveUserRolesPayload>(path, payload);\n }\n\n /**\n * Change user roles in a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles New roles to assign\n */\n changeUserRoles(tenantId: string, groupId: string, userId: string, roles: string[]): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/change`;\n const payload: PassflowChangeUserRolesPayload = { user_id: userId, roles };\n return this.axiosClient.post<PassflowStatusResponse, PassflowChangeUserRolesPayload>(path, payload);\n }\n\n /**\n * Delete a user from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n */\n deleteUserFromGroup(tenantId: string, groupId: string, userId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/${userId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n // 3. Role Management\n\n /**\n * Get roles for a tenant\n * @param tenantId Tenant ID\n */\n getRolesForTenant(tenantId: string): Promise<PassflowRoleResponse[]> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role`;\n return this.axiosClient.get<PassflowRoleResponse[]>(path);\n }\n\n /**\n * Create a role for a tenant\n * @param tenantId Tenant ID\n * @param name Role name\n */\n createRoleForTenant(tenantId: string, name: string): Promise<PassflowRoleResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role`;\n const payload: PassflowCreateRolePayload = { name };\n return this.axiosClient.post<PassflowRoleResponse, PassflowCreateRolePayload>(path, payload);\n }\n\n /**\n * Update a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n * @param name New role name\n */\n updateRole(tenantId: string, roleId: string, name: string): Promise<PassflowRoleResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role/${roleId}`;\n const payload: PassflowUpdateRolePayload = { name };\n return this.axiosClient.put<PassflowRoleResponse, PassflowUpdateRolePayload>(path, payload);\n }\n\n /**\n * Delete a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n */\n deleteRole(tenantId: string, roleId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role/${roleId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n // 4. User Management in Tenants\n\n /**\n * Delete a user from a tenant\n * @param tenantId Tenant ID\n * @param userId User ID\n */\n deleteUserFromTenant(tenantId: string, userId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/user/${userId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n // 5. Invitation Management\n\n /**\n * Get invitations to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n */\n getGroupInvitations(tenantId: string, groupId: string, limit: number, skip: number): Promise<PassflowInvitationsResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/invitations`;\n return this.axiosClient.get<PassflowInvitationsResponse>(path, {\n params: { limit, skip },\n });\n }\n\n /**\n * Get invitations to a tenant\n * @param tenantId Tenant ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n */\n getTenantInvitations(tenantId: string, limit: number, skip: number): Promise<PassflowInvitationsResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/invitations`;\n return this.axiosClient.get<PassflowInvitationsResponse>(path, {\n params: { limit, skip },\n });\n }\n\n /**\n * Invalidate an invitation by ID\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param inviteId Invitation ID\n */\n invalidateInviteById(tenantId: string, groupId: string, inviteId: string): Promise<Record<string, never>> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/invite/${inviteId}`;\n return this.axiosClient.delete<Record<string, never>>(path);\n }\n\n /**\n * Invalidate an invitation by email\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param email Email address\n */\n invalidateInviteByEmail(tenantId: string, groupId: string, email: string): Promise<Record<string, never>> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/invite/email/${email}`;\n return this.axiosClient.delete<Record<string, never>>(path);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n PassflowConfig,\n PassflowEndpointPaths,\n RegisteredTwoFactorMethod,\n TwoFactorAlternativeRequest,\n TwoFactorChallengeRequest,\n TwoFactorChallengeResponse,\n TwoFactorConfirmRequest,\n TwoFactorConfirmResponse,\n TwoFactorDisableRequest,\n TwoFactorDisableResponse,\n TwoFactorMethod,\n TwoFactorRecoveryRequest,\n TwoFactorRecoveryResponse,\n TwoFactorRegenerateRequest,\n TwoFactorRegenerateResponse,\n TwoFactorSetupMagicLinkErrorCode,\n TwoFactorSetupMagicLinkValidationResponse,\n TwoFactorSetupResponse,\n TwoFactorStatusResponse,\n TwoFactorVerifyRequest,\n TwoFactorVerifyRequestV2,\n TwoFactorVerifyResponse,\n TwoFactorVerifyResponseV2,\n pathWithParams,\n} from './model';\n\n/** Backend response format (snake_case) for magic link validation */\ninterface TwoFactorSetupMagicLinkBackendResponse {\n session_token?: string;\n user_id?: string;\n expires_in?: number;\n app_id?: string | null;\n}\n\n/**\n * API client for Two-Factor Authentication operations\n */\nexport class TwoFactorApiClient {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Get current 2FA enrollment status\n * GET /user/2fa/status\n */\n getStatus(): Promise<TwoFactorStatusResponse> {\n return this.axiosClient.get<TwoFactorStatusResponse>(PassflowEndpointPaths.twoFactorStatus);\n }\n\n /**\n * Begin 2FA setup process\n * POST /user/2fa/setup/begin\n * Returns secret and QR code for authenticator app\n */\n beginSetup(): Promise<TwoFactorSetupResponse> {\n return this.axiosClient.post<TwoFactorSetupResponse, {}>(PassflowEndpointPaths.twoFactorSetupBegin, {});\n }\n\n /**\n * Confirm 2FA setup with TOTP code\n * POST /user/2fa/setup/confirm\n * Returns recovery codes on success\n */\n confirmSetup(payload: TwoFactorConfirmRequest): Promise<TwoFactorConfirmResponse> {\n return this.axiosClient.post<TwoFactorConfirmResponse, TwoFactorConfirmRequest>(\n PassflowEndpointPaths.twoFactorSetupConfirm,\n payload,\n );\n }\n\n /**\n * Verify TOTP code during login\n * POST /auth/2fa/verify\n * Uses tfa_token as Bearer token for authentication\n */\n verify(payload: TwoFactorVerifyRequest): Promise<TwoFactorVerifyResponse> {\n const { tfa_token, code } = payload;\n return this.axiosClient.post<TwoFactorVerifyResponse, { code: string }>(\n PassflowEndpointPaths.twoFactorVerify,\n { code },\n {\n headers: {\n Authorization: `Bearer ${tfa_token}`,\n },\n },\n );\n }\n\n /**\n * Use recovery code for authentication\n * POST /auth/2fa/recovery\n * Uses tfa_token as Bearer token for authentication\n */\n useRecoveryCode(payload: TwoFactorRecoveryRequest): Promise<TwoFactorRecoveryResponse> {\n const { tfa_token, recovery_code } = payload;\n return this.axiosClient.post<TwoFactorRecoveryResponse, { recovery_code: string }>(\n PassflowEndpointPaths.twoFactorRecovery,\n { recovery_code },\n {\n headers: {\n Authorization: `Bearer ${tfa_token}`,\n },\n },\n );\n }\n\n /**\n * Disable 2FA (requires TOTP verification)\n * DELETE /user/2fa\n */\n disable(payload: TwoFactorDisableRequest): Promise<TwoFactorDisableResponse> {\n return this.axiosClient.delete<TwoFactorDisableResponse>(PassflowEndpointPaths.twoFactor, { data: payload });\n }\n\n /**\n * Regenerate recovery codes\n * POST /user/2fa/recovery-codes/regenerate\n */\n regenerateRecoveryCodes(payload: TwoFactorRegenerateRequest): Promise<TwoFactorRegenerateResponse> {\n return this.axiosClient.post<TwoFactorRegenerateResponse, TwoFactorRegenerateRequest>(\n PassflowEndpointPaths.twoFactorRegenerateCodes,\n payload,\n );\n }\n\n /**\n * Validate magic link token for 2FA setup\n * GET /auth/2fa-setup/:token\n *\n * This endpoint validates an admin-generated magic link token\n * and returns a scoped session (scope: \"2fa_setup\") that can ONLY\n * be used for completing 2FA setup operations.\n *\n * This method never throws - it always returns a TwoFactorSetupMagicLinkValidationResponse\n * with either success=true and session data, or success=false and error details.\n *\n * @param token - Magic link token from URL parameter\n * @returns Validation response with scoped session token or error\n */\n validateTwoFactorSetupMagicLink(token: string): Promise<TwoFactorSetupMagicLinkValidationResponse> {\n // Construct endpoint with token as path parameter\n const endpoint = `${PassflowEndpointPaths.twoFactorSetupMagicLink}/${token}`;\n\n // No authentication required - token validation IS the authentication\n return this.axiosClient\n .get<TwoFactorSetupMagicLinkValidationResponse>(endpoint, {\n // Override default auth headers (this is a public endpoint)\n transformRequest: [\n (data, headers) => {\n if (headers) {\n delete headers.Authorization;\n }\n return data;\n },\n ],\n })\n .then((response) => {\n // Transform snake_case backend response to camelCase\n const backendResponse = response as unknown as TwoFactorSetupMagicLinkBackendResponse;\n return {\n success: true,\n sessionToken: backendResponse.session_token,\n userId: backendResponse.user_id,\n expiresIn: backendResponse.expires_in,\n appId: backendResponse.app_id,\n };\n })\n .catch((error) => {\n // Map all errors to structured TwoFactorSetupMagicLinkValidationResponse\n // This ensures consumers always get consistent error format\n if (error.response) {\n const status = error.response.status;\n const data = error.response.data || {};\n\n // Parse Retry-After header for rate limiting\n const retryAfter = error.response.headers?.['retry-after']\n ? parseInt(error.response.headers['retry-after'], 10)\n : undefined;\n\n return {\n success: false,\n error: {\n code: data.error || this.mapStatusToErrorCode(status),\n message: data.message || this.getDefaultErrorMessage(status),\n retryAfter,\n },\n };\n }\n\n // Network error or other unexpected error\n return {\n success: false,\n error: {\n code: 'SERVER_ERROR' as const,\n message: error instanceof Error ? error.message : 'Unable to connect to the server. Please check your connection.',\n },\n };\n });\n }\n\n /**\n * Map HTTP status code to magic link error code\n */\n private mapStatusToErrorCode(status: number): TwoFactorSetupMagicLinkErrorCode {\n switch (status) {\n case 400:\n return 'INVALID_TOKEN';\n case 404:\n return 'REVOKED_TOKEN';\n case 410:\n return 'EXPIRED_TOKEN';\n case 429:\n return 'RATE_LIMITED';\n default:\n return 'SERVER_ERROR';\n }\n }\n\n /**\n * Get default error message for HTTP status code\n */\n private getDefaultErrorMessage(status: number): string {\n switch (status) {\n case 400:\n return 'The provided magic link is invalid or malformed.';\n case 404:\n return 'This magic link has been revoked or does not exist.';\n case 410:\n return 'This magic link has expired. Please request a new one from your administrator.';\n case 429:\n return 'Too many validation attempts. Please try again later.';\n default:\n return 'An error occurred while validating the magic link.';\n }\n }\n\n // ============================================\n // v2 Multi-Method 2FA API Methods\n // ============================================\n\n /**\n * Get available 2FA methods for current user\n * GET /v2/user/2fa/methods/available\n */\n getAvailableMethods(): Promise<TwoFactorMethod[]> {\n return this.axiosClient.get<TwoFactorMethod[]>(PassflowEndpointPaths.TwoFactorMethodsAvailable);\n }\n\n /**\n * Get registered 2FA methods for current user\n * GET /v2/user/2fa/methods\n */\n getRegisteredMethods(): Promise<RegisteredTwoFactorMethod[]> {\n return this.axiosClient.get<RegisteredTwoFactorMethod[]>(PassflowEndpointPaths.TwoFactorMethodsRegistered);\n }\n\n /**\n * Start 2FA method setup.\n * POST /v2/user/2fa/setup/start\n *\n * The method travels in the body: at this point it is a parameter of the\n * request, not the identity of an enrolled resource.\n */\n startMethodSetup(method: TwoFactorMethod, payload?: Record<string, unknown>): Promise<unknown> {\n return this.axiosClient.post<unknown, Record<string, unknown>>(PassflowEndpointPaths.TwoFactorSetupStart, {\n ...payload,\n method,\n });\n }\n\n /**\n * Complete 2FA method setup.\n * POST /v2/user/2fa/setup/complete\n */\n completeMethodSetup(method: TwoFactorMethod, payload: Record<string, unknown>): Promise<unknown> {\n return this.axiosClient.post<unknown, Record<string, unknown>>(PassflowEndpointPaths.TwoFactorSetupComplete, {\n ...payload,\n method,\n });\n }\n\n /**\n * Remove registered 2FA method\n * DELETE /v2/user/2fa/methods/:id\n */\n removeMethod(methodId: string): Promise<void> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorMethodRemove, { id: methodId });\n return this.axiosClient.delete<void>(endpoint);\n }\n\n /**\n * Request 2FA challenge during login\n * POST /v2/auth/2fa/challenge\n */\n requestChallenge(payload: TwoFactorChallengeRequest): Promise<TwoFactorChallengeResponse> {\n return this.axiosClient.post<TwoFactorChallengeResponse, TwoFactorChallengeRequest>(\n PassflowEndpointPaths.TwoFactorChallenge,\n payload,\n );\n }\n\n /**\n * Verify 2FA challenge (v2)\n * POST /v2/auth/2fa/verify\n */\n verifyV2(payload: TwoFactorVerifyRequestV2): Promise<TwoFactorVerifyResponseV2> {\n return this.axiosClient.post<TwoFactorVerifyResponseV2, TwoFactorVerifyRequestV2>(\n PassflowEndpointPaths.TwoFactorVerifyV2,\n payload,\n );\n }\n\n /**\n * Switch to alternative 2FA method during challenge\n * POST /v2/auth/2fa/alternative\n */\n switchToAlternative(payload: TwoFactorAlternativeRequest): Promise<TwoFactorChallengeResponse> {\n return this.axiosClient.post<TwoFactorChallengeResponse, TwoFactorAlternativeRequest>(\n PassflowEndpointPaths.TwoFactorAlternative,\n payload,\n );\n }\n\n /**\n * Get trusted devices\n * GET /v2/user/2fa/trusted-devices\n */\n getTrustedDevices(): Promise<unknown[]> {\n return this.axiosClient.get<unknown[]>(PassflowEndpointPaths.TwoFactorTrustedDevices);\n }\n\n /**\n * Revoke trusted device\n * DELETE /v2/user/2fa/trusted-devices/:id\n */\n revokeTrustedDevice(deviceId: string): Promise<void> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorTrustedDeviceRevoke, { id: deviceId });\n return this.axiosClient.delete<void>(endpoint);\n }\n}\n","/**\n * The assurance-upgrade API — the prepare round of a step-up.\n *\n * One endpoint, deliberately. The commit round is `POST /auth/refresh`, which\n * belongs to `AuthAPI` because that is the only path that stores the rotated\n * tokens; splitting it off here would elevate the session on the server and\n * leave the client holding stale ones.\n *\n * Constructed exactly like `AuthAPI`, sharing `StorageManager` and\n * `DeviceService`, which is what makes cookie mode, CSRF and the device headers\n * work without duplicating any of it.\n */\n\nimport type { AxiosRequestConfig } from 'axios';\n\nimport type { DeviceService } from '../device';\nimport type { StorageManager } from '../storage';\nimport { markUpgradeTransaction } from '../upgrade/markers';\n\nimport { AxiosClient } from './axios-client';\nimport { type PassflowConfig, PassflowEndpointPaths, type UpgradeStartWire } from './model';\n\n/** The `POST /auth/upgrade/start` request body. `relying_party_id` is passkey-only. */\nexport type UpgradeStartPayload = {\n method: string;\n relying_party_id?: string;\n};\n\nexport class UpgradeAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Issues a single-use, session-bound upgrade challenge.\n *\n * Returns the raw snake_case body: normalisation is the transport's job, and\n * keeping it out of here means the wire shape has exactly one definition.\n *\n * The request carries the upgrade-transaction marker, so a 401 from this round\n * — a stale access token, a foreign session — is recognised as part of an\n * elevation by the response interceptor and does not invalidate the session\n * behind the user's back.\n */\n startUpgrade(payload: UpgradeStartPayload): Promise<UpgradeStartWire> {\n return this.axiosClient.post<UpgradeStartWire, UpgradeStartPayload>(\n PassflowEndpointPaths.upgradeStart,\n payload,\n markUpgradeTransaction<AxiosRequestConfig>({}),\n );\n }\n}\n","import { RegistrationResponseJSON } from '@simplewebauthn/types';\n\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n OS,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowPasskeyRegisterPayload,\n PassflowPasskeyStart,\n PassflowSuccessResponse,\n PassflowUserPasskey,\n} from './model';\n\nexport class UserAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getUserPasskeys() {\n return this.axiosClient.get<PassflowUserPasskey[]>(PassflowEndpointPaths.userPasskey);\n }\n\n renameUserPasskey(name: string, passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.axiosClient.patch<PassflowSuccessResponse, { name: string }>(\n `${PassflowEndpointPaths.userPasskey}/${passkeyId}`,\n {\n name,\n },\n );\n }\n\n deleteUserPasskey(passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.axiosClient.delete<PassflowSuccessResponse>(`${PassflowEndpointPaths.userPasskey}/${passkeyId}`);\n }\n\n addUserPasskeyStart({\n relyingPartyId,\n deviceId,\n os,\n passkeyDisplayName,\n passkeyUsername,\n }: {\n relyingPartyId: string;\n deviceId: string;\n os: OS;\n passkeyDisplayName?: string;\n passkeyUsername?: string;\n }): Promise<PassflowPasskeyStart> {\n const payload = {\n passkey_display_name: passkeyDisplayName,\n passkey_username: passkeyUsername,\n relying_party_id: relyingPartyId,\n deviceId,\n os,\n };\n\n return this.axiosClient.post<PassflowPasskeyStart, typeof payload>(PassflowEndpointPaths.addUserPasskey, payload);\n }\n\n addUserPasskeyComplete(passkeyData: RegistrationResponseJSON, deviceId: string, challengeId: string): Promise<void> {\n return this.axiosClient.post<void, PassflowPasskeyRegisterPayload>(PassflowEndpointPaths.completeAddUserPasskey, {\n challenge_id: challengeId,\n device: deviceId,\n passkey_data: passkeyData,\n });\n }\n}\n","/**\n * Passflow Store\n *\n * Event subscription system using the Observer pattern.\n * Manages authentication event notifications to subscribers.\n * Supports filtered subscriptions for specific event types.\n *\n * @module store\n */\n\nimport type { TwoFactorMethod } from './api/model';\nimport { ParsedTokens, Tokens } from './types';\n\n/**\n * Passflow event types\n */\nexport enum PassflowEvent {\n SignIn = 'signin',\n SignInStart = 'signin:start',\n Register = 'register',\n RegisterStart = 'register:start',\n SignOut = 'signout',\n SessionRestored = 'session:restored',\n SessionExpired = 'session:expired',\n Error = 'error',\n Refresh = 'refresh',\n RefreshStart = 'refresh:start',\n TokenCacheExpired = 'token-cache-expired',\n TwoFactorRequired = '2fa:required',\n TwoFactorSetupStarted = '2fa:setup_started',\n TwoFactorEnabled = '2fa:enabled',\n TwoFactorDisabled = '2fa:disabled',\n TwoFactorVerified = '2fa:verified',\n TwoFactorRecoveryUsed = '2fa:recovery_used',\n TwoFactorRecoveryCodesLow = '2fa:recovery_low',\n TwoFactorRecoveryCodesExhausted = '2fa:recovery_exhausted',\n TwoFactorSetupMagicLinkValidated = '2fa:magic_link_validated',\n TwoFactorSetupMagicLinkFailed = '2fa:magic_link_failed',\n TwoFactorChallengeReceived = 'two_factor_challenge_received',\n TwoFactorMethodSwitched = 'two_factor_method_switched',\n TwoFactorDeviceTrusted = 'two_factor_device_trusted',\n}\n\n/**\n * Error payload interface for structured error information\n */\nexport interface ErrorPayload {\n message: string;\n code?: string | number;\n details?: unknown;\n originalError?: unknown;\n}\n\n/**\n * Event-specific payload types\n */\nexport type PassflowEventPayload = {\n [PassflowEvent.SignIn]: { tokens?: Tokens; parsedTokens?: ParsedTokens };\n [PassflowEvent.SignInStart]: { email?: string; provider?: string };\n [PassflowEvent.Register]: { tokens?: Tokens; parsedTokens?: ParsedTokens };\n [PassflowEvent.RegisterStart]: { email?: string };\n [PassflowEvent.SignOut]: { userId?: string };\n [PassflowEvent.SessionRestored]: { id: string; email?: string; username?: string; [key: string]: unknown };\n [PassflowEvent.SessionExpired]: { reason: 'refresh_failed' | 'token_invalid' | 'logged_out' };\n [PassflowEvent.Error]: ErrorPayload;\n [PassflowEvent.Refresh]: { tokens?: Tokens; parsedTokens?: ParsedTokens };\n [PassflowEvent.RefreshStart]: { tokenId?: string };\n [PassflowEvent.TokenCacheExpired]: { isExpired: boolean };\n [PassflowEvent.TwoFactorRequired]: { email: string; challengeId: string; tfaToken: string };\n [PassflowEvent.TwoFactorSetupStarted]: { secret: string; method?: TwoFactorMethod };\n [PassflowEvent.TwoFactorEnabled]: { recoveryCodes: string[]; clearRecoveryCodes: () => void };\n [PassflowEvent.TwoFactorDisabled]: Record<string, never>;\n [PassflowEvent.TwoFactorVerified]: { tokens?: Tokens };\n [PassflowEvent.TwoFactorRecoveryUsed]: { tokens?: Tokens; remainingCodes: number };\n [PassflowEvent.TwoFactorRecoveryCodesLow]: { tokens?: Tokens; remainingCodes: number };\n [PassflowEvent.TwoFactorRecoveryCodesExhausted]: { tokens?: Tokens };\n [PassflowEvent.TwoFactorSetupMagicLinkValidated]: {\n userId: string;\n appId?: string | null;\n expiresIn: number;\n sessionToken: string;\n };\n [PassflowEvent.TwoFactorSetupMagicLinkFailed]: {\n error: {\n code: 'INVALID_TOKEN' | 'EXPIRED_TOKEN' | 'REVOKED_TOKEN' | 'RATE_LIMITED' | 'SERVER_ERROR';\n message: string;\n retryAfter?: number;\n };\n };\n [PassflowEvent.TwoFactorChallengeReceived]: {\n challengeId: string;\n method: TwoFactorMethod;\n alternativeMethods: TwoFactorMethod[];\n };\n [PassflowEvent.TwoFactorMethodSwitched]: {\n challengeId: string;\n method: TwoFactorMethod;\n alternativeMethods: TwoFactorMethod[];\n };\n [PassflowEvent.TwoFactorDeviceTrusted]: Record<string, never>;\n};\n\n/**\n * Passflow subscriber interface\n */\nexport interface PassflowSubscriber {\n onAuthChange<E extends PassflowEvent>(eventType: E, payload?: PassflowEventPayload[E]): void;\n}\n\n/**\n * Store for managing Passflow event subscriptions\n */\nexport class PassflowStore {\n private subscribers: Map<PassflowSubscriber, Set<PassflowEvent> | null> = new Map();\n\n /**\n * Subscribe to authentication events\n * @param subscriber The subscriber to register\n * @param events Optional specific events to subscribe to\n */\n subscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]): void {\n if (events?.length) {\n const eventSet = new Set<PassflowEvent>(events);\n this.subscribers.set(subscriber, eventSet);\n } else {\n this.subscribers.set(subscriber, null);\n }\n }\n\n /**\n * Unsubscribe from authentication events\n * @param subscriber The subscriber to unregister\n * @param events Optional specific events to unsubscribe from\n */\n unsubscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]): void {\n if (!events?.length) {\n this.subscribers.delete(subscriber);\n return;\n }\n\n const subscribedEvents = this.subscribers.get(subscriber);\n if (!subscribedEvents) {\n return;\n }\n\n events.forEach((event) => subscribedEvents.delete(event));\n if (subscribedEvents.size === 0) {\n this.subscribers.delete(subscriber);\n }\n }\n\n /**\n * Notify subscribers of an event\n * @param eventType The type of event that occurred\n * @param payload Event-specific payload data\n */\n notify<E extends PassflowEvent>(eventType: E, payload?: PassflowEventPayload[E]): void {\n this.subscribers.forEach((events, subscriber) => {\n if (!events || events.has(eventType)) {\n subscriber.onAuthChange?.(eventType, payload);\n }\n });\n }\n}\n","/**\n * Upgrade transport error classification.\n *\n * The server speaks two languages about the same failure: an exact\n * `PassflowError.id` and an HTTP status. The id is read FIRST and the status only\n * as a fallback, because the id names the cause while the status is a bucket —\n * `refreshErrorStatus` maps four unrelated upgrade errors onto one bare 401, and\n * three of them want the prompt kept open while the fourth ends the elevation.\n *\n * This module is pure: it imports nothing and touches no transport. It\n * duck-types whatever it is handed — a `PassflowError`, a raw `AxiosError`, or a\n * bare object — so it can be tested with literals and reused from any surface.\n */\n\n/** What kind of failure the server reported, in remedy terms rather than status terms. */\nexport type UpgradeErrorKind =\n /** The user typed the wrong code. Retry with a FRESH prepare round; the modal stays open. */\n | 'wrong_code'\n /** Bad, expired, spent or foreign challenge. Same remedy as `wrong_code`. */\n | 'challenge_spent'\n /** The server's per-challenge attempt budget is burned. Stop; a fresh prepare will not help. */\n | 'exhausted'\n /**\n * The CONTROLLER's local attempt budget is burned: the user has answered\n * wrongly `maxAttempts` times and the elevation gives up.\n *\n * Terminal, and distinct from the four retryable kinds for one specific\n * reason: the loop's last failure is usually a `wrong_code`, which IS\n * retryable, and returning that error as the flight's outcome told every\n * surface above \"ask them again\" for a flight that will never ask again — so\n * the UI re-prompted forever against a budget the server had already closed.\n * The budget being spent is the fact that must travel, not the last answer\n * that spent it; the answer travels on `cause`.\n */\n | 'attempts_exhausted'\n /** This FACTOR is not enrolled for this user. Drop it for this flight, offer the others. */\n | 'not_registered'\n /** Our static `requiresPrepare` disagrees with the server. Recover by asserting directly. */\n | 'prepare_not_required'\n /** Unsupported factor, or no session at all. Not retryable; surface it, do not log the user out. */\n | 'fatal'\n /** The deployment cannot serve an upgrade at all (no challenge storage, or a BFF commit). */\n | 'unavailable'\n /** A malformed response from a server that claimed success. */\n | 'protocol'\n /** No response: the request never completed. */\n | 'network'\n /**\n * A genuine session failure DURING the elevation — a commit 401 that is none of\n * the four recoverable upgrade errors, so it is an expired or revoked refresh\n * token rather than a bad answer. Terminates the elevation instead of\n * re-prompting, and the transport invalidates the session itself.\n */\n | 'session_changed';\n\n/** Server error ids this module classifies. Verified against `src/l/messages_const.go`. */\nexport const UpgradeErrorIds = {\n twoFactorCodeInvalid: 'error.api.request.2fa_code.invalid',\n challengeInvalid: 'error.upgrade.challenge_invalid',\n challengeSessionMismatch: 'error.upgrade.challenge_session_mismatch',\n challengeTooManyAttempts: 'error.upgrade.challenge_too_many_attempts',\n unsupportedFactor: 'error.upgrade.unsupported_factor',\n noSession: 'error.upgrade.no_session',\n unavailable: 'error.upgrade.unavailable',\n methodNotRegistered: 'error.upgrade.method_not_registered',\n prepareNotRequired: 'error.upgrade.prepare_not_required',\n methodNotSupported: 'error.2fa.method_not_supported',\n twoFactorNotEnabled: 'error.2fa.not_enabled',\n notImplemented: 'error.not.implemented',\n tokenInvalid: 'error.api.request.token.invalid',\n} as const;\n\n/**\n * **Every** error `refreshErrorStatus` maps to a 401 on the commit path, as\n * enumerated by `src/web/api/refresh_token.go:157-178`.\n *\n * All five are \"recoverable\" in the session sense: every one of them is the\n * upgrade protocol talking, so none of them means the session died. Note that\n * `challenge_too_many_attempts` is in here yet is NOT retryable — it ends the\n * elevation while leaving the session intact. The two axes are independent and\n * are deliberately not collapsed.\n *\n * `error.2fa.not_enabled` was missing from this list, which signed out a user\n * who had no 2FA enrolled the moment an elevation reached the commit round —\n * the exact lock-out PFL-497 exists to prevent. The set is kept complete for\n * documentation and for callers, but the invalidation decision no longer rests\n * on it: see `invalidatesSessionOnCommit`, which is the inverse predicate.\n */\nexport const RECOVERABLE_COMMIT_ERROR_IDS: readonly string[] = Object.freeze([\n UpgradeErrorIds.twoFactorCodeInvalid,\n UpgradeErrorIds.twoFactorNotEnabled,\n UpgradeErrorIds.challengeInvalid,\n UpgradeErrorIds.challengeSessionMismatch,\n UpgradeErrorIds.challengeTooManyAttempts,\n]);\n\n/**\n * Error ids that positively mean the SESSION is gone, not merely the elevation.\n *\n * This is the predicate the transport invalidates on, and it is deliberately a\n * POSITIVE list rather than the complement of the recoverable one. The server's\n * 401 set on the commit path is enumerated and small, so a list of \"ids that\n * mean the session died\" can be complete in a way that \"everything else\" never\n * is; and when a future server adds a sixth 401 id, an allow-list of expiries\n * fails toward \"I don't know\" rather than toward \"log them out\", which is this\n * feature's governing rule. A client-side miss costs a stale session that the\n * next ordinary 401 clears; a false positive costs the user their session in\n * the middle of proving who they are.\n */\nexport const SESSION_INVALIDATING_COMMIT_ERROR_IDS: readonly string[] = Object.freeze([UpgradeErrorIds.tokenInvalid]);\n\n/** Kinds the user can recover from by answering again, possibly with another factor. */\nconst RETRYABLE_KINDS: readonly UpgradeErrorKind[] = Object.freeze([\n 'wrong_code',\n 'challenge_spent',\n 'not_registered',\n 'prepare_not_required',\n]);\n\n/** True when a kind leaves the elevation alive. Single source for `retryable`. */\nexport const isRetryableUpgradeKind = (kind: UpgradeErrorKind): boolean => RETRYABLE_KINDS.includes(kind);\n\nexport type UpgradeTransportErrorInit = {\n kind: UpgradeErrorKind;\n message?: string;\n status?: number;\n /** The `PassflowError.id`, verbatim, when the server supplied one. */\n errorId?: string;\n cause?: unknown;\n};\n\n/** Which round of the transaction failed. The same status means different things on each. */\nexport type UpgradeEndpoint = 'start' | 'refresh';\n\nconst DEFAULT_MESSAGES: Record<UpgradeErrorKind, string> = {\n wrong_code: 'That code was not accepted. Try again.',\n challenge_spent: 'That verification challenge is no longer valid. Start again.',\n exhausted: 'Too many attempts. Try again later.',\n attempts_exhausted: 'Too many incorrect attempts. Verification was not completed.',\n not_registered: 'That verification method is not set up for this account.',\n prepare_not_required: 'This verification method needs no preparation round.',\n fatal: 'This verification method cannot be used right now.',\n unavailable: 'Step-up verification is not available on this deployment.',\n protocol: 'The server returned an unexpected upgrade response.',\n network: 'The verification request did not reach the server.',\n session_changed: 'The session ended during verification.',\n};\n\n/**\n * A classified transport failure.\n *\n * `code` is the literal, bundling-proof discriminant every upgrade error class\n * carries; the server's own id lives in `errorId`. (The design's §3.3 called the\n * latter `code`, which §13.3 then claimed for the literal tag — `errorId` keeps\n * both without one shadowing the other.)\n */\nexport class UpgradeTransportError extends Error {\n readonly name = 'UpgradeTransportError';\n readonly code = 'upgrade_transport' as const;\n readonly kind: UpgradeErrorKind;\n readonly status?: number;\n readonly errorId?: string;\n readonly retryable: boolean;\n /** Declared here rather than inherited: `Error.cause` is ES2022 and this build targets ES2021 libs. */\n readonly cause?: unknown;\n\n constructor(init: UpgradeTransportErrorInit) {\n super(init.message || DEFAULT_MESSAGES[init.kind]);\n this.kind = init.kind;\n this.status = init.status;\n this.errorId = init.errorId;\n this.retryable = isRetryableUpgradeKind(init.kind);\n if (init.cause !== undefined) {\n this.cause = init.cause;\n }\n }\n}\n\n/**\n * Every literal `code` an upgrade error class carries, including the two that\n * live in `controller.ts` (`upgrade_cancelled`, `upgrade_unsatisfiable`).\n *\n * The codes are listed rather than imported because this module imports nothing\n * — `controller.ts` imports it, so naming those classes here would be a cycle —\n * and because `code` is the discriminant that survives bundling, a duplicated\n * package copy and a cross-realm boundary, which is exactly the property a\n * pass-through guard needs.\n */\nconst UPGRADE_ERROR_CODES: readonly string[] = Object.freeze([\n 'upgrade_transport',\n 'upgrade_insufficient',\n 'upgrade_unavailable',\n 'upgrade_session_changed',\n 'upgrade_cancelled',\n 'upgrade_unsatisfiable',\n]);\n\n/**\n * Reports whether a value is one of the SDK's own upgrade error classes — an\n * outcome that has already been decided, on any surface.\n *\n * Used where a lower layer must not re-bucket a settled outcome: classifying an\n * `UpgradeSessionChangedError` as a bare network failure (which is what\n * happened, because it carries no `response`) turned §13.3's `session changed\n * mid-flight` row into `upgrade_transport` on all four adapters.\n */\nexport const isUpgradeSdkError = (e: unknown): e is Error & { code: string } =>\n typeof e === 'object' &&\n e !== null &&\n typeof (e as { code?: unknown }).code === 'string' &&\n UPGRADE_ERROR_CODES.includes((e as { code: string }).code);\n\n/** Reports whether a value is an already-classified transport failure. */\nexport const isUpgradeTransportError = (e: unknown): e is UpgradeTransportError =>\n e instanceof UpgradeTransportError ||\n (typeof e === 'object' && e !== null && (e as { code?: unknown }).code === 'upgrade_transport');\n\ntype ErrorFacts = { status?: number; errorId?: string; message?: string; hasResponse: boolean };\n\n/**\n * Pulls status, error id and message out of whatever the API layer rejected with.\n *\n * Three shapes reach here: a `PassflowError` (what `AxiosClient` normally\n * produces), a raw `AxiosError` whose body carries the Passflow envelope, and a\n * plain `Error` from a non-HTTP failure.\n */\nconst readErrorFacts = (e: unknown): ErrorFacts => {\n if (typeof e !== 'object' || e === null) {\n return { hasResponse: false, message: typeof e === 'string' ? e : undefined };\n }\n\n const candidate = e as {\n id?: unknown;\n status?: unknown;\n message?: unknown;\n response?: { status?: unknown; data?: unknown };\n };\n\n const response = candidate.response;\n const body = response?.data as { error?: { id?: unknown } } | undefined;\n const envelopeId = typeof body?.error?.id === 'string' ? body.error.id : undefined;\n\n const status =\n typeof candidate.status === 'number'\n ? candidate.status\n : typeof response?.status === 'number'\n ? response.status\n : undefined;\n\n return {\n status,\n errorId: typeof candidate.id === 'string' ? candidate.id : envelopeId,\n message: typeof candidate.message === 'string' ? candidate.message : undefined,\n hasResponse: response !== undefined || typeof candidate.status === 'number',\n };\n};\n\n/**\n * The id → kind table. `undefined` means \"this id says nothing; fall back to status\".\n *\n * `endpoint` is a parameter because one id genuinely means different things on\n * the two rounds — see `tokenInvalid` below. Everything else is round-independent.\n */\nconst kindFromErrorId = (errorId: string | undefined, endpoint: UpgradeEndpoint): UpgradeErrorKind | undefined => {\n switch (errorId) {\n case UpgradeErrorIds.twoFactorCodeInvalid:\n return 'wrong_code';\n case UpgradeErrorIds.challengeInvalid:\n case UpgradeErrorIds.challengeSessionMismatch:\n return 'challenge_spent';\n case UpgradeErrorIds.challengeTooManyAttempts:\n return 'exhausted';\n case UpgradeErrorIds.methodNotRegistered:\n return 'not_registered';\n // `error.2fa.not_enabled` is the same statement from the account's side —\n // the factor is not set up — and it is ROUND-DEPENDENT.\n //\n // On the PREPARE round it is the server refusing one factor while the\n // flight is still choosing, which §13.4 makes flight-local pruning plus a\n // re-prompt: retryable with another factor, never an expiry.\n //\n // On the COMMIT round C26c governs, and it is explicit: a commit 401\n // carrying any other id, `error.2fa.not_enabled` included, terminates the\n // elevation as `session changed mid-flight` while `setSessionInvalid()` is\n // NOT called and `SessionState` is untouched. Mapping it to\n // `not_registered` there made it retryable, so the controller pruned TOTP\n // and re-prompted against a commit that can only refuse again — the user\n // sat in front of a modal that never resolved. Terminal settlement with the\n // session left alone is the lock-out-free outcome the ticket exists for;\n // invalidation stays off because this id is not on\n // `SESSION_INVALIDATING_COMMIT_ERROR_IDS`.\n case UpgradeErrorIds.twoFactorNotEnabled:\n return endpoint === 'refresh' ? 'session_changed' : 'not_registered';\n case UpgradeErrorIds.prepareNotRequired:\n return 'prepare_not_required';\n case UpgradeErrorIds.unavailable:\n case UpgradeErrorIds.notImplemented:\n return 'unavailable';\n case UpgradeErrorIds.unsupportedFactor:\n case UpgradeErrorIds.noSession:\n case UpgradeErrorIds.methodNotSupported:\n return 'fatal';\n // The one round-dependent id. §13.4's table maps a `start` 401 carrying it\n // to `fatal`; §13.4 revision 5 specifies the COMMIT round's occurrence as\n // *session changed mid-flight* — it is the single id on the positive\n // allow-list `invalidatesSessionOnCommit` reads, so classifying it `fatal`\n // on that round contradicted the predicate that acts on it.\n case UpgradeErrorIds.tokenInvalid:\n return endpoint === 'refresh' ? 'session_changed' : 'fatal';\n default:\n // An id we do not recognise — the normal case against a newer server.\n // `undefined` hands the decision to the status fallback.\n return undefined;\n }\n};\n\n/** The status → kind fallback, which differs per round. */\nconst kindFromStatus = (status: number | undefined, endpoint: UpgradeEndpoint): UpgradeErrorKind => {\n if (status === undefined) {\n return 'network';\n }\n if (status === 501) {\n return 'unavailable';\n }\n if (status === 400) {\n return 'fatal';\n }\n if (status === 401 || status === 403) {\n // On the prepare round an unrecognised 401 is the challenge going stale, and\n // a fresh prepare fixes it. On the commit round an unrecognised id is a\n // server this SDK does not know: the five ids `refreshErrorStatus` can emit\n // as a 401 are all mapped by `kindFromErrorId`, so nothing enumerated\n // reaches here. The elevation ends — there is no sane way to continue it —\n // but the SESSION does not: `invalidatesSessionOnCommit` is a positive list\n // and says nothing about an id it has never seen.\n return endpoint === 'start' ? 'challenge_spent' : 'session_changed';\n }\n if (status >= 500) {\n return 'network';\n }\n return 'fatal';\n};\n\n/**\n * Classifies a rejection from either upgrade round into an `UpgradeTransportError`.\n *\n * Already-classified errors pass through unchanged, so a nested call cannot\n * re-bucket a `prepare_not_required` into something terminal.\n */\nexport const classifyUpgradeError = (e: unknown, endpoint: UpgradeEndpoint): UpgradeTransportError => {\n if (e instanceof UpgradeTransportError) {\n return e;\n }\n\n const facts = readErrorFacts(e);\n if (!facts.hasResponse) {\n return new UpgradeTransportError({ kind: 'network', message: facts.message, cause: e });\n }\n\n const kind = kindFromErrorId(facts.errorId, endpoint) ?? kindFromStatus(facts.status, endpoint);\n\n return new UpgradeTransportError({\n kind,\n message: facts.message,\n status: facts.status,\n errorId: facts.errorId,\n cause: e,\n });\n};\n\n/**\n * Reports whether a classified commit failure is a KNOWN upgrade-protocol error\n * that leaves the session intact.\n *\n * Informational: it answers \"is this one of the server's enumerated commit\n * 401s\". It is NOT the invalidation decision — `invalidatesSessionOnCommit` is,\n * and an unrecognised id is not an expiry just because this list has not heard\n * of it.\n */\nexport const isRecoverableCommitFailure = (error: UpgradeTransportError): boolean =>\n error.errorId !== undefined && RECOVERABLE_COMMIT_ERROR_IDS.includes(error.errorId);\n\n/**\n * Reports whether a classified commit failure means the SESSION has ended.\n *\n * The one predicate the transport invalidates on, and the inverse of what this\n * module used to ask. `undefined` — no id at all — is explicitly NOT an expiry:\n * a 401 the SDK cannot explain is \"I don't know\", and \"I don't know\" must never\n * resolve to signing the user out mid-verification.\n */\nexport const invalidatesSessionOnCommit = (error: UpgradeTransportError): boolean =>\n error.errorId !== undefined && SESSION_INVALIDATING_COMMIT_ERROR_IDS.includes(error.errorId);\n\n/* ────────────────────────────────────────────────────────────────────────────\n * The remaining §13.3 error classes.\n *\n * Every upgrade error carries a literal `code` discriminant as well as being\n * `instanceof`-checkable, because `code` is what survives bundling, a duplicated\n * package copy and a cross-realm boundary — the three situations in which an\n * `instanceof` check quietly returns `false` on a genuine failure.\n *\n * They live beside `UpgradeTransportError` rather than in an adapter so that the\n * axios and imperative surfaces can be pure translation: the core decides which\n * class an outcome maps to (`coreOutcomeToError`), the adapter only throws it.\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** The bar a challenge demanded, or an elevation reached, on both axes. */\nexport type AssuranceBar = {\n /** The raw winning `acr_values` list, for display and diagnostics. */\n acrValues?: string;\n /** The parsed bar. `undefined` = unknown, or not constrained on this axis. */\n level?: number;\n /** The freshness constraint, when that is what was demanded. */\n maxAge?: number;\n};\n\n/**\n * The elevation completed, and the protected operation's replay was challenged\n * again: the proof was real but did not reach this operation's bar.\n *\n * This is the outcome that makes the AAL3-with-only-TOTP path honest. It is\n * per-operation, never per-proof — a requestless `passflow.upgrade()` with no\n * stated bar can never produce it, because it has nothing to replay.\n */\nexport class UpgradeInsufficientError extends Error {\n readonly name = 'UpgradeInsufficientError';\n readonly code = 'upgrade_insufficient' as const;\n readonly obtained: AssuranceBar;\n readonly required: AssuranceBar;\n\n constructor(obtained: AssuranceBar, required: AssuranceBar, message?: string) {\n super(message ?? 'The session was elevated, but this action requires a higher assurance level');\n this.obtained = obtained;\n this.required = required;\n }\n}\n\n/** The deployment cannot serve an upgrade at all — no challenge storage, or a BFF commit. */\nexport class UpgradeUnavailableError extends Error {\n readonly name = 'UpgradeUnavailableError';\n readonly code = 'upgrade_unavailable' as const;\n\n constructor(message = 'Step-up verification is not available on this deployment') {\n super(message);\n }\n}\n\n/** The session ended or changed while the elevation was running (§7.4). */\nexport class UpgradeSessionChangedError extends Error {\n readonly name = 'UpgradeSessionChangedError';\n readonly code = 'upgrade_session_changed' as const;\n\n constructor(message = 'The session changed during verification') {\n super(message);\n }\n}\n","/**\n * Session and user identity pinning for an upgrade flight (§13.4 revision 6).\n *\n * ## The rule\n *\n * An upgrade flight is bound to the **session and user identity it began\n * under**, and that identity is verified **at the moment tokens are applied** —\n * not only around `prepare` and `commit`. On mismatch the rotated tokens are\n * discarded, never applied, and the flight settles as `session changed\n * mid-flight`.\n *\n * ## Why the epoch counter was not enough\n *\n * The round-1 epoch check wraps the two rounds that talk to the server. It does\n * not wrap token APPLICATION, which happens inside `AuthService.refreshToken`\n * after the commit returns. A logout and re-login during an in-flight upgrade\n * therefore let user A's flight write its rotated tokens over user B's live\n * session: the request was authorized as A, the storage belongs to B, and\n * nothing in between compared the two principals.\n *\n * An epoch tells you something changed. An identity check tells you **whether\n * it is still the same principal**, which is the property actually required.\n * Both run, and they are complementary rather than redundant.\n *\n * ## Why this module is pure\n *\n * It imports nothing and reads no storage, so the token-applying path can be\n * given a pin by value and the comparison can be tested with literals. Reading\n * the identity is the host's job — it is the only code that knows where the\n * session lives in each delivery mode.\n */\n\n/**\n * The principal an upgrade flight is pinned to.\n *\n * `sub` is the user. `sid` is the session when the server issues one; it is\n * absent in tokens that carry no session claim, and the field is then simply\n * not part of the comparison — a missing claim cannot be made to prove\n * anything, and inventing a value would make two different sessions compare\n * equal or unequal arbitrarily.\n */\nexport type UpgradeSessionPin = {\n readonly sub: string;\n readonly sid?: string;\n};\n\n/** Claims the pin is read from. Deliberately `unknown`-typed: it is a parsed JWT body. */\nexport type UpgradeSessionClaims = { sub?: unknown; sid?: unknown } | null | undefined;\n\n/**\n * Reads a pin from token claims, or `undefined` when there is no identity to\n * pin to.\n *\n * `sub` is mandatory: without a user there is no principal, and a pin that\n * compares nothing would report \"unchanged\" for every change.\n */\nexport const readSessionPin = (claims: UpgradeSessionClaims): UpgradeSessionPin | undefined => {\n if (!claims || typeof claims.sub !== 'string' || claims.sub === '') {\n return undefined;\n }\n return typeof claims.sid === 'string' && claims.sid !== '' ? { sub: claims.sub, sid: claims.sid } : { sub: claims.sub };\n};\n\n/**\n * Is `current` the same principal the flight was pinned to?\n *\n * `false` when `current` is absent: a pin exists, so the identity WAS readable\n * when the flight began, and an identity that has since become unreadable is a\n * change — the session was torn down. That is fail-closed on token\n * APPLICATION (nothing is written) and still fail-open on session STATE\n * (nothing is invalidated, nobody is logged out). §13.4 revision 6 keeps those\n * two axes separate on purpose.\n *\n * When both sides carry a `sid` it must match as well, which is what catches a\n * logout and re-login as the SAME user.\n */\nexport const sessionPinMatches = (pin: UpgradeSessionPin, current: UpgradeSessionPin | undefined): boolean => {\n if (!current || current.sub !== pin.sub) {\n return false;\n }\n if (pin.sid !== undefined && current.sid !== undefined) {\n return pin.sid === current.sid;\n }\n return true;\n};\n","import { ERROR_MESSAGE_MAX_LENGTH, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH } from '../constants';\n\n/**\n * Validates JWT token format (not signature, just structure)\n * Checks that the token has 3 parts separated by dots and each part is valid base64url\n *\n * @param token - The token string to validate\n * @returns true if the token has valid JWT format, false otherwise\n */\nexport function isValidJWTFormat(token: string): boolean {\n if (!token || typeof token !== 'string') return false;\n\n const parts = token.split('.');\n if (parts.length !== 3) return false;\n\n // Check each part is valid base64url (alphanumeric, underscore, hyphen only)\n const base64UrlPattern = /^[A-Za-z0-9_-]+$/;\n return parts.every((part) => base64UrlPattern.test(part) && part.length > 0);\n}\n\n/**\n * Sanitizes error messages from URL parameters to prevent XSS\n * Removes HTML tags and limits length\n *\n * @param message - The error message to sanitize\n * @returns Sanitized error message\n */\nexport function sanitizeErrorMessage(message: string): string {\n // Remove any HTML tags\n const cleaned = message.replace(/<[^>]*>/g, '');\n // Limit length to prevent excessively long error messages\n return cleaned.substring(0, ERROR_MESSAGE_MAX_LENGTH);\n}\n\n/**\n * Validates email format using RFC 5322 simplified pattern\n *\n * @param email - The email string to validate\n * @returns true if valid email format, false otherwise\n */\nexport function isValidEmail(email: string): boolean {\n if (!email || typeof email !== 'string') return false;\n\n const trimmed = email.trim();\n if (trimmed.length === 0) return false;\n\n // RFC 5322 simplified pattern\n const emailPattern = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailPattern.test(trimmed);\n}\n\n/**\n * Validates phone number format (E.164 format)\n *\n * @param phone - The phone number string to validate\n * @returns true if valid phone format, false otherwise\n */\nexport function isValidPhoneNumber(phone: string): boolean {\n if (!phone || typeof phone !== 'string') return false;\n\n const trimmed = phone.trim();\n\n // E.164 format: +[country code][number] (1-15 digits after +)\n const phonePattern = /^\\+[1-9]\\d{1,14}$/;\n return phonePattern.test(trimmed);\n}\n\n/**\n * Validates username format\n *\n * @param username - The username string to validate\n * @returns true if valid username format, false otherwise\n */\nexport function isValidUsername(username: string): boolean {\n if (!username || typeof username !== 'string') return false;\n\n const trimmed = username.trim();\n if (trimmed.length < USERNAME_MIN_LENGTH || trimmed.length > USERNAME_MAX_LENGTH) return false;\n\n // Alphanumeric, underscore, hyphen only\n const usernamePattern = /^[a-zA-Z0-9_-]+$/;\n return usernamePattern.test(trimmed);\n}\n\n/**\n * Validates TOTP code format (6 or 8 numeric digits)\n *\n * @param code - The TOTP code to validate\n * @param digits - Expected number of digits (6 or 8, defaults to 6 for backward compatibility)\n * @returns true if valid TOTP code format, false otherwise\n */\nexport function isValidTOTPCode(code: string, digits: 6 | 8 = 6): boolean {\n if (!code || typeof code !== 'string') return false;\n\n // TOTP codes must be exactly 6 or 8 numeric digits\n const pattern = digits === 8 ? /^\\d{8}$/ : /^\\d{6}$/;\n return pattern.test(code);\n}\n\n/**\n * Normalizes and validates recovery code format\n * Converts to uppercase and removes whitespace\n *\n * @param code - The recovery code to normalize and validate\n * @returns Normalized recovery code if valid, null if invalid\n */\nexport function normalizeRecoveryCode(code: string): string | null {\n if (!code || typeof code !== 'string') return null;\n\n // Normalize: uppercase and remove all whitespace\n const normalized = code.toUpperCase().replace(/\\s+/g, '');\n\n // Basic validation: must be alphanumeric\n // Accept reasonable lengths (4-16 chars) to allow API to do final validation\n // This allows formats like \"ABCD1234\", \"ABCD-1234\", or even \"INVALID\" for testing\n const recoveryPattern = /^[A-Z0-9-]{4,16}$/;\n\n if (!recoveryPattern.test(normalized)) return null;\n\n return normalized;\n}\n","import axios from 'axios';\nimport {\n AuthAPI,\n PassflowAuthorizationResponse,\n PassflowError,\n PassflowFederatedAuthExtendedPayload,\n PassflowFederatedAuthPayload,\n PassflowPasskeyAuthenticateStartPayload,\n PassflowPasskeyRegisterStartPayload,\n PassflowPasswordlessResponse,\n PassflowPasswordlessSignInCompletePayload,\n PassflowPasswordlessSignInPayload,\n PassflowSendPasswordResetEmailPayload,\n PassflowSignInPayload,\n PassflowSignUpPayload,\n PassflowSuccessResponse,\n PassflowValidationResponse,\n TokenExchangeConfig,\n type UpgradeRefreshField,\n getOSFromDeviceType,\n} from '../api';\nimport { DeviceService } from '../device';\nimport { type PlatformAdapter, PopupClosedError } from '../platform';\nimport { webAdapter } from '../platform';\nimport { StorageManager } from '../storage';\nimport { ErrorPayload, PassflowEvent, PassflowStore } from '../store';\nimport { TokenType, isTokenExpired, parseToken } from '../token';\nimport { TokenDeliveryManager, TokenDeliveryMode } from '../token/delivery-manager';\nimport { ParsedTokens, Tokens } from '../types';\nimport { UpgradeSessionChangedError, UpgradeTransportError } from '../upgrade/errors';\nimport { type UpgradeSessionPin, readSessionPin, sessionPinMatches } from '../upgrade/session';\nimport { isValidEmail, isValidPhoneNumber, isValidUsername } from '../utils/validation';\nimport { TokenCacheService } from './token-cache-service';\n\n/**\n * Service for handling authentication related functionality\n */\nexport class AuthService {\n private tokenDeliveryManager: TokenDeliveryManager;\n private tokenExchangeConfig?: TokenExchangeConfig;\n\n constructor(\n private authApi: AuthAPI,\n private deviceService: DeviceService,\n private storageManager: StorageManager,\n private subscribeStore: PassflowStore,\n private tokenCacheService: TokenCacheService,\n private scopes: string[],\n private createTenantForNewUser: boolean,\n private url: string,\n private sessionCallbacks: {\n createSession?: ({ tokens, parsedTokens }: { tokens?: Tokens; parsedTokens?: ParsedTokens }) => Promise<void>;\n expiredSession?: () => Promise<void>;\n },\n private appId?: string,\n tokenExchangeConfig?: TokenExchangeConfig,\n private platform: PlatformAdapter = webAdapter,\n ) {\n this.tokenExchangeConfig = tokenExchangeConfig;\n this.tokenDeliveryManager = new TokenDeliveryManager(storageManager);\n\n // If token exchange is enabled, set BFF mode immediately\n if (tokenExchangeConfig?.enabled) {\n this.tokenDeliveryManager.setMode(TokenDeliveryMode.BFF);\n }\n\n // Initialize session state on page load (cookie/BFF mode only)\n this.initializeSession();\n }\n\n /**\n * Initialize session state on page load for cookie/BFF mode\n */\n private async initializeSession(): Promise<void> {\n if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {\n // Cookie/BFF mode: validate session with server\n await this.restoreSession();\n }\n }\n\n /**\n * Restore session for cookie/BFF mode on page load\n * Validates that HttpOnly cookies are still valid\n * @returns true if session is valid, false otherwise\n */\n async restoreSession(): Promise<boolean> {\n // BFF mode: validate session with BFF endpoint\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.statusUrl) {\n try {\n const response = await fetch(this.tokenExchangeConfig.statusUrl, {\n method: 'GET',\n credentials: 'include', // Include httpOnly cookies\n });\n\n if (response.ok) {\n const data = await response.json();\n if (data.authenticated) {\n this.tokenDeliveryManager.setSessionValid();\n return true;\n }\n }\n\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n } catch (_error) {\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n }\n }\n\n if (!this.tokenDeliveryManager.isCookieMode()) {\n return false; // Only applicable to cookie mode\n }\n\n try {\n // Call lightweight endpoint to validate session\n // This uses the HttpOnly cookies automatically\n const response = await this.authApi.validateSession();\n\n if (response.valid) {\n this.tokenDeliveryManager.setSessionValid();\n\n // If response includes user info, emit event\n if (response.user) {\n this.subscribeStore.notify(PassflowEvent.SessionRestored, response.user);\n }\n\n return true;\n } else {\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n }\n } catch (_error) {\n // Session invalid or network error\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n }\n }\n\n /**\n * Process successful authentication response\n * Handles token storage, session state, CSRF tokens\n * In BFF mode, forwards tokens to BFF server\n */\n private async processAuthResponse(response: PassflowAuthorizationResponse, scopes: string[]): Promise<void> {\n // BFF mode overrides server-returned delivery mode\n if (this.tokenExchangeConfig?.enabled) {\n // Don't change mode from BFF\n } else if ('token_delivery' in response && response.token_delivery) {\n // Detect and update delivery mode from server\n this.tokenDeliveryManager.setMode(response.token_delivery as TokenDeliveryMode);\n }\n\n // Mark session as valid after successful auth\n this.tokenDeliveryManager.setSessionValid();\n\n // BFF mode: forward tokens to BFF server\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.callbackUrl) {\n await this.forwardTokensToBFF(response);\n }\n\n // Save tokens (conditional based on delivery mode)\n // In BFF mode, only ID token is saved locally\n response.scopes = scopes;\n this.storageManager.saveTokens(response, this.tokenDeliveryManager.getMode());\n this.tokenCacheService.setTokensCache(response);\n\n // Store CSRF token if present (cookie mode)\n if (response.csrf_token) {\n this.storageManager.setCsrfToken(response.csrf_token);\n }\n }\n\n /**\n * Forward tokens to BFF server for httpOnly cookie storage\n */\n private async forwardTokensToBFF(tokens: PassflowAuthorizationResponse): Promise<void> {\n if (!this.tokenExchangeConfig?.callbackUrl) {\n return;\n }\n const response = await fetch(this.tokenExchangeConfig.callbackUrl, {\n method: 'POST',\n credentials: 'include', // Include/set httpOnly cookies\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n access_token: tokens.access_token,\n refresh_token: tokens.refresh_token,\n id_token: tokens.id_token,\n // expires_in is returned by the server but not typed in the SDK\n expires_in: (tokens as Record<string, unknown>).expires_in,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`BFF token storage failed: ${response.status}`);\n }\n }\n\n async signIn(payload: PassflowSignInPayload): Promise<PassflowAuthorizationResponse> {\n // Validate input before API call\n if ('email' in payload && payload.email) {\n if (!isValidEmail(payload.email)) {\n const error = new Error('Invalid email format');\n const errorPayload: ErrorPayload = {\n message: 'Invalid email format',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n if ('username' in payload && payload.username) {\n if (!isValidUsername(payload.username)) {\n const error = new Error(\n 'Invalid username format. Username must be 3-30 characters and contain only letters, numbers, underscores, and hyphens',\n );\n const errorPayload: ErrorPayload = {\n message:\n 'Invalid username format. Username must be 3-30 characters and contain only letters, numbers, underscores, and hyphens',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n if ('phone' in payload && payload.phone) {\n if (!isValidPhoneNumber(payload.phone)) {\n const error = new Error('Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)');\n const errorPayload: ErrorPayload = {\n message: 'Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n this.subscribeStore.notify(PassflowEvent.SignInStart, { email: payload.email });\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n payload.scopes = payload.scopes ?? this.scopes;\n\n try {\n const response = await this.authApi.signIn(payload, deviceId, os);\n\n // Check if 2FA is required (either via requires_2fa flag or tfa_token presence)\n if (('requires_2fa' in response && response.requires_2fa === true) || ('tfa_token' in response && response.tfa_token)) {\n // Emit TwoFactorRequired event for TwoFactorService to listen to\n this.subscribeStore.notify(PassflowEvent.TwoFactorRequired, {\n email: payload.email || '',\n challengeId: response.challenge_id || '',\n tfaToken: response.tfa_token || '',\n });\n\n // DO NOT save tokens or emit SignIn event\n // Return response with requires_2fa flag\n return response;\n }\n\n // Normal flow (no 2FA required)\n await this.processAuthResponse(response, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Sign in failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async signUp(payload: PassflowSignUpPayload): Promise<PassflowAuthorizationResponse> {\n // Validate user input before API call\n if (payload.user.email && !isValidEmail(payload.user.email)) {\n const error = new Error('Invalid email format');\n const errorPayload: ErrorPayload = {\n message: 'Invalid email format',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n if (payload.user.phone_number && !isValidPhoneNumber(payload.user.phone_number)) {\n const error = new Error('Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)');\n const errorPayload: ErrorPayload = {\n message: 'Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n this.subscribeStore.notify(PassflowEvent.RegisterStart, { email: payload.user.email });\n payload.scopes = payload.scopes ?? this.scopes;\n payload.create_tenant = this.createTenantForNewUser;\n\n try {\n const response = await this.authApi.signUp(payload);\n await this.processAuthResponse(response, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.Register, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Sign up failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passwordlessSignIn(payload: PassflowPasswordlessSignInPayload): Promise<PassflowPasswordlessResponse> {\n // Validate input before API call\n if (payload.email && !isValidEmail(payload.email)) {\n const error = new Error('Invalid email format');\n const errorPayload: ErrorPayload = {\n message: 'Invalid email format',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n if (payload.phone && !isValidPhoneNumber(payload.phone)) {\n const error = new Error('Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)');\n const errorPayload: ErrorPayload = {\n message: 'Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n this.subscribeStore.notify(PassflowEvent.SignInStart, { email: payload.email });\n payload.scopes = payload.scopes ?? this.scopes;\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n\n try {\n const response = await this.authApi.passwordlessSignIn(payload, deviceId, os);\n // Don't emit SignIn event yet since this is just the first step (magic link sent)\n // We'll emit SignIn when passwordlessSignInComplete is called\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to send passwordless sign-in link',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passwordlessSignInComplete(payload: PassflowPasswordlessSignInCompletePayload): Promise<PassflowValidationResponse> {\n this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n payload.scopes = payload.scopes ?? this.scopes;\n payload.device = this.deviceService.getDeviceId();\n\n try {\n const response = await this.authApi.passwordlessSignInComplete(payload);\n await this.processAuthResponse(response, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Passwordless sign in failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async logOut() {\n // BFF mode: call BFF logout endpoint to clear httpOnly cookies\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.logoutUrl) {\n try {\n const response = await fetch(this.tokenExchangeConfig.logoutUrl, {\n method: 'POST',\n credentials: 'include', // Include httpOnly cookies\n });\n\n if (!response.ok) {\n // Logout endpoint returned error - ignore and continue with local cleanup\n // Server-side session cleanup is best-effort, local cleanup is critical\n }\n } catch (_error) {\n // Network error during logout - ignore and continue with local cleanup\n // User experience is better if we silently handle logout failures\n }\n } else {\n // Regular mode: call passflow logout API\n const refreshToken = this.storageManager.getToken(TokenType.refresh_token);\n const deviceId = this.storageManager.getDeviceId();\n\n try {\n // Call logout API (works in both cookie and JSON modes)\n // Cookie mode: server reads refresh token from HttpOnly cookie\n // JSON mode: uses refresh_token from storage\n const response = await this.authApi.logOut(deviceId, refreshToken, !this.appId);\n if (response.status !== 'ok') {\n throw new Error('Logout failed');\n }\n } catch (_error) {\n // Logout API call failed - ignore and continue with local cleanup\n // User experience is better if we silently handle logout failures\n }\n }\n\n // Clear all local state (both modes)\n this.storageManager.deleteTokens();\n this.storageManager.clearIdToken();\n this.storageManager.clearCsrfToken();\n this.tokenDeliveryManager.reset();\n this.subscribeStore.notify(PassflowEvent.SignOut, {});\n }\n\n /**\n * The session and user identity this client currently holds, or `undefined`\n * when none is readable.\n *\n * Read from the stored access token's claims, which is the same source every\n * other client-side session fact comes from. In a delivery mode that keeps\n * the access token out of reach — a cookie-mode or BFF session — there is\n * nothing to read and the answer is `undefined`.\n */\n readSessionPin(): UpgradeSessionPin | undefined {\n const token = this.storageManager.getToken(TokenType.access_token);\n if (!token) {\n return undefined;\n }\n try {\n return readSessionPin(parseToken(token) as { sub?: unknown; sid?: unknown });\n } catch {\n return undefined;\n }\n }\n\n /**\n * Refuses to APPLY tokens that belong to a different principal (§13.4\n * revision 6 / H2).\n *\n * Called immediately before the rotated tokens are written, which is the only\n * place the check cannot be bypassed: the epoch guard around `prepare` and\n * `commit` runs before the server call, and token application happens after\n * it returns, so a logout and re-login in between let user A's flight write\n * over user B's session.\n *\n * It throws `UpgradeSessionChangedError`, so the elevation terminates as\n * §13.3's `session changed mid-flight` on every surface — the outcome class\n * itself, not a transport failure that happens to carry the kind: this is the\n * only entry point into token application, so a caller holding the promise\n * directly (`AuthService.refreshToken`) must see the same `code` a waiter\n * behind the controller does. It does NOT invalidate\n * anything: `setSessionInvalid()` runs off a positive allow-list elsewhere,\n * and the live user is never logged out as a side effect of someone else's\n * abandoned flight. Fail-closed on token application, fail-open on session\n * state — two different axes.\n */\n private assertSessionPin(pin: UpgradeSessionPin | undefined): void {\n if (!pin) {\n // No pin was captured when the flight started, which means this delivery\n // mode exposes no readable identity at all. There is nothing to compare,\n // so the epoch guard is the only protection in force — stated, not\n // silently assumed.\n return;\n }\n if (sessionPinMatches(pin, this.readSessionPin())) {\n return;\n }\n // biome-ignore lint/suspicious/noConsole: §13.4 revision 6 / C41 requires one visible `warn` on this path. A discarded elevation that left no trace is indistinguishable from a feature that silently never fired, and this is the fail-open diagnostic rule the whole module is built on.\n console.warn(\n '[passflow] the session or user changed while a step-up upgrade was in flight; the elevated tokens were discarded and the live session was left untouched.',\n );\n throw new UpgradeSessionChangedError('The session changed during verification, so the elevated tokens were discarded');\n }\n\n /**\n * Refreshes the session, optionally committing an assurance upgrade.\n *\n * This is the ONLY token-storing path for all four delivery modes, which is\n * why the upgrade commit goes through here rather than posting to\n * `/auth/refresh` directly: it saves the rotated tokens, updates the cache and\n * emits `Refresh`. Bypassing it would elevate the session on the server and\n * leave the client holding stale tokens — everything looks fine until the next\n * request 401s.\n *\n * An upgrade commit changes the error handling in one specific way: a 4xx is\n * NOT treated as a dead session. The server answers a wrong code with a 401,\n * and tearing the session down there is the logout-at-step-up defect. The\n * upgrade transport classifies those failures and invalidates the session\n * itself when the 401 turns out to be a genuine expiry.\n */\n async refreshToken(upgrade?: UpgradeRefreshField, pin?: UpgradeSessionPin): Promise<PassflowAuthorizationResponse> {\n this.subscribeStore.notify(PassflowEvent.RefreshStart, {});\n\n // BFF mode cannot carry an upgrade, and the refusal has to come BEFORE the\n // network call: the BFF refresh endpoint is the customer's own, posted to\n // with an empty body, so there is nowhere to put `{upgrade:{...}}` without a\n // customer-backend change. Refusing early is what lets the UI show\n // \"unavailable\" instead of asking for a code that can never be submitted.\n if (upgrade && this.tokenDeliveryManager.isBFFMode()) {\n throw new UpgradeTransportError({\n kind: 'unavailable',\n message: 'Step-up verification cannot be committed through a BFF refresh endpoint',\n });\n }\n\n // BFF mode: call BFF refresh endpoint\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.refreshUrl) {\n try {\n const response = await fetch(this.tokenExchangeConfig.refreshUrl, {\n method: 'POST',\n credentials: 'include', // Include httpOnly cookies\n });\n\n if (!response.ok) {\n this.tokenDeliveryManager.setSessionInvalid();\n throw new Error('BFF token refresh failed');\n }\n\n const data = await response.json();\n\n // Update session state\n this.tokenDeliveryManager.setSessionValid();\n\n // If BFF returns id_token, update local storage\n if (data.id_token) {\n this.storageManager.setIdToken(data.id_token);\n }\n\n this.subscribeStore.notify(PassflowEvent.Refresh, {\n tokens: data,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenCacheService.isRefreshing = false;\n this.tokenCacheService.tokenExpiredFlag = false;\n return data;\n } catch (error) {\n this.tokenDeliveryManager.setSessionInvalid();\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Token refresh failed',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n // Cookie mode: Server reads refresh token from HttpOnly cookie\n if (this.tokenDeliveryManager.isCookieMode()) {\n try {\n // Call refresh endpoint - browser sends HttpOnly cookies automatically\n const response = upgrade\n ? await this.authApi.refreshToken('', this.scopes, undefined, upgrade)\n : await this.authApi.refreshToken('', this.scopes);\n\n // The pin is verified BEFORE anything is applied — see `assertSessionPin`.\n this.assertSessionPin(pin);\n\n // Update session state\n this.tokenDeliveryManager.setSessionValid();\n\n // Process response (stores ID token, CSRF token)\n await this.processAuthResponse(response, this.scopes);\n\n this.subscribeStore.notify(PassflowEvent.Refresh, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenCacheService.isRefreshing = false;\n this.tokenCacheService.tokenExpiredFlag = false;\n return response;\n } catch (error) {\n // A failed upgrade commit is not a failed session: the four recoverable\n // upgrade errors all arrive as a bare 401. Classification — and the\n // invalidation a genuine expiry still deserves — belongs to the upgrade\n // transport, which knows it issued the request.\n if (!upgrade) {\n this.tokenDeliveryManager.setSessionInvalid();\n }\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Token refresh failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n // JSON mode: Existing behavior\n const tokens = this.storageManager.getTokens();\n if (!tokens) {\n const error = new Error('No tokens found');\n const errorPayload: ErrorPayload = {\n message: 'No tokens found',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n } else if (!tokens?.refresh_token) {\n const error = new Error('No refresh token found');\n const errorPayload: ErrorPayload = {\n message: 'No refresh token found',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n const oldScopes = tokens?.scopes ?? this.scopes;\n try {\n // The `upgrade` argument is passed only when there is one, rather than as\n // an explicit `undefined`: the ordinary refresh call stays byte-identical\n // to what it was before step-up existed.\n const refresh = tokens?.refresh_token ?? '';\n const access = tokens?.access_token;\n const response = upgrade\n ? await this.authApi.refreshToken(refresh, oldScopes, access, upgrade)\n : await this.authApi.refreshToken(refresh, oldScopes, access);\n // The pin is verified BEFORE the tokens are written — see\n // `assertSessionPin`. This is the line H2 exists for: `saveTokens` is the\n // moment user A's rotated tokens would land in user B's storage.\n this.assertSessionPin(pin);\n\n response.scopes = oldScopes;\n this.storageManager.saveTokens(response);\n this.tokenCacheService.setTokensCache(response);\n this.subscribeStore.notify(PassflowEvent.Refresh, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenCacheService.isRefreshing = false;\n this.tokenCacheService.tokenExpiredFlag = false;\n this.tokenCacheService.startTokenCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Token refresh failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n details:\n axios.isAxiosError(error) && error.response\n ? {\n status: error.response.status,\n data: error.response.data,\n }\n : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n\n // Check if this is an auth error (4xx) indicating session is invalid\n const is4xxError =\n axios.isAxiosError(error) && error.response?.status && error.response.status >= 400 && error.response.status < 500;\n\n // On an upgrade commit a 4xx is the server rejecting the PROOF, not the\n // session. Deleting the tokens here would log the user out for mistyping\n // six digits — the defect this flow exists to fix.\n if (is4xxError && !upgrade) {\n // Session is invalid - mark as expired and clean up\n this.tokenCacheService.tokenExpiredFlag = true;\n this.tokenCacheService.setTokensCache(undefined);\n this.storageManager.deleteTokens();\n this.subscribeStore.notify(PassflowEvent.SessionExpired, { reason: 'refresh_failed' });\n }\n\n if (error instanceof PassflowError) {\n throw error;\n } else if (is4xxError && !upgrade) {\n // Rethrown verbatim on the upgrade path instead: the status is the only\n // thing left to classify by once the server sent no error envelope, and\n // flattening it into a generic Error destroys that.\n throw new Error(`Getting unknown error message from server with code:${error.response?.status}`);\n } else {\n throw error;\n }\n }\n }\n\n async sendPasswordResetEmail(payload: PassflowSendPasswordResetEmailPayload): Promise<PassflowSuccessResponse> {\n try {\n return await this.authApi.sendPasswordResetEmail(payload);\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to send password reset email',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async resetPassword(newPassword: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n const urlParams = new URLSearchParams(this.platform.getCurrentUrl()?.search ?? '');\n const resetToken = urlParams.get('token') ?? undefined;\n const sscopes = scopes ?? this.scopes;\n\n try {\n const response = await this.authApi.resetPassword(newPassword, sscopes, resetToken);\n await this.processAuthResponse(response, sscopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Password reset failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passkeyRegister(payload: PassflowPasskeyRegisterStartPayload): Promise<PassflowAuthorizationResponse> {\n if (!this.platform.passkeys) throw new Error('Passkeys are not supported on this platform');\n this.subscribeStore.notify(PassflowEvent.RegisterStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n payload.scopes = payload.scopes ?? this.scopes;\n payload.create_tenant = this.createTenantForNewUser;\n\n try {\n const { challenge_id, publicKey } = await this.authApi.passkeyRegisterStart(payload, deviceId, os, !this.appId);\n // user handle should be base64 encoded for simplewebauthn lib we are using\n publicKey.user.id = this.platform.btoa(publicKey.user.id);\n const webauthn = await this.platform.passkeys.startRegistration(publicKey);\n if (!webauthn) throw new Error('Passkey registration was not completed');\n\n const responseRegisterComplete = await this.authApi.passkeyRegisterComplete(\n webauthn,\n deviceId,\n challenge_id,\n !this.appId,\n );\n await this.processAuthResponse(responseRegisterComplete, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.Register, {\n tokens: responseRegisterComplete,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return responseRegisterComplete;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Passkey registration failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passkeyAuthenticate(payload: PassflowPasskeyAuthenticateStartPayload): Promise<PassflowAuthorizationResponse> {\n if (!this.platform.passkeys) throw new Error('Passkeys are not supported on this platform');\n this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n payload.scopes = payload.scopes ?? this.scopes;\n\n try {\n const { challenge_id, publicKey } = await this.authApi.passkeyAuthenticateStart(payload, deviceId, os, !this.appId);\n const webauthn = await this.platform.passkeys.startAuthentication(publicKey);\n if (!webauthn) throw new Error('Passkey authentication was not completed');\n\n const responseAuthenticateComplete = await this.authApi.passkeyAuthenticateComplete(\n webauthn,\n deviceId,\n challenge_id,\n !this.appId,\n );\n\n if ('access_token' in responseAuthenticateComplete) {\n await this.processAuthResponse(responseAuthenticateComplete, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: responseAuthenticateComplete,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n }\n\n return responseAuthenticateComplete;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Passkey authentication failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n createFederatedAuthUrl(payload: PassflowFederatedAuthExtendedPayload): string {\n const passflowPathWithProvider = `/auth/federated/start/${payload.provider}`;\n\n if (!this.appId) throw new Error('AppId is required for federated auth');\n const sscopes = payload.scopes ?? this.scopes;\n\n const params: Record<string, string> = {\n scopes: sscopes.join(' '),\n redirect_url: payload.redirect_url ?? this.platform.getCurrentUrl()?.origin ?? '',\n appId: this.appId,\n ...(payload.invite_token ? { invite_token: payload.invite_token } : {}),\n ...(payload.create_tenant ? { create_tenant: payload.create_tenant.toString() } : {}),\n ...(payload.device ? { device: payload.device } : {}),\n };\n\n const url = new URL(passflowPathWithProvider, this.url);\n const queryParams = new URLSearchParams(params);\n url.search = queryParams.toString();\n\n return url.toString();\n }\n\n async federatedAuthWithPopup(payload: PassflowFederatedAuthPayload): Promise<void> {\n this.subscribeStore.notify(PassflowEvent.SignInStart, { provider: payload.provider });\n const sscopes = payload.scopes ?? this.scopes;\n const deviceId = this.deviceService.getDeviceId();\n const passflowURL = this.createFederatedAuthUrl({ ...payload, scopes: sscopes, device: deviceId });\n\n try {\n const result = await this.platform.openAuthUrl(passflowURL, {\n mode: 'popup',\n expectedReturnOrigin: this.platform.getCurrentUrl()?.origin ?? '',\n });\n\n const urlParams = new URLSearchParams(result.callbackSearch);\n const access_token = urlParams.get('access_token') || '';\n const refresh_token = urlParams.get('refresh_token') || '';\n const id_token = urlParams.get('id_token') || '';\n\n const tokensData = {\n access_token,\n refresh_token: refresh_token || undefined,\n id_token: id_token || undefined,\n scopes: sscopes,\n };\n\n await this.processAuthResponse(tokensData, sscopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: tokensData,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.platform.navigateTo(this.platform.getCurrentUrl()?.origin ?? '');\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Authentication failed',\n code: error instanceof PopupClosedError ? 'POPUP_CLOSED' : 'AUTH_FAILED',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n }\n }\n\n federatedAuthWithRedirect(payload: PassflowFederatedAuthPayload): void {\n this.subscribeStore.notify(PassflowEvent.SignInStart, { provider: payload.provider });\n const sscopes = payload.scopes ?? this.scopes;\n const deviceId = this.deviceService.getDeviceId();\n const passflowURL = this.createFederatedAuthUrl({ ...payload, scopes: sscopes, device: deviceId });\n this.platform.navigateTo(passflowURL);\n }\n\n // Helper methods for authentication UI redirect\n authRedirectUrl(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): string {\n try {\n const { url, redirectUrl, scopes, appId } = options ?? {};\n const externalUrl = new URL(url ?? this.url);\n // add web to the pathname if it's not there\n externalUrl.pathname = (externalUrl.pathname.endsWith('/') ? externalUrl.pathname : externalUrl.pathname + '/') + 'web';\n const sscopes = scopes ?? this.scopes;\n const params: Record<string, string> = {\n appId: appId ?? this.appId ?? '',\n redirectto: redirectUrl ?? this.platform.getCurrentUrl()?.href ?? '',\n scopes: sscopes.join(','),\n };\n\n const queryParams = new URLSearchParams(params);\n externalUrl.search = queryParams.toString();\n return externalUrl.toString();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to create auth redirect URL',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n authRedirect(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): void {\n try {\n this.platform.navigateTo(this.authRedirectUrl(options));\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to redirect to auth page',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n /**\n * Check if user is authenticated\n * CRITICAL: Cookie/BFF mode checks ID token + session state, NOT access_token\n */\n isAuthenticated(parsedTokens: ParsedTokens): boolean {\n try {\n if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {\n // Cookie/BFF mode: Check for ID token presence + session validity\n const hasIdToken = !!parsedTokens?.id_token || !!this.storageManager.getIdToken();\n const sessionValid = this.tokenDeliveryManager.isSessionValid();\n const sessionUnknown = this.tokenDeliveryManager.isSessionUnknown();\n\n // Trust session if:\n // 1. We have ID token (proves we authenticated at some point)\n // 2. Session is valid (haven't received 401) OR\n // 3. Session is unknown (haven't tried yet, optimistic)\n return hasIdToken && (sessionValid || sessionUnknown);\n }\n\n // JSON mode: existing logic (check access/refresh token expiry)\n if (!parsedTokens || !parsedTokens.access_token) return false;\n\n return (\n !isTokenExpired(parsedTokens.access_token) ||\n (parsedTokens.refresh_token !== undefined && !isTokenExpired(parsedTokens.refresh_token))\n );\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to check authentication status',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return false;\n }\n }\n\n /**\n * Handle session check and callbacks\n */\n async submitSessionCheck(doRefresh = false): Promise<Tokens | undefined> {\n let tokens;\n let parsedTokens;\n try {\n tokens = await this.getTokens(doRefresh);\n parsedTokens = this.tokenCacheService.getParsedTokens();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error || error instanceof PassflowError ? error.message : 'Session check failed',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n tokens = undefined;\n }\n\n if (tokens && this.sessionCallbacks.createSession) {\n await this.sessionCallbacks.createSession({ tokens, parsedTokens });\n }\n\n if (!tokens && this.sessionCallbacks.expiredSession) {\n await this.sessionCallbacks.expiredSession();\n }\n\n return tokens;\n }\n\n /**\n * Get tokens and refresh if needed\n * Cookie/BFF mode: Returns ID token only (access/refresh in HttpOnly cookies)\n * JSON mode: Returns all tokens from localStorage\n */\n async getTokens(doRefresh: boolean): Promise<Tokens | undefined> {\n try {\n // Cookie/BFF mode: Server manages access/refresh tokens in HttpOnly cookies\n if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {\n const tokens = this.storageManager.getTokens();\n // In cookie/BFF mode, we only have ID token in localStorage\n if (!tokens?.id_token) return undefined;\n\n // If session is invalid and refresh requested, try refresh\n if (this.tokenDeliveryManager.isSessionInvalid() && doRefresh) {\n return await this.refreshToken();\n }\n\n // Return tokens (ID token only, access/refresh are in cookies)\n return tokens;\n }\n\n // JSON mode: Existing behavior\n const tokens = this.storageManager.getTokens();\n // we have no token in storage\n if (!tokens || !tokens.access_token) return undefined;\n\n const access = parseToken(tokens.access_token);\n\n if (isTokenExpired(access)) {\n // we have expired token and we need to refresh it or throw error if it's not possible\n if (doRefresh) return await this.refreshToken();\n\n // we need return undefined here, because we have expired token and we no need to refresh it\n return undefined;\n } else {\n return tokens;\n }\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return undefined;\n }\n }\n}\n","import type {\n InvitationAPI,\n InvitationsPaginatedList,\n InviteLinkResponse,\n PassflowSuccessResponse,\n RequestInviteLinkPayload,\n} from '../api';\n\n/**\n * Service for managing invitations\n */\nexport class InvitationService {\n constructor(private invitationApi: InvitationAPI) {}\n\n /**\n * Requests an invitation link that can be used to invite users\n * @param payload Request invitation payload\n * @returns Promise with invitation link and token\n */\n requestInviteLink(payload: RequestInviteLinkPayload): Promise<InviteLinkResponse> {\n return this.invitationApi.requestInviteLink(payload);\n }\n\n /**\n * Gets a list of active invitations\n * @param options Optional parameters for filtering and pagination\n * @returns Promise with paginated list of invitations\n */\n getInvitations(options: {\n tenantID: string;\n groupID?: string;\n skip?: number | string;\n limit?: number | string;\n }): Promise<InvitationsPaginatedList> {\n return this.invitationApi.getInvitations(options);\n }\n\n /**\n * Deletes an invitation by token\n * @param token The invitation token to delete\n * @returns Promise with success response\n */\n deleteInvitation(token: string): Promise<PassflowSuccessResponse> {\n return this.invitationApi.deleteInvitation(token);\n }\n\n /**\n * Resends an invitation by token\n * @param token The invitation token to resend\n * @returns Promise with success response\n */\n resendInvitation(token: string): Promise<PassflowSuccessResponse> {\n return this.invitationApi.resendInvitation(token);\n }\n\n /**\n * Gets a link to an invitation by id\n * @param invitationID The invitation ID to get link\n * @returns Promise with the link\n */\n getInvitationLink(invitationID: string): Promise<InviteLinkResponse> {\n return this.invitationApi.getInvitationLink(invitationID);\n }\n}\n","/**\n * Logger interface for Passflow services\n */\nexport interface Logger {\n error(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\n\n/**\n * Default console logger implementation\n */\nexport class ConsoleLogger implements Logger {\n error(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.error(message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.warn(message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.info(message, ...args);\n }\n\n debug(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.debug(message, ...args);\n }\n}\n\n/**\n * Get the default logger\n * @returns Default logger instance\n */\nexport function getDefaultLogger(): Logger {\n return new ConsoleLogger();\n}\n","// lib/services/tenant-user-membership.ts\n\nimport type { PassflowGroup, PassflowRole, PassflowTenantResponse, PassflowUserInGroup } from '../api/model';\n\n/**\n * Flat user representation\n */\nexport interface User {\n id: string;\n name: string | null;\n email: string | null;\n phone: string | null;\n}\n\n/**\n * Flat group representation\n */\nexport interface Group {\n id: string;\n name: string;\n default: boolean;\n updated_at: string;\n created_at: string;\n}\n\n/**\n * Flat role representation\n */\nexport interface Role {\n id: string;\n tenant_id: string;\n name: string;\n}\n\n/**\n * Maps a user to a group with specific roles\n */\nexport interface Membership {\n userId: string;\n groupId: string;\n roleIds: string[];\n}\n\n/**\n * Full tenant view with lookup maps\n */\nexport interface TenantData {\n tenant_id: string;\n tenant_name: string;\n users: User[];\n groups: Group[];\n roles: Role[];\n memberships: Membership[];\n usersById: Map<string, User>;\n groupsById: Map<string, Group>;\n rolesById: Map<string, Role>;\n}\n\n/**\n * Utility for transforming raw PassflowTenantResponse\n * into a flattened TenantData model with quick lookup methods.\n */\nexport class TenantUserMembership {\n private data: TenantData;\n\n constructor(raw: PassflowTenantResponse) {\n this.data = this.normalize(raw);\n }\n\n private normalize(raw: PassflowTenantResponse): TenantData {\n const users = new Map<string, User>();\n const groups = new Map<string, Group>();\n const roles = new Map<string, Role>();\n const memberships: Membership[] = [];\n\n // process groups\n raw.groups?.forEach((g: PassflowGroup) => {\n groups.set(g.id, {\n id: g.id,\n name: g.name,\n default: g.default ?? false,\n updated_at: g.updated_at,\n created_at: g.created_at,\n });\n });\n\n // process roles\n raw.roles?.forEach((r: PassflowRole) => {\n roles.set(r.id, {\n id: r.id,\n tenant_id: r.tenant_id,\n name: r.name,\n });\n });\n\n // process users and memberships\n raw.users_in_groups?.forEach((uig: PassflowUserInGroup) => {\n const u = uig.user;\n if (u && !users.has(u.id)) {\n users.set(u.id, {\n id: u.id,\n name: u.name ?? null,\n email: u.email ?? null,\n phone: u.phone ?? null,\n });\n }\n if (u && uig.group_id && groups.has(uig.group_id)) {\n memberships.push({\n userId: u.id,\n groupId: uig.group_id,\n roleIds: uig.roles?.map((r) => r.id) ?? [],\n });\n }\n });\n\n return {\n tenant_id: raw.tenant_id,\n tenant_name: raw.tenant_name,\n users: Array.from(users.values()),\n groups: Array.from(groups.values()),\n roles: Array.from(roles.values()),\n memberships,\n usersById: users,\n groupsById: groups,\n rolesById: roles,\n };\n }\n\n /**\n * Returns all users in the specified group.\n */\n getUsersInGroup(groupId: string): User[] {\n return this.data.memberships\n .filter((m) => m.groupId === groupId)\n .map((m) => this.data.usersById.get(m.userId))\n .filter((u): u is User => u !== undefined);\n }\n\n /**\n * Returns all groups to which the specified user belongs.\n */\n getGroupsForUser(userId: string): Group[] {\n return this.data.memberships\n .filter((m) => m.userId === userId)\n .map((m) => this.data.groupsById.get(m.groupId))\n .filter((g): g is Group => g !== undefined);\n }\n\n /**\n * Returns all roles that the specified user has in the specified group.\n */\n getUserRolesInGroup(userId: string, groupId: string): Role[] {\n const membership = this.data.memberships.find((m) => m.userId === userId && m.groupId === groupId);\n if (!membership) {\n return [];\n }\n return membership.roleIds.map((roleId) => this.data.rolesById.get(roleId)).filter((r): r is Role => r !== undefined);\n }\n\n /**\n * Returns the full TenantData object.\n */\n getData(): TenantData {\n return this.data;\n }\n}\n","import axios from 'axios';\nimport {\n PassflowAuthorizationResponse,\n PassflowGroupResponse,\n PassflowInvitationsResponse,\n PassflowRoleResponse,\n PassflowStatusResponse,\n PassflowTenantResponse,\n PassflowUserTenantMembershipResponse,\n TenantAPI,\n} from '../api';\nimport { Logger, getDefaultLogger } from './logger';\nimport { TenantUserMembership } from './tenant-user-membership';\n\n/**\n * Service for managing tenants\n */\nexport class TenantService {\n private logger: Logger;\n\n constructor(\n private tenantApi: TenantAPI,\n private scopes: string[],\n logger?: Logger,\n ) {\n this.logger = logger || getDefaultLogger();\n }\n\n /**\n * Handle Passflow API errors\n * @param error The error object\n * @param context Context information for logging\n * @throws Formatted error with Passflow API error details\n */\n private handlePassflowError(error: unknown, context: string): never {\n // Check if it's an Axios error with a response\n if (axios.isAxiosError(error) && error.response?.data) {\n const responseData = error.response.data;\n\n // Check if it's a Passflow API error format\n if (\n typeof responseData === 'object' &&\n responseData !== null &&\n 'error' in responseData &&\n typeof responseData.error === 'object' &&\n responseData.error !== null\n ) {\n const passflowError = responseData.error as {\n id: string;\n message: string;\n status: number;\n location: string;\n time: string;\n };\n\n // Log the formatted error\n this.logger.error(`${context}: ${passflowError.id} - ${passflowError.message} (Status: ${passflowError.status})`);\n\n // Throw a new error with the formatted message\n throw new Error(`Passflow API Error: ${passflowError.id} - ${passflowError.message} (Status: ${passflowError.status})`);\n }\n }\n\n // If it's not a Passflow API error, log and rethrow\n this.logger.error(`${context}:`, error);\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(String(error));\n }\n\n /**\n * Join a tenant invitation\n * @param token The invitation token\n * @param scopes Optional scopes to request\n * @returns Promise with invite response\n */\n async joinInvitation(token: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n try {\n const sscopes = scopes ?? this.scopes;\n return await this.tenantApi.joinInvitation(token, sscopes);\n } catch (error) {\n this.handlePassflowError(error, 'Join invitation failed');\n }\n }\n\n /**\n * Create a new tenant\n * @param name The name of the tenant\n * @returns Promise with tenant response\n */\n async createTenant(name: string): Promise<PassflowTenantResponse> {\n try {\n return await this.tenantApi.createTenant(name);\n } catch (error) {\n this.handlePassflowError(error, 'Tenant creation failed');\n }\n }\n\n // 1. Tenant Management\n\n /**\n * Get tenant details\n * @param tenantId Tenant ID\n * @returns Promise with tenant response\n */\n /**\n * Get tenant details\n * @param tenantId Tenant ID\n * @returns Promise with tenant response\n */\n async getTenantDetails(tenantId: string): Promise<PassflowTenantResponse> {\n try {\n const response = await this.tenantApi.getTenantDetails(tenantId);\n return response;\n } catch (error) {\n this.handlePassflowError(error, `Get tenant details failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Get tenant details and transform into TenantUserMembership\n * @param tenantId Tenant ID\n * @returns Promise with TenantUserMembership instance\n */\n async getTenantUserMembership(tenantId: string): Promise<TenantUserMembership> {\n try {\n const response = await this.tenantApi.getTenantDetails(tenantId);\n return new TenantUserMembership(response);\n } catch (error) {\n this.handlePassflowError(error, `Get tenant user membership failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Update tenant name\n * @param tenantId Tenant ID\n * @param name New tenant name\n * @returns Promise with status response\n */\n async updateTenant(tenantId: string, name: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.updateTenant(tenantId, name);\n } catch (error) {\n this.handlePassflowError(error, `Update tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Delete a tenant\n * @param tenantId Tenant ID\n * @returns Promise with status response\n */\n async deleteTenant(tenantId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteTenant(tenantId);\n } catch (error) {\n this.handlePassflowError(error, `Delete tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Get user's tenant memberships\n * @returns Promise with user tenant membership response\n */\n async getUserTenantMembership(): Promise<PassflowUserTenantMembershipResponse> {\n try {\n return await this.tenantApi.getUserTenantMembership();\n } catch (error) {\n this.handlePassflowError(error, 'Get user tenant memberships failed');\n }\n }\n\n // 2. Group Management\n\n /**\n * Create a group in a tenant\n * @param tenantId Tenant ID\n * @param name Group name\n * @returns Promise with group response\n */\n async createGroup(tenantId: string, name: string): Promise<PassflowGroupResponse> {\n try {\n return await this.tenantApi.createGroup(tenantId, name);\n } catch (error) {\n this.handlePassflowError(error, `Group creation failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Get group information\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @returns Promise with group response\n */\n async getGroupInfo(tenantId: string, groupId: string): Promise<PassflowGroupResponse> {\n try {\n return await this.tenantApi.getGroupInfo(tenantId, groupId);\n } catch (error) {\n this.handlePassflowError(error, `Get group info failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Update a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param name New group name\n * @returns Promise with group response\n */\n async updateGroup(tenantId: string, groupId: string, name: string): Promise<PassflowGroupResponse> {\n try {\n return await this.tenantApi.updateGroup(tenantId, groupId, name);\n } catch (error) {\n this.handlePassflowError(error, `Update group failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Delete a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @returns Promise with status response\n */\n async deleteGroup(tenantId: string, groupId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteGroup(tenantId, groupId);\n } catch (error) {\n this.handlePassflowError(error, `Delete group failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Add a user to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param role Role to assign\n * @returns Promise with status response\n */\n async addUserToGroup(tenantId: string, groupId: string, userId: string, role: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.addUserToGroup(tenantId, groupId, userId, role);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Add user to group failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n /**\n * Remove user roles from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles Roles to remove\n * @returns Promise with status response\n */\n async removeUserRolesFromGroup(\n tenantId: string,\n groupId: string,\n userId: string,\n roles: string[],\n ): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.removeUserRolesFromGroup(tenantId, groupId, userId, roles);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Remove user roles from group failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n /**\n * Change user roles in a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles New roles to assign\n * @returns Promise with status response\n */\n async changeUserRoles(tenantId: string, groupId: string, userId: string, roles: string[]): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.changeUserRoles(tenantId, groupId, userId, roles);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Change user roles failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n /**\n * Delete a user from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @returns Promise with status response\n */\n async deleteUserFromGroup(tenantId: string, groupId: string, userId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteUserFromGroup(tenantId, groupId, userId);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Delete user from group failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n // 3. Role Management\n\n /**\n * Get roles for a tenant\n * @param tenantId Tenant ID\n * @returns Promise with array of role responses\n */\n async getRolesForTenant(tenantId: string): Promise<PassflowRoleResponse[]> {\n try {\n return await this.tenantApi.getRolesForTenant(tenantId);\n } catch (error) {\n this.handlePassflowError(error, `Get roles for tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Create a role for a tenant\n * @param tenantId Tenant ID\n * @param name Role name\n * @returns Promise with role response\n */\n async createRoleForTenant(tenantId: string, name: string): Promise<PassflowRoleResponse> {\n try {\n return await this.tenantApi.createRoleForTenant(tenantId, name);\n } catch (error) {\n this.handlePassflowError(error, `Create role for tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Update a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n * @param name New role name\n * @returns Promise with role response\n */\n async updateRole(tenantId: string, roleId: string, name: string): Promise<PassflowRoleResponse> {\n try {\n return await this.tenantApi.updateRole(tenantId, roleId, name);\n } catch (error) {\n this.handlePassflowError(error, `Update role failed for tenant ID ${tenantId}, role ID ${roleId}`);\n }\n }\n\n /**\n * Delete a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n * @returns Promise with status response\n */\n async deleteRole(tenantId: string, roleId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteRole(tenantId, roleId);\n } catch (error) {\n this.handlePassflowError(error, `Delete role failed for tenant ID ${tenantId}, role ID ${roleId}`);\n }\n }\n\n // 4. User Management in Tenants\n\n /**\n * Delete a user from a tenant\n * @param tenantId Tenant ID\n * @param userId User ID\n * @returns Promise with status response\n */\n async deleteUserFromTenant(tenantId: string, userId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteUserFromTenant(tenantId, userId);\n } catch (error) {\n this.handlePassflowError(error, `Delete user from tenant failed for tenant ID ${tenantId}, user ID ${userId}`);\n }\n }\n\n // 5. Invitation Management\n\n /**\n * Get invitations to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n * @returns Promise with invitations response\n */\n async getGroupInvitations(\n tenantId: string,\n groupId: string,\n limit: number,\n skip: number,\n ): Promise<PassflowInvitationsResponse> {\n try {\n return await this.tenantApi.getGroupInvitations(tenantId, groupId, limit, skip);\n } catch (error) {\n this.handlePassflowError(error, `Get group invitations failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Get invitations to a tenant\n * @param tenantId Tenant ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n * @returns Promise with invitations response\n */\n async getTenantInvitations(tenantId: string, limit: number, skip: number): Promise<PassflowInvitationsResponse> {\n try {\n return await this.tenantApi.getTenantInvitations(tenantId, limit, skip);\n } catch (error) {\n this.handlePassflowError(error, `Get tenant invitations failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Invalidate an invitation by ID\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param inviteId Invitation ID\n * @returns Promise with empty record\n */\n async invalidateInviteById(tenantId: string, groupId: string, inviteId: string): Promise<Record<string, never>> {\n try {\n return await this.tenantApi.invalidateInviteById(tenantId, groupId, inviteId);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Invalidate invite by ID failed for tenant ID ${tenantId}, group ID ${groupId}, invite ID ${inviteId}`,\n );\n }\n }\n\n /**\n * Invalidate an invitation by email\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param email Email address\n * @returns Promise with empty record\n */\n async invalidateInviteByEmail(tenantId: string, groupId: string, email: string): Promise<Record<string, never>> {\n try {\n return await this.tenantApi.invalidateInviteByEmail(tenantId, groupId, email);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Invalidate invite by email failed for tenant ID ${tenantId}, group ID ${groupId}, email ${email}`,\n );\n }\n }\n}\n","import { AuthAPI } from '../api';\nimport type { PlatformAdapter } from '../platform';\nimport { webAdapter } from '../platform';\nimport { StorageManager } from '../storage';\nimport { ErrorPayload, PassflowEvent, PassflowStore } from '../store';\nimport { isTokenExpired, parseToken } from '../token';\nimport type { ParsedTokens, Tokens } from '../types';\n\nexport class TokenCacheService {\n tokensCache: Tokens | undefined;\n parsedTokensCache: ParsedTokens | undefined;\n\n private checkInterval: NodeJS.Timeout | null = null;\n private readonly CHECK_INTERVAL = 60000; // 1 minute (was 10ms)\n private unsubscribeForeground: (() => void) | null = null;\n private unsubscribeUnload: (() => void) | null = null;\n isRefreshing = false;\n tokenExpiredFlag = false;\n\n constructor(\n private storageManager: StorageManager,\n private authApi: AuthAPI,\n private subscribeStore: PassflowStore,\n private platform: PlatformAdapter = webAdapter,\n ) {\n this.storageManager = storageManager;\n this.authApi = authApi;\n this.setupLifecycleListeners();\n }\n\n initialize() {\n try {\n const tokens = this.storageManager.getTokens();\n if (!tokens) {\n // No tokens to watch. Polling would emit spurious TokenCacheExpired\n // events for logged-out users; wait for a sign-in to (re-)initialize.\n return;\n }\n\n // Cookie mode: access_token may not be in storage (only ID token)\n // In cookie mode, we cache whatever tokens we have (likely just ID token)\n if (!tokens.access_token) {\n this.setTokensCache(tokens);\n this.startTokenCheck();\n return;\n }\n\n // JSON mode: check access_token expiry\n const access = parseToken(tokens.access_token);\n\n if (isTokenExpired(access)) {\n this.tokenExpiredFlag = true;\n this.stopTokenCheck();\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: true });\n } else {\n this.setTokensCache(tokens);\n this.startTokenCheck();\n }\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.setTokensCache(undefined);\n }\n }\n\n private async refreshTokensCache(tokens: Tokens) {\n if (this.isRefreshing) return;\n\n try {\n this.isRefreshing = true;\n this.subscribeStore.notify(PassflowEvent.RefreshStart, {});\n\n const response = await this.authApi.refreshToken(tokens?.refresh_token ?? '', tokens.scopes ?? [], tokens.access_token);\n this.setTokensCache(response);\n\n this.subscribeStore.notify(PassflowEvent.Refresh, { tokens: response, parsedTokens: this.getParsedTokens() });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenExpiredFlag = false;\n this.startTokenCheck();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n\n // Mark session as expired to prevent infinite refresh loops\n this.tokenExpiredFlag = true;\n this.setTokensCache(undefined);\n this.stopTokenCheck();\n\n // Clear stored tokens since refresh failed\n this.storageManager.deleteTokens();\n\n // Notify that session has expired - app should redirect to login\n this.subscribeStore.notify(PassflowEvent.SessionExpired, { reason: 'refresh_failed' });\n } finally {\n this.isRefreshing = false;\n }\n }\n\n startTokenCheck() {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n }\n\n if (this.tokenExpiredFlag) return;\n\n // Setup lifecycle listeners for foreground/visibility checks\n this.setupLifecycleListeners();\n\n this.checkInterval = setInterval(() => {\n // Skip check if page is not in foreground\n if (!this.platform.isInForeground()) {\n return;\n }\n\n if (this.isRefreshing || this.tokenExpiredFlag) return;\n\n if (this.isExpired() && !this.tokenExpiredFlag) {\n this.tokenExpiredFlag = true;\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: true });\n this.stopTokenCheck();\n }\n }, this.CHECK_INTERVAL);\n }\n\n private setupLifecycleListeners(): void {\n // Clean up existing listeners before setting up new ones\n if (this.unsubscribeForeground) {\n this.unsubscribeForeground();\n this.unsubscribeForeground = null;\n }\n\n this.unsubscribeForeground = this.platform.onForeground(() => {\n if (this.checkInterval && !this.isRefreshing && !this.tokenExpiredFlag && this.isExpired()) {\n this.tokenExpiredFlag = true;\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: true });\n this.stopTokenCheck();\n }\n });\n\n if (!this.unsubscribeUnload) {\n this.unsubscribeUnload = this.platform.onBeforeUnload(() => {\n this.destroy();\n });\n }\n }\n\n private stopTokenCheck() {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n\n if (this.unsubscribeForeground) {\n this.unsubscribeForeground();\n this.unsubscribeForeground = null;\n }\n }\n\n /**\n * Cleanup method to stop all intervals and remove event listeners.\n * Should be called when the service is no longer needed.\n */\n destroy() {\n this.stopTokenCheck();\n if (this.unsubscribeUnload) {\n this.unsubscribeUnload();\n this.unsubscribeUnload = null;\n }\n }\n\n setTokensCache(tokens: Tokens | undefined): void {\n this.tokensCache = tokens;\n\n // Cookie mode: Only ID token may be available (access/refresh tokens in HttpOnly cookies)\n // JSON mode: All tokens expected\n // Handle partial data gracefully for cookie mode\n if (tokens) {\n // Parse available tokens (some may be undefined in cookie mode)\n this.parsedTokensCache = {\n access_token: tokens.access_token ? parseToken(tokens.access_token) : undefined,\n id_token: tokens.id_token ? parseToken(tokens.id_token) : undefined,\n refresh_token: tokens.refresh_token ? parseToken(tokens.refresh_token) : undefined,\n scopes: tokens.scopes,\n };\n } else {\n this.parsedTokensCache = undefined;\n }\n }\n\n getTokens() {\n return this.tokensCache;\n }\n\n async getTokensWithRefresh() {\n try {\n if (!this.tokensCache) return this.tokensCache;\n\n // Cookie mode: access_token may not be available (in HttpOnly cookie)\n // In cookie mode, we cannot check expiry client-side, so just return tokens\n // Server will handle refresh via 401 response and axios interceptor\n if (!this.tokensCache.access_token) {\n return this.tokensCache;\n }\n\n // JSON mode: check and refresh if needed\n const access = parseToken(this.tokensCache.access_token);\n\n if (isTokenExpired(access) && !this.tokenExpiredFlag) {\n await this.refreshTokensCache(this.tokensCache);\n return this.tokensCache;\n } else {\n return this.tokensCache;\n }\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return undefined;\n }\n }\n\n getParsedTokens() {\n return this.parsedTokensCache;\n }\n\n isExpired() {\n if (!this.tokensCache) return true;\n\n // Cookie mode: access_token may not be available (in HttpOnly cookie)\n // In cookie mode, we can't check expiry client-side, so return false\n // and let the server respond with 401 when the cookie expires\n if (!this.tokensCache.access_token) {\n return false; // Assume valid in cookie mode, server will validate\n }\n\n const access = parseToken(this.tokensCache.access_token);\n return isTokenExpired(access);\n }\n}\n","import {\n RegisteredTwoFactorMethod,\n TwoFactorAlternativeRequest,\n TwoFactorChallengeRequest,\n TwoFactorChallengeResponse,\n TwoFactorConfirmResponse,\n TwoFactorDisableResponse,\n TwoFactorMethod,\n TwoFactorRecoveryResponse,\n TwoFactorRegenerateResponse,\n TwoFactorSetupMagicLinkSession,\n TwoFactorSetupMagicLinkValidationResponse,\n TwoFactorSetupResponse,\n TwoFactorStatusResponse,\n TwoFactorVerifyRequestV2,\n TwoFactorVerifyResponse,\n TwoFactorVerifyResponseV2,\n} from '../api/model';\nimport { TwoFactorApiClient } from '../api/two-factor';\nimport { PassflowEvent, PassflowStore } from '../store';\nimport { isValidTOTPCode, normalizeRecoveryCode } from '../utils/validation';\n\n/**\n * Partial auth state for 2FA verification flow\n */\ninterface PartialAuthState {\n email?: string;\n challengeId?: string;\n tfaToken?: string;\n timestamp: number;\n expiresAt: number;\n}\n\n/** Payload for TwoFactorRequired event */\ninterface TwoFactorRequiredPayload {\n email: string;\n challengeId: string;\n tfaToken: string;\n}\n\n/** Error with optional id field */\ninterface ErrorWithId extends Error {\n id?: string;\n}\n\n/**\n * Service for managing Two-Factor Authentication\n */\nexport class TwoFactorService {\n private partialAuthState?: PartialAuthState;\n private readonly PARTIAL_AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes timeout\n private readonly SESSION_STORAGE_KEY = 'passflow_2fa_challenge';\n\n // TOTP digit configuration (6 or 8 digits)\n // This field is used throughout the service for TOTP validation and API responses\n private totpDigits: 6 | 8 = 6; // Default to 6 for backward compatibility\n\n // Magic link session storage (in-memory only for security - no persistence)\n private magicLinkSession?: TwoFactorSetupMagicLinkSession;\n\n constructor(\n private twoFactorApi: TwoFactorApiClient,\n private subscribeStore: PassflowStore,\n ) {\n // Listen for TwoFactorRequired event from AuthService\n // Create a subscriber that handles the TwoFactorRequired event\n const eventSubscriber = {\n onAuthChange: (event: PassflowEvent, payload?: unknown) => {\n if (event === PassflowEvent.TwoFactorRequired) {\n const tfPayload = payload as TwoFactorRequiredPayload;\n this.setPartialAuthState(tfPayload.email, tfPayload.challengeId, tfPayload.tfaToken);\n }\n },\n };\n\n this.subscribeStore.subscribe(eventSubscriber, [PassflowEvent.TwoFactorRequired]);\n }\n\n /**\n * Emit error event and throw the error\n * Helper method to ensure errors are properly emitted to subscribers\n */\n private emitErrorAndThrow(error: unknown, context: string): never {\n const errorWithId = error as ErrorWithId;\n const errorPayload = {\n message: error instanceof Error ? error.message : `${context} failed`,\n originalError: error,\n code: errorWithId?.id || undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n /**\n * Get 2FA enrollment status for current user\n */\n async getStatus(): Promise<TwoFactorStatusResponse> {\n try {\n const response = await this.twoFactorApi.getStatus();\n // Store totp_digits from backend response\n if (response.totp_digits) {\n this.totpDigits = response.totp_digits;\n }\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get 2FA status');\n }\n }\n\n /**\n * Begin 2FA setup process\n * Returns secret and QR code for authenticator app\n */\n async beginSetup(): Promise<TwoFactorSetupResponse> {\n try {\n const response = await this.twoFactorApi.beginSetup();\n // Store totp_digits from backend response\n if (response.totp_digits) {\n this.totpDigits = response.totp_digits;\n }\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupStarted, { secret: response.secret });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Begin 2FA setup');\n }\n }\n\n /**\n * Confirm 2FA setup with TOTP code\n * Returns recovery codes that MUST be displayed to user\n */\n async confirmSetup(code: string): Promise<TwoFactorConfirmResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n try {\n const response = await this.twoFactorApi.confirmSetup({ code });\n\n // Emit event with recovery codes and cleanup callback\n // The clearRecoveryCodes callback modifies the original response.recovery_codes array\n this.subscribeStore.notify(PassflowEvent.TwoFactorEnabled, {\n recoveryCodes: response.recovery_codes,\n clearRecoveryCodes: () => {\n // Clear the array in place to ensure all references are cleared\n response.recovery_codes.length = 0;\n },\n });\n\n // Return response with recovery codes intact\n // Apps must handle secure storage/display of codes\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Confirm 2FA setup');\n }\n }\n\n /**\n * Verify TOTP code during login\n * Completes authentication if successful\n */\n async verify(code: string): Promise<TwoFactorVerifyResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n // Attempt to recover partial auth state from sessionStorage\n this.recoverPartialAuthState();\n\n if (!this.isVerificationRequired()) {\n throw new Error('2FA verification expired or not required. User must sign in first.');\n }\n\n // Validate that tfa_token exists (now required)\n if (!this.partialAuthState?.tfaToken) {\n throw new Error('No TFA token found. User must sign in first.');\n }\n\n try {\n const response = await this.twoFactorApi.verify({\n code,\n tfa_token: this.partialAuthState.tfaToken,\n });\n\n // Clear partial auth state (includes sessionStorage)\n this.clearPartialAuthState();\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorVerified, { tokens: response });\n\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Verify 2FA code');\n }\n }\n\n /**\n * Use recovery code for authentication\n * Completes authentication if successful\n */\n async useRecoveryCode(code: string): Promise<TwoFactorRecoveryResponse> {\n try {\n // Normalize and validate recovery code format\n const normalizedCode = normalizeRecoveryCode(code);\n if (!normalizedCode) {\n throw new Error('Invalid recovery code format. Expected format: XXXX-XXXX or XXXXXXXX (alphanumeric).');\n }\n\n // Attempt to recover partial auth state from sessionStorage\n this.recoverPartialAuthState();\n\n if (!this.isVerificationRequired()) {\n throw new Error('2FA verification expired or not required. User must sign in first.');\n }\n\n // Validate that tfa_token exists (now required)\n if (!this.partialAuthState?.tfaToken) {\n throw new Error('No TFA token found. User must sign in first.');\n }\n\n const response = await this.twoFactorApi.useRecoveryCode({\n recovery_code: normalizedCode,\n tfa_token: this.partialAuthState.tfaToken,\n });\n\n // Clear partial auth state (includes sessionStorage)\n this.clearPartialAuthState();\n\n // Check for recovery codes exhaustion\n if (response.remaining_recovery_codes === 0) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorRecoveryCodesExhausted, { tokens: response });\n } else if (response.remaining_recovery_codes <= 2) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorRecoveryCodesLow, {\n tokens: response,\n remainingCodes: response.remaining_recovery_codes,\n });\n }\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorRecoveryUsed, {\n tokens: response,\n remainingCodes: response.remaining_recovery_codes,\n });\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorVerified, { tokens: response });\n\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Use recovery code');\n }\n }\n\n /**\n * Disable 2FA (requires TOTP verification)\n */\n async disable(code: string): Promise<TwoFactorDisableResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n try {\n const response = await this.twoFactorApi.disable({ code });\n this.subscribeStore.notify(PassflowEvent.TwoFactorDisabled, {});\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Disable 2FA');\n }\n }\n\n /**\n * Regenerate recovery codes\n */\n async regenerateRecoveryCodes(code: string): Promise<TwoFactorRegenerateResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n try {\n const response = await this.twoFactorApi.regenerateRecoveryCodes({ code });\n\n // Create a copy of recovery codes for potential event handling\n const codesCopy = [...response.recovery_codes];\n\n // Clear recovery codes from response immediately (security)\n response.recovery_codes = [];\n\n // Note: No event is emitted for regeneration, but we clear codes from response\n // as a security measure. Apps should extract codes synchronously from the return value.\n\n // Restore codes to the copy so they can be returned\n response.recovery_codes = codesCopy;\n\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Regenerate recovery codes');\n }\n }\n\n /**\n * Check if 2FA verification is required (local state check)\n * Returns true if user has signed in but needs 2FA verification\n */\n isVerificationRequired(): boolean {\n // Attempt to recover partial auth state from sessionStorage\n // This is needed when the Passflow instance is recreated (e.g., due to React state changes)\n // but the 2FA challenge data was stored by a previous instance\n this.recoverPartialAuthState();\n\n if (!this.partialAuthState) return false;\n\n // Check if expired\n if (Date.now() > this.partialAuthState.expiresAt) {\n this.clearPartialAuthState();\n return false;\n }\n\n return true;\n }\n\n /**\n * Set partial auth state when login requires 2FA\n * Called internally via event listener when AuthService emits TwoFactorRequired\n */\n setPartialAuthState(email?: string, challengeId?: string, tfaToken?: string): void {\n this.partialAuthState = {\n email,\n challengeId,\n tfaToken,\n timestamp: Date.now(),\n expiresAt: Date.now() + this.PARTIAL_AUTH_TIMEOUT_MS,\n };\n\n // Persist to sessionStorage for page refresh recovery\n // SECURITY NOTE: Storing tfaToken in sessionStorage is acceptable because:\n // 1. The tfaToken is a JWT with short expiration (5 minutes)\n // 2. It cannot be used alone - requires a valid TOTP code (6-digit from authenticator app)\n // 3. It expires after 5 minutes (PARTIAL_AUTH_TIMEOUT_MS and JWT expiration)\n // 4. It's session-scoped (cleared on tab close)\n // 5. Storing it enables better UX (page refresh recovery during 2FA flow)\n // The security boundary is the TOTP code, not the TFA token.\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.setItem(this.SESSION_STORAGE_KEY, JSON.stringify(this.partialAuthState));\n } catch {\n // Ignore sessionStorage errors (e.g., quota exceeded, disabled)\n }\n }\n }\n\n /**\n * Clear partial auth state\n * Called on logout or successful verification\n */\n clearPartialAuthState(): void {\n this.partialAuthState = undefined;\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.removeItem(this.SESSION_STORAGE_KEY);\n } catch {\n // Ignore sessionStorage errors\n }\n }\n }\n\n /**\n * Attempt to recover partial auth state from sessionStorage\n * Called before verification operations to handle page refresh\n */\n private recoverPartialAuthState(): void {\n if (this.partialAuthState) return; // Already in memory\n\n if (typeof sessionStorage === 'undefined') return;\n\n try {\n const storedState = sessionStorage.getItem(this.SESSION_STORAGE_KEY);\n if (!storedState) return;\n\n const parsed = JSON.parse(storedState) as PartialAuthState;\n\n // Check if expired\n if (Date.now() < parsed.expiresAt) {\n this.partialAuthState = parsed;\n } else {\n // Expired - clean up\n sessionStorage.removeItem(this.SESSION_STORAGE_KEY);\n }\n } catch {\n // Invalid data - clean up\n try {\n sessionStorage.removeItem(this.SESSION_STORAGE_KEY);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n // ============================================\n // Magic Link 2FA Setup Methods\n // ============================================\n\n /**\n * Validate magic link token for 2FA setup\n *\n * This method validates an admin-generated magic link token and\n * creates a scoped session (scope: \"2fa_setup\") that can ONLY be\n * used for completing 2FA setup operations.\n *\n * Session characteristics:\n * - Stored in memory only (no persistence across page reloads)\n * - Short-lived (typically 1 hour expiration)\n * - Cannot be refreshed\n * - Cannot be promoted to full authentication\n * - Only valid for 2FA setup endpoints\n *\n * @param token - Magic link token from URL parameter\n * @returns Validation response with session details\n */\n async validateTwoFactorSetupMagicLink(token: string): Promise<TwoFactorSetupMagicLinkValidationResponse> {\n // Call backend validation endpoint (API client handles all error cases)\n const response = await this.twoFactorApi.validateTwoFactorSetupMagicLink(token);\n\n // If validation successful, store session in memory\n if (response.success && response.sessionToken && response.userId) {\n this.magicLinkSession = {\n sessionToken: response.sessionToken,\n userId: response.userId,\n appId: response.appId,\n scope: '2fa_setup',\n timestamp: Date.now(),\n expiresAt: Date.now() + (response.expiresIn || 3600) * 1000,\n };\n\n // Emit success event\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupMagicLinkValidated, {\n userId: response.userId,\n appId: response.appId,\n expiresIn: response.expiresIn || 3600,\n sessionToken: response.sessionToken,\n });\n } else if (response.error) {\n // Emit failure event\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupMagicLinkFailed, {\n error: response.error,\n });\n }\n\n return response;\n }\n\n /**\n * Get current magic link session (if any)\n * Used by React SDK to access session token for API calls\n *\n * @returns Active magic link session or null if none/expired\n */\n getMagicLinkSession(): TwoFactorSetupMagicLinkSession | null {\n if (!this.magicLinkSession) return null;\n\n // Check if expired\n if (Date.now() > this.magicLinkSession.expiresAt) {\n this.clearMagicLinkSession();\n return null;\n }\n\n return this.magicLinkSession;\n }\n\n /**\n * Clear magic link session\n * Called after successful setup completion or on error\n */\n clearMagicLinkSession(): void {\n this.magicLinkSession = undefined;\n }\n\n /**\n * Check if magic link session is active\n * Used by React SDK to determine if form can use magic link auth\n */\n hasMagicLinkSession(): boolean {\n return this.getMagicLinkSession() !== null;\n }\n\n /**\n * Get the session token from magic link session (if active)\n * Used by AxiosClient for injecting auth header on 2FA setup endpoints\n */\n getMagicLinkSessionToken(): string | null {\n const session = this.getMagicLinkSession();\n return session?.sessionToken || null;\n }\n\n /**\n * Get configured TOTP digit count\n * Returns the number of digits (6 or 8) for TOTP codes\n * Useful for UI components that need to render the correct number of input fields\n */\n getTotpDigits(): 6 | 8 {\n return this.totpDigits;\n }\n\n // ============================================\n // v2 Multi-Method 2FA Service Methods\n // ============================================\n\n /**\n * Get available 2FA methods for current user\n */\n async getAvailableMethods(): Promise<TwoFactorMethod[]> {\n try {\n return await this.twoFactorApi.getAvailableMethods();\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get available 2FA methods');\n }\n }\n\n /**\n * Get registered 2FA methods for current user\n */\n async getRegisteredMethods(): Promise<RegisteredTwoFactorMethod[]> {\n try {\n return await this.twoFactorApi.getRegisteredMethods();\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get registered 2FA methods');\n }\n }\n\n /**\n * Start 2FA method setup\n */\n async startMethodSetup(method: TwoFactorMethod, payload?: Record<string, unknown>): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.startMethodSetup(method, payload);\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupStarted, { secret: '', method });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Start 2FA method setup');\n }\n }\n\n /**\n * Complete 2FA method setup\n */\n async completeMethodSetup(method: TwoFactorMethod, payload: Record<string, unknown>): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.completeMethodSetup(method, payload);\n this.subscribeStore.notify(PassflowEvent.TwoFactorEnabled, {\n recoveryCodes: [],\n clearRecoveryCodes: () => {\n // No-op: v2 multi-method setup doesn't return recovery codes\n },\n });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Complete 2FA method setup');\n }\n }\n\n /**\n * Remove registered 2FA method\n */\n async removeMethod(methodId: string): Promise<void> {\n try {\n await this.twoFactorApi.removeMethod(methodId);\n } catch (error) {\n this.emitErrorAndThrow(error, 'Remove 2FA method');\n }\n }\n\n /**\n * Request 2FA challenge during login\n */\n async requestChallenge(payload: TwoFactorChallengeRequest): Promise<TwoFactorChallengeResponse> {\n try {\n const response = await this.twoFactorApi.requestChallenge(payload);\n this.subscribeStore.notify(PassflowEvent.TwoFactorChallengeReceived, {\n challengeId: response.challenge_id,\n method: response.method,\n alternativeMethods: response.alternative_methods,\n });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Request 2FA challenge');\n }\n }\n\n /**\n * Verify 2FA challenge (v2)\n */\n async verifyV2(payload: TwoFactorVerifyRequestV2): Promise<TwoFactorVerifyResponseV2> {\n try {\n const response = await this.twoFactorApi.verifyV2(payload);\n if (response.success) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorVerified, {\n tokens: {\n access_token: response.access_token,\n refresh_token: response.refresh_token,\n },\n });\n if (response.device_trusted) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorDeviceTrusted, {});\n }\n }\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Verify 2FA challenge');\n }\n }\n\n /**\n * Switch to alternative 2FA method during challenge\n */\n async switchToAlternative(payload: TwoFactorAlternativeRequest): Promise<TwoFactorChallengeResponse> {\n try {\n const response = await this.twoFactorApi.switchToAlternative(payload);\n this.subscribeStore.notify(PassflowEvent.TwoFactorMethodSwitched, {\n challengeId: response.challenge_id,\n method: response.method,\n alternativeMethods: response.alternative_methods,\n });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Switch to alternative 2FA method');\n }\n }\n\n /**\n * Get trusted devices\n */\n async getTrustedDevices(): Promise<unknown[]> {\n try {\n return await this.twoFactorApi.getTrustedDevices();\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get trusted devices');\n }\n }\n\n /**\n * Revoke trusted device\n */\n async revokeTrustedDevice(deviceId: string): Promise<void> {\n try {\n await this.twoFactorApi.revokeTrustedDevice(deviceId);\n } catch (error) {\n this.emitErrorAndThrow(error, 'Revoke trusted device');\n }\n }\n}\n","import { PassflowSuccessResponse, UserAPI, getOSFromDeviceType } from '../api';\nimport type { PlatformAdapter } from '../platform';\nimport { webAdapter } from '../platform';\n\n/**\n * Service for managing user profile and passkeys\n */\nexport class UserService {\n constructor(\n private userAPI: UserAPI,\n private deviceService: DeviceService,\n private platform: PlatformAdapter = webAdapter,\n ) {}\n\n /**\n * Get user's registered passkeys\n * @returns Promise with passkeys array\n */\n getUserPasskeys() {\n return this.userAPI.getUserPasskeys();\n }\n\n /**\n * Rename a user passkey\n * @param name The new name for the passkey\n * @param passkeyId The ID of the passkey to rename\n * @returns Promise with success response\n */\n renameUserPasskey(name: string, passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.userAPI.renameUserPasskey(name, passkeyId);\n }\n\n /**\n * Delete a user passkey\n * @param passkeyId The ID of the passkey to delete\n * @returns Promise with success response\n */\n deleteUserPasskey(passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.userAPI.deleteUserPasskey(passkeyId);\n }\n\n /**\n * Add a new passkey for the current user\n * @param options Optional parameters for the passkey\n * @returns Promise that resolves when the passkey is added\n */\n async addUserPasskey({\n relyingPartyId,\n passkeyUsername,\n passkeyDisplayName,\n }: {\n relyingPartyId?: string;\n passkeyUsername?: string;\n passkeyDisplayName?: string;\n } = {}): Promise<void> {\n if (!this.platform.passkeys) throw new Error('Passkeys are not supported on this platform');\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n const { challenge_id, publicKey } = await this.userAPI.addUserPasskeyStart({\n relyingPartyId: (relyingPartyId || this.platform.getCurrentUrl()?.hostname) ?? '',\n deviceId,\n os,\n passkeyDisplayName,\n passkeyUsername,\n });\n // user handle should be base64 encoded for simplewebauthn lib we are using\n publicKey.user.id = this.platform.btoa(publicKey.user.id);\n const webauthn = await this.platform.passkeys.startRegistration(publicKey);\n if (!webauthn) throw new Error('Passkey registration was not completed');\n return await this.userAPI.addUserPasskeyComplete(webauthn, deviceId, challenge_id);\n }\n}\n\n// We need to import DeviceService after the class definition to avoid circular dependency\nimport { DeviceService } from '../device';\n","/**\n * Upgrade factor abstraction.\n *\n * A factor knows two things: whether proving it needs a server round-trip\n * first, and how to turn whatever the user did into the `upgrade.assertion`\n * blob `POST /auth/refresh` expects.\n *\n * `requiresPrepare` is declared statically rather than discovered by trying.\n * The server actively REJECTS a prepare call for one-shot factors\n * (`ErrorUpgradePrepareNotRequired`), so \"call /begin and see what happens\"\n * costs a guaranteed round-trip and a 400 on every TOTP upgrade.\n */\n\nimport type { AuthenticationResponseJSON, PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types';\nimport type { PlatformAdapter } from '../platform';\n\n/** Factor types the upgrade protocol understands, named as `upgrade.type` on the wire. */\nexport type UpgradeFactorType = 'totp' | 'recovery_codes' | 'passkey' | 'email_otp' | 'sms_otp';\n\n/** The prepare round's result — `POST /auth/upgrade/begin`, normalised to camelCase. */\nexport type UpgradePrepared = {\n challengeId: string;\n method: string;\n /** RFC 3339. The server gives a challenge five minutes; treat it as a hard deadline. */\n expiresAt: string;\n /** WebAuthn assertion options, present only for a passkey upgrade. */\n webauthn?: PublicKeyCredentialRequestOptionsJSON;\n /** Masked delivery target (`j***@x.com`), present only for email / SMS OTP. */\n codeSentTo?: string;\n};\n\n/**\n * The `upgrade.assertion` payload. Deliberately an open record: the server\n * types this field as `json.RawMessage` so new factors can extend the shape\n * without a wire-format change.\n */\nexport type UpgradeAssertion = {\n challenge_id?: string;\n code?: string;\n credential?: AuthenticationResponseJSON;\n};\n\n/** What a factor needs from the UI before it can be asserted. */\nexport type UpgradeFactorInput =\n /** A user-typed value: an OTP or a recovery code. */\n | 'code'\n /** Nothing typed — the ceremony itself collects the proof (a passkey prompt). */\n | 'none';\n\nexport type UpgradeAssertContext = {\n /** The value the user typed, for factors whose `input` is `'code'`. */\n code?: string;\n /** The prepare round's result, for factors where `requiresPrepare` is true. */\n prepared?: UpgradePrepared;\n /** Platform seam for the WebAuthn ceremony — keeps non-web adapters working. */\n platform: PlatformAdapter;\n};\n\nexport type UpgradeFactor = {\n type: UpgradeFactorType;\n /** Whether `POST /auth/upgrade/begin` must run before the user can produce proof. */\n requiresPrepare: boolean;\n /** What the UI must collect before `assert` can run. */\n input: UpgradeFactorInput;\n /** Turns the user's response into the assertion blob sent as `upgrade.assertion`. */\n assert(ctx: UpgradeAssertContext): Promise<UpgradeAssertion>;\n};\n\n/** Thrown when a factor cannot produce proof — a cancelled prompt, or missing prerequisites. */\nexport class UpgradeFactorError extends Error {\n readonly factor: UpgradeFactorType;\n /** True when the user dismissed the ceremony rather than failing it. */\n readonly cancelled: boolean;\n\n constructor(factor: UpgradeFactorType, message: string, cancelled = false) {\n super(message);\n this.name = 'UpgradeFactorError';\n this.factor = factor;\n this.cancelled = cancelled;\n }\n}\n\n/** Requires a typed value, rejecting the empty string that an untouched input yields. */\nconst requireCode = (factor: UpgradeFactorType, code: string | undefined): string => {\n const trimmed = code?.trim();\n if (!trimmed) {\n throw new UpgradeFactorError(factor, 'A verification code is required');\n }\n return trimmed;\n};\n\n/**\n * TOTP — the canonical one-shot factor. The code is derived from a shared\n * secret and the clock, so nothing needs to be fetched first.\n */\nexport const totpFactor: UpgradeFactor = {\n type: 'totp',\n requiresPrepare: false,\n input: 'code',\n assert: async ({ code }) => ({ code: requireCode('totp', code) }),\n};\n\n/**\n * Recovery codes — also one-shot. Verified against the user's stored code set,\n * so like TOTP there is no challenge to issue.\n */\nexport const recoveryCodesFactor: UpgradeFactor = {\n type: 'recovery_codes',\n requiresPrepare: false,\n input: 'code',\n assert: async ({ code }) => ({ code: requireCode('recovery_codes', code) }),\n};\n\n/**\n * Passkey — the challenge-response factor. The prepare round supplies the\n * server nonce and, critically, an `allowCredentials` list scoped to the\n * signed-in user; the ceremony signs it and the assertion goes back with the\n * challenge id that binds it to this session.\n */\nexport const passkeyFactor: UpgradeFactor = {\n type: 'passkey',\n requiresPrepare: true,\n input: 'none',\n assert: async ({ prepared, platform }) => {\n if (!prepared?.webauthn) {\n throw new UpgradeFactorError('passkey', 'The upgrade challenge carried no WebAuthn options');\n }\n // `passkeys` is optional on the adapter: a platform without WebAuthn cannot\n // run this ceremony at all. Fail before the prepare round is spent rather\n // than after, so the user can fall back to another factor with the\n // challenge budget intact.\n const passkeys = platform.passkeys;\n if (!passkeys) {\n throw new UpgradeFactorError('passkey', 'This platform does not support passkeys');\n }\n\n let credential: AuthenticationResponseJSON | undefined;\n try {\n credential = await passkeys.startAuthentication(prepared.webauthn);\n } catch (e) {\n // A dismissed or timed-out WebAuthn prompt rejects. That is a cancel, not\n // a failure: the caller should let the user retry or back out, never\n // treat it as a bad credential.\n throw new UpgradeFactorError('passkey', (e as Error)?.message || 'Passkey verification was cancelled', true);\n }\n if (!credential) {\n throw new UpgradeFactorError('passkey', 'Passkey verification was cancelled', true);\n }\n\n return { challenge_id: prepared.challengeId, credential };\n },\n};\n\n/**\n * The factors this SDK can actually complete.\n *\n * `email_otp` and `sms_otp` are intentionally ABSENT. The server routes them\n * (`upgradePrepareMethod` accepts both) but has no OTP transport, so no user can\n * enrol them and `upgradeMethodRegistered` refuses the prepare round. Adding\n * them later is one entry here plus a factor whose assert returns\n * `{ challenge_id, code }` — no change to anything that consumes this registry.\n */\nexport const DEFAULT_UPGRADE_FACTORS: readonly UpgradeFactor[] = Object.freeze([\n totpFactor,\n passkeyFactor,\n recoveryCodesFactor,\n]);\n\n/** Indexes a factor list by type for lookup during a challenge. */\nexport const factorRegistry = (\n factors: readonly UpgradeFactor[] = DEFAULT_UPGRADE_FACTORS,\n): Map<UpgradeFactorType, UpgradeFactor> => new Map(factors.map((f) => [f.type, f]));\n","/**\n * Which factor do we put in front of the user, and in what order?\n *\n * The challenge names what the server wants (`acrValues`, `maxAge`); this\n * module decides which of the SDK's registered factors to offer and which one\n * leads. The first entry is the \"suggested\" method the UI renders immediately\n * — everything after it sits behind \"use another way\" (PFL-509).\n *\n * ## Rank, never filter\n *\n * It is tempting to drop factors that \"cannot reach\" the demanded level. The\n * SDK cannot know that. The server computes `acr` from the *combination* of the\n * session's base methods and the verified factor, so a code factor DOES satisfy\n * an AAL3 challenge when the base session already reaches AAL3 — and for a\n * passkey the level depends on what the assertion reports, which is unknowable\n * until the ceremony runs.\n *\n * Getting that wrong is asymmetric. Offering a factor that turns out to be\n * insufficient costs one round-trip and a retry. Hiding one that would have\n * worked tells the user to re-authenticate when they did not have to, and\n * there is no way back from that. So every factor stays on the list; the\n * ordering carries the opinion, and the server stays the authority.\n *\n * ## Enrolment is filtered upstream, not here\n *\n * `available` is what this SDK can complete, not what the user has. The server\n * refuses a prepare round for an unenrolled factor\n * (`ErrorUpgradeMethodNotRegistered`). Narrowing `available` to the user's\n * actual credentials belongs to the caller — see the note at the bottom.\n */\n\nimport { requiredAcrLevel } from './acr';\nimport type { UpgradeChallenge } from './challenge';\nimport type { UpgradeFactor, UpgradeFactorType } from './factors';\n\n/** The assurance level a challenge demands, when it names one at all. */\nconst AAL3 = 3;\n\n/**\n * Ceremony preference among factors that are equally suitable, lowest first.\n *\n * Passkey leads because it is the cheapest ceremony for the user — one gesture,\n * nothing to read or type, nothing to mistype — and the strongest: it is the\n * only factor that can carry a session to AAL3 on its own, and the only one\n * that is phishing-resistant.\n *\n * Recovery codes sit last on purpose. They verify perfectly well, but each one\n * is single-use and the user's supply is finite and usually inconvenient to\n * reach. Spending one on a routine elevation is a bad trade when a repeatable\n * factor is available, so they are an escape hatch rather than a suggestion.\n */\nconst CEREMONY_ORDER: Record<UpgradeFactorType, number> = {\n passkey: 0,\n totp: 1,\n email_otp: 2,\n sms_otp: 3,\n recovery_codes: 9,\n};\n\n/**\n * The level this challenge demands.\n *\n * Delegates to the ONE shared recognizer in `acr.ts` — the same one\n * `challenge.ts` uses — so there is a single answer in this package to \"what\n * level does this `acr_values` string demand\". The local `Number.parseInt` list\n * reduction this replaced read `\"2gold\"` as level 2, which contradicted the rule\n * that opaque values do not participate.\n *\n * A merged challenge already carries the cross-channel bar in `requiredLevel`,\n * so that is preferred when present: re-deriving it from `acrValues` alone would\n * drop the other channel's contribution and could rank for a weaker bar than\n * the one the prompt is showing.\n *\n * Returns undefined when the challenge names no level (a `max_age`-only\n * freshness challenge), in which case any factor re-proves recency equally.\n */\nconst requiredLevel = (challenge: UpgradeChallenge): number | undefined =>\n challenge.requiredLevel ?? requiredAcrLevel(challenge.acrValues);\n\n/**\n * Ranks a factor against the demanded level. Lower sorts first.\n *\n * Only one rule applies beyond ceremony preference: when the challenge demands\n * AAL3, a factor that cannot reach it unaided is demoted out of the suggested\n * slot. It is still offered — the base session may carry the level — but it\n * should not be the thing the user is shown first, because for most sessions it\n * will not be enough.\n */\nconst rank = (factor: UpgradeFactor, demanded: number | undefined): number => {\n const ceremony = CEREMONY_ORDER[factor.type] ?? 5;\n const needsAAL3 = demanded !== undefined && demanded >= AAL3;\n // `psk` + `hwk` + `user` is the only marker set ComputeACR scores as AAL3,\n // and only a passkey ceremony can report it.\n const reachesAAL3Unaided = factor.type === 'passkey';\n const demoted = needsAAL3 && !reachesAAL3Unaided ? 100 : 0;\n return demoted + ceremony;\n};\n\n/**\n * Orders the factors to offer for a challenge. Index 0 is the suggested\n * method; the rest are the \"use another way\" alternatives.\n *\n * The suggested slot is not only a UX choice: PFL-510 treats a non-suggested\n * choice as a minor risk signal, so whatever leads here becomes the behavioural\n * baseline deviation is measured against.\n *\n * @param available - Factors this SDK can complete. Narrow to the user's\n * enrolled credentials before calling, when known.\n * @param challenge - What the server demanded (`acrValues`, `maxAge`).\n * @returns The factors to offer, most-preferred first. Empty only when\n * `available` was empty — this never drops a factor of its own accord.\n */\nexport const selectFactors = (available: readonly UpgradeFactor[], challenge: UpgradeChallenge): readonly UpgradeFactor[] => {\n const demanded = requiredLevel(challenge);\n // Sort a copy: `available` may be the caller's registry, and a stable sort\n // keeps registration order as the tie-break for equally ranked factors.\n return [...available].sort((a, b) => rank(a, demanded) - rank(b, demanded));\n};\n\n/*\n * TODO(PFL-497): narrow `available` to the user's enrolled factors before this\n * runs. Today the SDK would need two calls — `GET /user/passkey` and\n * `GET /v2/user/2fa/methods` — and would have to reimplement the union rule the\n * server already owns privately in `upgradeMethodRegistered`. That rule is\n * scheduled to change (passkeys move into the method registry), so duplicating\n * it client-side would break on a backend release. Wire this to the single\n * `?purpose=upgrade` discovery endpoint once it exists; until then an\n * unenrolled suggestion costs one refused prepare round.\n */\n","/**\n * Upgrade orchestration — ONE state machine, and it is the single authority for\n * lifecycle.\n *\n * Turns a parsed RFC 9470 challenge into an elevated session: pick a factor,\n * ask the user, run the prepare round if the factor needs one, assert, then\n * commit through `POST /auth/refresh` with the `upgrade` field.\n *\n * The controller owns no UI. It calls out to a prompt handler, which the React\n * layer (or any consumer) implements — that is what keeps the same logic usable\n * from a headless client.\n *\n * ## Why a state machine rather than rules\n *\n * Three review rounds each found two independently-correct lifecycle fixes\n * colliding: fold-versus-single-flight, replay-binding-versus-aggregation, and\n * a prompt outliving the flight whose state had already been cleared. The cause\n * was not any individual rule — it was that lifecycle was specified as rules\n * scattered across four sections, so two of them could each be locally right\n * and jointly impossible. Everything about when state is set, read and cleared\n * lives here, and nothing else writes these fields.\n *\n * ## The states\n *\n * | state | meaning |\n * |--------------|----------------------------------------------------------------|\n * | `idle` | no flight; every field clear; `inFlight === null` |\n * | `collecting` | prompt open, no factor selected yet |\n * | `proving` | a factor is selected; `prepare`/commit on the wire |\n * | `replaying` | proof accepted; waiters replaying their own bound requests |\n * | `settling` | every waiter has reported; the summary is complete |\n * | `done` | prompt settled and every waiter settled; one step from `idle` |\n *\n * ## The decision that shapes all of it\n *\n * **An elevation ends when every waiting request has been retried and settled.**\n * Proof acceptance does NOT end the flight. The previous implementation cleared\n * `inFlight` in a `.finally` on proof completion, and that `.finally` was a real\n * window: a prompt on screen with `inFlight === null`, in which an unrelated\n * challenged request opened a SECOND modal and a waiter could report into a\n * controller that had already forgotten its flight. Clearing happens in one\n * synchronous step, after the prompt has settled, and nowhere else.\n *\n * Two costs the design accepts for that: the prompt stays open for one extra\n * round trip while the replays settle, and a genuinely new challenge arriving in\n * that window queues behind the flight rather than opening a second prompt.\n */\n\nimport type { AssuranceBar, UpgradeChallenge } from './challenge';\nimport { UpgradeInsufficientError, UpgradeSessionChangedError, UpgradeTransportError, UpgradeUnavailableError } from './errors';\nimport {\n DEFAULT_UPGRADE_FACTORS,\n type UpgradeAssertion,\n type UpgradeFactor,\n UpgradeFactorError,\n type UpgradeFactorType,\n type UpgradePrepared,\n factorRegistry,\n} from './factors';\nimport { selectFactors } from './select';\n\nimport type { PlatformAdapter } from '../platform';\n\n/** What the UI is shown when an upgrade is required. */\nexport type UpgradePromptRequest = {\n /** What the server demanded — the MERGED requirement, when several challenges joined. */\n challenge: UpgradeChallenge;\n /** Usable factors, already ordered — the suggested one first. */\n factors: readonly UpgradeFactor[];\n /** Failure from the previous attempt, when the user is retrying. */\n previousError?: Error;\n /**\n * Called the moment the user picks a factor and BEFORE any answer is\n * submitted. REQUIRED of every prompt implementation — the SDK's own modal,\n * the `useUpgrade` hook and a render-prop override alike.\n *\n * `selected` is defined off this call and off nothing else. Deriving it from\n * `prompt` resolving would implement the SUBMISSION boundary instead: the\n * shipped `UpgradePromptResult` resolves with the code already collected, so\n * `selected` would stay false for the whole interval in which the user has\n * picked TOTP and is typing six digits — and a stronger challenge arriving\n * then would re-rank the list under their fingers, the exact UI jump the\n * selection boundary exists to prevent.\n *\n * An override that never calls it leaves the controller in `collecting` for\n * the whole ceremony, so a late join re-ranks mid-entry: degraded, not silent,\n * because `onRequirementRaised` still fires and the re-rank is observable.\n */\n onFactorSelected: (factor: UpgradeFactorType) => void;\n /**\n * The channel by which the controller pushes a raised bar and a re-ranked list\n * into an OPEN prompt, which updates without resolving. Only ever used before\n * a factor is selected (branch 2). The one-answer resolution contract is\n * unchanged.\n *\n * A prompt SUBSCRIBES by assigning its own handler to this field on the\n * request object it was handed, synchronously, before it awaits anything:\n *\n * ```ts\n * const prompt: UpgradePromptHandler = (request) => {\n * request.onRequirementRaised = (next) => setView(next);\n * return collectAnswer(request);\n * };\n * ```\n *\n * It is a slot rather than a controller-supplied function because the\n * direction is controller → prompt: only the prompt knows how to re-render\n * itself. An override that leaves it unset shows the old bar, and the raised\n * bar then surfaces as a re-challenged replay reporting `insufficient` —\n * degraded, never silent.\n */\n onRequirementRaised?: (next: { challenge: UpgradeChallenge; factors: readonly UpgradeFactor[] }) => void;\n};\n\n/** The user's answer to an upgrade prompt. */\nexport type UpgradePromptResult = { type: 'submit'; factor: UpgradeFactorType; code?: string } | { type: 'cancel' };\n\n/** Renders the upgrade UI and resolves once the user submits or backs out. */\nexport type UpgradePromptHandler = (request: UpgradePromptRequest) => Promise<UpgradePromptResult>;\n\n/** Transport seam — the controller never touches axios directly, so it stays testable. */\nexport type UpgradeTransport = {\n /**\n * `POST /auth/upgrade/start`. Only called for factors whose `requiresPrepare`\n * is true.\n *\n * Resolves `undefined` when no prepare round was needed after all — a one-shot\n * factor, or a server that answers `prepare_not_required` because its view of\n * the factor differs from ours. Both are recoveries: the factor then asserts\n * with no `prepared`, which is already the TOTP path.\n */\n prepare(method: UpgradeFactorType): Promise<UpgradePrepared | undefined>;\n /** `POST /auth/refresh` carrying `upgrade`. Resolves once elevated tokens are stored. */\n commit(factor: UpgradeFactorType, assertion: UpgradeAssertion): Promise<void>;\n};\n\n/** The six states. The controller is always in exactly one. */\nexport type UpgradeFlightState = 'idle' | 'collecting' | 'proving' | 'replaying' | 'settling' | 'done';\n\n/**\n * Who is waiting on this elevation.\n *\n * A `request` waiter owns a replay bound to its own URL and method. An\n * `imperative` waiter has nothing to replay, but when its caller STATED a bar\n * it still has a locally checkable requirement — which is why `requested`\n * exists, and why an AAL3 imperative call joining an AAL2 flight can no longer\n * be reported as `elevated`.\n */\nexport type Waiter = { kind: 'request'; url: string; method: string } | { kind: 'imperative'; requested?: AssuranceBar };\n\n/** A waiter's terminal classification. `pending` until it reports. */\nexport type WaiterOutcome = 'pending' | 'replayed' | 'insufficient' | 'failed';\n\n/** What a waiter reports once its own replay has settled. */\nexport type WaiterReport = {\n outcome: 'replayed' | 'insufficient' | 'failed';\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n};\n\n/**\n * A registered waiter.\n *\n * `report()` is the declared inbound edge: classification happens in the\n * adapter, after `elevate()` resolved, and this is the channel by which it\n * reaches the controller. Because `waiters` is cleared in the final transition\n * rather than on proof completion, the entries are still alive when the summary\n * reads them.\n */\nexport type WaiterEntry = Waiter & {\n outcome: WaiterOutcome;\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n /** Called exactly once. Idempotent: later calls are ignored. */\n report(result: WaiterReport): void;\n};\n\n/** The proof's own outcome, which is NOT any operation's outcome. */\nexport type UpgradeProof = { status: 'proven'; obtained: AssuranceBar } | { status: 'failed'; reason: Error };\n\n/** What a successful `elevate()` hands back to its caller. */\nexport type UpgradeElevation = {\n /** This caller's own registered waiter. `report()` is its edge back. */\n waiter: WaiterEntry;\n /** The bar the proof reached, as far as the client can know it. */\n obtained: AssuranceBar;\n};\n\n/** One row of the per-flight outcome summary. */\nexport type UpgradeWaiterSummary = {\n url?: string;\n method?: string;\n outcome: WaiterOutcome;\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n};\n\n/**\n * The per-flight outcome summary: a pure function of `waiters` plus `proof`.\n *\n * `terminal` is what the prompt may report. The modal must NOT say \"success\"\n * while any waiter is re-challenged — for AAL3 endpoints with TOTP-only users\n * that is the NORMAL path, not an edge case, and a design cannot both claim the\n * server is the authority and delete the path by which its second refusal\n * reaches the user.\n */\nexport type UpgradeFlightSummary = {\n proof: UpgradeProof;\n waiters: UpgradeWaiterSummary[];\n terminal: 'success' | 'insufficient' | 'failed';\n /** The bars of the STRONGEST unmet waiter, so the message is honest. */\n insufficient?: { obtained: AssuranceBar; required: AssuranceBar };\n /** The proof failure, when the flight never produced a proof. */\n error?: Error;\n};\n\nexport type UpgradeControllerOptions = {\n transport: UpgradeTransport;\n prompt: UpgradePromptHandler;\n platform: PlatformAdapter;\n /** Defaults to the factors this SDK can complete. */\n factors?: readonly UpgradeFactor[];\n /** How many times the user may answer wrongly before the elevation gives up. */\n maxAttempts?: number;\n /**\n * The bar the elevation actually reached, when the host can read it — from the\n * `acr` claim of the freshly stored access token, for instance.\n *\n * Without it the controller reports the bar the ceremony was RANKED for, which\n * is the honest client-side approximation: the server scores `acr` from the\n * base session plus the verified factor and is the only authority, and a\n * replay that is re-challenged corrects the approximation immediately.\n */\n readObtainedBar?: () => AssuranceBar | undefined;\n /**\n * Called once per flight, in the settling transition, after every waiter has\n * reported. This is how the open prompt is given its terminal state, and how\n * the React layer gets exactly one terminal state per flight.\n */\n onFlightSettled?: (summary: UpgradeFlightSummary) => void;\n /**\n * Liveness guard: how long to wait for waiters to report before settling the\n * flight anyway. A waiter that never calls `report()` is an adapter bug, and\n * without a bound it would hold `inFlight` open forever — so every later\n * elevation would join a dead flight, which is a worse failure than settling\n * early. Defaults to 30s.\n */\n replaySettleTimeoutMs?: number;\n};\n\n/**\n * Raised when the user dismissed the upgrade rather than failing it.\n *\n * `cause` is carried for one case only: a prompt implementation that threw or\n * rejected is treated as a failed COLLECTION and settles on the cancellation\n * contract, because there is no answer and no proof — but the thrown error is\n * the only evidence of an integration bug, so it travels rather than being\n * swallowed.\n */\nexport class UpgradeCancelledError extends Error {\n readonly code = 'upgrade_cancelled' as const;\n /** Declared here rather than inherited: `Error.cause` is ES2022, this build targets ES2021 libs. */\n readonly cause?: unknown;\n\n constructor(cause?: unknown) {\n super('Upgrade authentication was cancelled');\n this.name = 'UpgradeCancelledError';\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n\n/** Raised when no registered factor can satisfy the challenge. */\nexport class UpgradeUnsatisfiableError extends Error {\n readonly code = 'upgrade_unsatisfiable' as const;\n\n constructor(message = 'No available authentication factor can satisfy this challenge') {\n super(message);\n this.name = 'UpgradeUnsatisfiableError';\n }\n}\n\n/**\n * The server writes `Attempts: 0, MaxAttempts: 5` on every NEWLY CREATED\n * challenge (`upgrade.go:177-180`), so the budget is per challenge, not per\n * session: a later sequential elevation gets its own and consumes nothing from\n * this one. Four local attempts therefore stop one short of the server's five,\n * which keeps the SDK from being the thing that burns the budget while leaving a\n * user who mistypes room to recover.\n */\nconst DEFAULT_MAX_ATTEMPTS = 4;\n\n/** Default liveness bound on waiter reports. See `replaySettleTimeoutMs`. */\nconst DEFAULT_SETTLE_TIMEOUT_MS = 30_000;\n\n/** A promise with its resolvers, which is all `elevate`'s per-waiter settlement needs. */\ntype Deferred<T> = { promise: Promise<T>; resolve: (value: T) => void; reject: (reason: unknown) => void };\n\nconst deferred = <T>(): Deferred<T> => {\n let resolve: (value: T) => void = () => undefined;\n let reject: (reason: unknown) => void = () => undefined;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n};\n\n/** A registered waiter plus the controller's own bookkeeping for it. */\ntype Registration = {\n entry: WaiterEntry;\n /** The bar THIS waiter arrived demanding. Checked against the proof. */\n demanded: AssuranceBar;\n /**\n * True only when the CALLER stated a bar on an imperative call.\n *\n * §13.3's `n/a` cell is the options-LESS imperative call: it states no bar and\n * has no protected operation, so it can never report `insufficient` (C24b).\n * Deriving \"stated\" from `demanded` is impossible once `demanded` falls back\n * to the challenge's own bar, and with `barMeets` no longer fail-open on an\n * unknown level that fallback would have started rejecting exactly the call\n * the spec says never rejects.\n */\n statedBar: boolean;\n settlement: Deferred<UpgradeElevation>;\n reported: boolean;\n};\n\n/** The bar a challenge demands, in the shape the errors and the hook share. */\nexport const barOf = (challenge: UpgradeChallenge): AssuranceBar => ({\n acrValues: challenge.acrValues,\n level: challenge.requiredLevel,\n maxAge: challenge.maxAge,\n});\n\n/**\n * Does `obtained` reach `required`?\n *\n * Only the `acr` axis is decidable client-side. A completed proof always\n * satisfies a freshness demand, since it just happened, and a requirement with\n * no level constrains nothing on this axis — so both of those are `true`.\n *\n * **An UNKNOWN obtained level does not meet a bar.** This is the one place in\n * the feature where fail-open is wrong, and §13.4 revision 6 draws the line\n * explicitly: fail-open governs *recovery* — availability, not logging the user\n * out, not blocking the app — never the *granting of access*. Answering \"meets\"\n * for a level we could not read is granting access on no evidence.\n *\n * It is NOT pre-judging satisfiability, and the difference is what §13.2 line\n * 1599 and §13.4 line 2213 protect: nothing is refused outright, no elevation\n * is declined, no factor is filtered. The SDK merely declines to ASSERT that a\n * bar was reached; the server's replay re-challenge decides, and that surfaces\n * as §13.3's `insufficient` with both bars attached. The only waiters this can\n * settle locally are imperative ones that STATED a bar, which have no replay to\n * ask the server about.\n */\nexport const barMeets = (obtained: AssuranceBar, required: AssuranceBar): boolean => {\n if (required.level === undefined) {\n return true;\n }\n if (obtained.level === undefined) {\n return false;\n }\n return obtained.level >= required.level;\n};\n\n/** Field-wise merge of two requirements: `acr` tightens up, freshness tightens down. */\nexport const mergeChallenges = (current: UpgradeChallenge, incoming: UpgradeChallenge): UpgradeChallenge => {\n const level =\n current.requiredLevel === undefined\n ? incoming.requiredLevel\n : incoming.requiredLevel === undefined\n ? current.requiredLevel\n : Math.max(current.requiredLevel, incoming.requiredLevel);\n\n // The raw list follows the bar that won, so a diagnostic never shows a string\n // that did not set the requirement.\n const acrValues =\n level === undefined\n ? (current.acrValues ?? incoming.acrValues)\n : level === current.requiredLevel\n ? current.acrValues\n : incoming.acrValues;\n\n const maxAge =\n current.maxAge === undefined\n ? incoming.maxAge\n : incoming.maxAge === undefined\n ? current.maxAge\n : Math.min(current.maxAge, incoming.maxAge);\n\n const channels = Array.from(new Set([...(current.channels ?? []), ...(incoming.channels ?? [])]));\n\n return {\n acrValues,\n requiredLevel: level,\n maxAge,\n status: current.status,\n ...(channels.length > 0 ? { channels } : {}),\n };\n};\n\n/** Is `incoming` STRICTLY stronger than `current` on either axis? */\nconst isStrictlyStronger = (current: UpgradeChallenge, incoming: UpgradeChallenge): boolean => {\n const acrRose =\n incoming.requiredLevel !== undefined &&\n (current.requiredLevel === undefined || incoming.requiredLevel > current.requiredLevel);\n const freshnessTightened =\n incoming.maxAge !== undefined && (current.maxAge === undefined || incoming.maxAge < current.maxAge);\n return acrRose || freshnessTightened;\n};\n\nexport class UpgradeController {\n private readonly transport: UpgradeTransport;\n private readonly prompt: UpgradePromptHandler;\n private readonly platform: PlatformAdapter;\n private readonly registry: Map<UpgradeFactorType, UpgradeFactor>;\n private readonly available: readonly UpgradeFactor[];\n private readonly maxAttempts: number;\n private readonly readObtainedBar?: () => AssuranceBar | undefined;\n private readonly onFlightSettled?: (summary: UpgradeFlightSummary) => void;\n private readonly settleTimeoutMs: number;\n\n /** The current state. Public for diagnostics and tests; nothing outside writes it. */\n private state: UpgradeFlightState = 'idle';\n\n /**\n * The elevation currently running.\n *\n * Single-flight is the STATE SET, not a flag: `elevate` returns the existing\n * flight in every state but `idle`, and starts one only from `idle`. A screen\n * that fires several protected requests at once gets several challenges, and\n * every one of them must land on ONE prompt — otherwise the user sees a stack\n * of modals and every extra elevation burns a challenge for nothing.\n */\n private inFlight: Promise<void> | null = null;\n\n /** The merged requirement. Written at registration and at every join. */\n private requirement: UpgradeChallenge | null = null;\n\n /** Non-null from the moment the prompt reports a selection, and from nowhere else. */\n private selected: UpgradeFactorType | null = null;\n\n /** The requirement in force when the factor was selected — what the ceremony was ranked for. */\n private rankedRequirement: UpgradeChallenge | null = null;\n\n private registrations: Registration[] = [];\n\n private proof: UpgradeProof | null = null;\n\n /** Factors the SERVER refused for this user, dropped for the duration of this flight only. */\n private pruned = new Set<UpgradeFactorType>();\n\n /** The request handed to the open prompt. Its `onRequirementRaised` slot is branch 2's channel. */\n private openPrompt: UpgradePromptRequest | null = null;\n\n /** Set while `awaitReports` is waiting; called by the reporting path. See H7. */\n private reportsSettled?: () => void;\n\n constructor(options: UpgradeControllerOptions) {\n this.transport = options.transport;\n this.prompt = options.prompt;\n this.platform = options.platform;\n this.available = options.factors ?? DEFAULT_UPGRADE_FACTORS;\n this.registry = factorRegistry(this.available);\n this.maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n this.readObtainedBar = options.readObtainedBar;\n this.onFlightSettled = options.onFlightSettled;\n this.settleTimeoutMs = options.replaySettleTimeoutMs ?? DEFAULT_SETTLE_TIMEOUT_MS;\n }\n\n /** The current lifecycle state. Read-only. */\n get currentState(): UpgradeFlightState {\n return this.state;\n }\n\n /** True while a flight is open — from the first challenge until the last waiter settled. */\n get active(): boolean {\n return this.inFlight !== null;\n }\n\n /**\n * Elevates the session so the challenged request can be retried.\n *\n * Every call registers a waiter, including the imperative one. Concurrent\n * callers share one elevation and one prompt; the returned promise settles for\n * THIS caller when the proof settles, and carries that caller's own waiter\n * entry so it can report its replay's outcome back.\n *\n * It resolves when the server accepted a factor and recorded the elevation. It\n * rejects for cancelled, exhausted, unsatisfiable, unavailable, session-changed\n * and non-retryable transport failure — and, for a waiter whose own demanded\n * bar the proof did not reach, with `UpgradeInsufficientError`.\n */\n elevate(challenge: UpgradeChallenge, waiter: Waiter = { kind: 'imperative' }): Promise<UpgradeElevation> {\n const registration = this.register(challenge, waiter);\n\n if (this.state === 'idle') {\n // T1 — `inFlight` is created BEFORE the prompt opens, so there is no\n // instant in which a modal is on screen and a second challenge finds the\n // controller idle.\n this.state = 'collecting';\n this.requirement = challenge;\n this.inFlight = this.runFlight();\n } else {\n this.join(challenge);\n // T8 and later: the proof has already settled, so this waiter cannot be\n // released by the flight's own loop — it is settled against the existing\n // proof right here. If the proof meets its bar it replays with the others;\n // if not, it is `insufficient` and its caller's retry drives a fresh,\n // SEQUENTIAL elevation. Either way no second prompt opens and no second\n // server challenge is created.\n const settled = this.proof;\n if (settled?.status === 'proven') {\n this.release(registration, settled.obtained);\n } else if (settled?.status === 'failed') {\n this.receiveReport(registration, { outcome: 'failed' });\n registration.settlement.reject(settled.reason);\n }\n }\n\n return registration.settlement.promise;\n }\n\n /** Registers a waiter and the bar it arrived demanding. */\n private register(challenge: UpgradeChallenge, waiter: Waiter): Registration {\n const statedBar = waiter.kind === 'imperative' && waiter.requested !== undefined;\n const demanded = waiter.kind === 'imperative' && waiter.requested ? waiter.requested : barOf(challenge);\n const registration: Registration = {\n demanded,\n statedBar,\n settlement: deferred<UpgradeElevation>(),\n reported: false,\n entry: {\n ...waiter,\n outcome: 'pending',\n report: (result: WaiterReport) => this.receiveReport(registration, result),\n },\n };\n this.registrations.push(registration);\n return registration;\n }\n\n /**\n * Folds a joining challenge into the running flight.\n *\n * The boundary is factor SELECTION. Before it, nothing is on the wire and\n * there is no race, so a strictly stronger challenge folds in place: the\n * requirement rises, the list is re-ranked for the higher bar, and the open\n * prompt is told. After it, a ceremony is running — a `prepare` round trip, a\n * TOTP commit, a WebAuthn ceremony holding the platform authenticator — and\n * neither interrupting it nor starting a second concurrent flight is\n * acceptable, so it completes at the bar it was ranked for and each waiter's\n * own replay decides its own fate.\n */\n private join(incoming: UpgradeChallenge): void {\n const current = this.requirement;\n if (!current) {\n this.requirement = incoming;\n return;\n }\n\n const stronger = isStrictlyStronger(current, incoming);\n this.requirement = mergeChallenges(current, incoming);\n\n if (!stronger) {\n // Branch 1 — the common case: two guards on one screen demanding the same\n // bar. Nothing observable changes.\n return;\n }\n\n if (this.state === 'collecting' && this.selected === null) {\n // Branch 2 — fold in place. One prompt, one ceremony, one challenge.\n this.openPrompt?.onRequirementRaised?.({ challenge: this.requirement, factors: this.offeredFactors() });\n }\n // Branch 3 (`proving`, `replaying`, `settling`, `done`) — the merged\n // requirement is recorded for diagnostics and for the NEXT elevation, but it\n // does not reshape the ceremony in progress. A TOTP commit cannot become an\n // AAL3 proof by rewriting a requirement, so the honest options were\n // \"interrupt the ceremony\" or \"let it finish and tell the late joiner the\n // truth\", and the second is chosen.\n }\n\n /** The factors to offer right now: ranked for the merged bar, minus what the server refused. */\n private offeredFactors(): readonly UpgradeFactor[] {\n const usable = this.available.filter((factor) => !this.pruned.has(factor.type));\n const challenge = this.requirement;\n return challenge ? selectFactors(usable, challenge) : usable;\n }\n\n /** The waiter's inbound edge. Idempotent — a second call is ignored. */\n private receiveReport(registration: Registration, result: WaiterReport): void {\n if (registration.reported) {\n return;\n }\n registration.reported = true;\n registration.entry.outcome = result.outcome;\n if (result.obtained) {\n registration.entry.obtained = result.obtained;\n }\n if (result.required) {\n registration.entry.required = result.required;\n }\n // The reporting path RESOLVES the wait, rather than a timer discovering it.\n // This is the only edge on which a waiter becomes non-pending, so it is the\n // only place the check belongs.\n this.notifyReportsSettled();\n }\n\n /** Resolves `awaitReports`' promise when the last outstanding report lands. */\n private notifyReportsSettled(): void {\n if (!this.reportsSettled || this.registrations.some((registration) => !registration.reported)) {\n return;\n }\n const settle = this.reportsSettled;\n this.reportsSettled = undefined;\n settle();\n }\n\n /**\n * The flight, start to finish.\n *\n * Nothing here clears state: clearing is one synchronous step at the very end,\n * after the prompt has settled, so nothing can observe a half-cleared\n * controller and no waiter can report into a flight the controller has\n * forgotten.\n */\n private async runFlight(): Promise<void> {\n // `prove()` is written not to throw, and this catch is the backstop that\n // makes \"every path settles\" structural rather than a property of one\n // function. An escaping throw would leave `inFlight` a rejected promise\n // with the state stuck mid-flight: every waiter already registered would\n // hang forever, and every later challenge would join a flight that can\n // never settle. H5's prompt-that-throws was one way in; this closes the\n // class, not the instance.\n let proof: UpgradeProof;\n try {\n proof = await this.prove();\n } catch (e) {\n proof = { status: 'failed', reason: e instanceof Error ? e : new Error(String(e)) };\n }\n this.proof = proof;\n\n if (proof.status === 'failed') {\n // T7 — no replay is EVER performed without a completed proof. A cancelled\n // elevation produced nothing to replay with, and re-issuing the user's\n // mutation after they declined would be the worst possible reading of\n // \"retry\".\n for (const registration of this.registrations) {\n this.receiveReport(registration, { outcome: 'failed' });\n registration.settlement.reject(proof.reason);\n }\n } else {\n // T6 → `replaying`. Each waiter is released to replay its OWN bound\n // request, and whether the proof satisfied that waiter's demand is the\n // SERVER's answer to that replay, never a client-side comparison made\n // here (§13.4). Only an imperative waiter, which has no request to\n // replay, is classified in `release()`.\n this.state = 'replaying';\n for (const registration of this.registrations) {\n this.release(registration, proof.obtained);\n }\n await this.awaitReports();\n }\n\n // T9 → T10 — the summary is complete, the prompt is settled exactly once.\n this.state = 'settling';\n const summary = this.summarise(proof);\n try {\n this.onFlightSettled?.(summary);\n } catch {\n // A consumer's settlement handler must not be able to wedge the flight\n // open: the clearing below is what makes the next call start from `idle`.\n }\n\n // T11 — one synchronous step, no `await` inside it.\n this.state = 'done';\n this.inFlight = null;\n this.requirement = null;\n this.selected = null;\n this.rankedRequirement = null;\n this.registrations = [];\n this.proof = null;\n this.pruned = new Set();\n this.openPrompt = null;\n this.reportsSettled = undefined;\n this.state = 'idle';\n }\n\n /**\n * Settles one waiter against the proof.\n *\n * An imperative waiter has no operation to replay, so it is classified here\n * and nowhere else: with no stated bar it simply succeeded, and with a stated\n * bar the proof is checked against it — which is what stops an AAL3 imperative\n * call that joined an AAL2 flight from resolving as though it had been\n * elevated to AAL3.\n *\n * A REQUEST waiter is never pre-judged here, and §13 is explicit about it\n * twice: \"a demand above 3 is never refused client-side — the SDK does not\n * pre-judge satisfiability (§13.4) — it ranks factors for the highest bar it\n * knows and lets the server's replay re-challenge decide, surfacing as\n * §13.3's `insufficient`\", and \"never refuse outright on the default path\".\n * The reason is not conformance alone: the client-side comparison is the\n * fragile half — ACR ordinals, the minimum-of-list rule — so refusing here\n * can skip a replay the server would have accepted, and it loses the\n * `elevated-insufficient` diagnostic the replay path raises. So the waiter is\n * released whatever the bar comparison says; `handleChallenge` replays, reads\n * the server's answer, and reports `insufficient` if that is what it is.\n */\n private release(registration: Registration, obtained: AssuranceBar): void {\n if (registration.entry.kind === 'imperative') {\n // Only a STATED bar is checked. An options-less imperative call resolves\n // `{status:'elevated'}` and never `insufficient` — §13.3's `n/a` cell.\n if (registration.statedBar && !barMeets(obtained, registration.demanded)) {\n this.receiveReport(registration, { outcome: 'insufficient', obtained, required: registration.demanded });\n registration.settlement.reject(new UpgradeInsufficientError(obtained, registration.demanded));\n return;\n }\n this.receiveReport(registration, { outcome: 'replayed', obtained });\n registration.settlement.resolve({ waiter: registration.entry, obtained });\n return;\n }\n\n registration.settlement.resolve({ waiter: registration.entry, obtained });\n }\n\n /**\n * Waits for every released waiter to report, under the liveness bound.\n *\n * Report-driven, not polled. The previous implementation re-armed a\n * `setTimeout(…, 0)` until the last waiter reported, which burned a timer per\n * event-loop turn for the whole replay window and made the state machine\n * untestable under fake timers — `runAllTimers()` never terminates against a\n * self-re-arming zero-delay timer. `receiveReport` is the one edge on which a\n * waiter stops being pending, so it resolves this promise directly.\n *\n * The deadline stays: a waiter that never reports is an adapter bug, and\n * settling it as `failed` is better than holding `inFlight` open forever,\n * which would make every later elevation join a dead flight.\n */\n private awaitReports(): Promise<void> {\n if (!this.registrations.some((registration) => !registration.reported)) {\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve) => {\n const deadline = setTimeout(() => {\n this.reportsSettled = undefined;\n for (const registration of this.registrations) {\n if (!registration.reported) {\n registration.reported = true;\n registration.entry.outcome = 'failed';\n }\n }\n resolve();\n }, this.settleTimeoutMs);\n\n this.reportsSettled = () => {\n clearTimeout(deadline);\n resolve();\n };\n });\n }\n\n /** The summary: a pure function of the waiters and the proof. */\n private summarise(proof: UpgradeProof): UpgradeFlightSummary {\n const waiters: UpgradeWaiterSummary[] = this.registrations.map((registration) => ({\n ...(registration.entry.kind === 'request' ? { url: registration.entry.url, method: registration.entry.method } : {}),\n outcome: registration.entry.outcome,\n ...(registration.entry.obtained ? { obtained: registration.entry.obtained } : {}),\n ...(registration.entry.required ? { required: registration.entry.required } : {}),\n }));\n\n if (proof.status === 'failed') {\n return { proof, waiters, terminal: 'failed', error: proof.reason };\n }\n\n // The strongest unmet waiter supplies the message, so it reads \"elevated to\n // AAL2; this action requires AAL3\" rather than an arbitrary one of several.\n let strongest: UpgradeWaiterSummary | undefined;\n for (const waiter of waiters) {\n if (waiter.outcome !== 'insufficient') {\n continue;\n }\n const level = waiter.required?.level ?? 0;\n if (!strongest || level > (strongest.required?.level ?? 0)) {\n strongest = waiter;\n }\n }\n\n if (strongest) {\n return {\n proof,\n waiters,\n terminal: 'insufficient',\n insufficient: {\n obtained: strongest.obtained ?? proof.obtained,\n required: strongest.required ?? {},\n },\n };\n }\n\n // A waiter that failed, or that never reported at all, is not a success.\n //\n // `success` is a claim about the OPERATIONS, not about the proof: the proof\n // succeeding is already `proof.status === 'proven'` above. Falling through\n // to `success` here let a proven proof plus a `failed` waiter tell the modal\n // \"Verification complete\" for a request that never completed — and a\n // `pending` waiter means the liveness bound expired with an adapter still\n // owing a report, which is even less of a success.\n if (waiters.some((waiter) => waiter.outcome === 'failed' || waiter.outcome === 'pending')) {\n return { proof, waiters, terminal: 'failed' };\n }\n\n // Every waiter replayed successfully and none stated an unmet bar. A waiter\n // that stated a bar IS a waiter, so \"no waiters → success\" can no longer\n // launder an unmet requested bar into a success message.\n return { proof, waiters, terminal: 'success' };\n }\n\n /**\n * Collects an answer and proves it, re-prompting on every recoverable failure.\n *\n * The prepare round is re-run on every attempt rather than cached: a challenge\n * is single-use and the server consumes it atomically, so a failed attempt has\n * already spent it. Reusing the id would fail with\n * `ErrorUpgradeChallengeInvalid` and look like a bad code to the user.\n */\n private async prove(): Promise<UpgradeProof> {\n let previousError: Error | undefined;\n\n for (let attempt = 0; attempt < this.maxAttempts; attempt++) {\n const factors = this.offeredFactors();\n if (factors.length === 0) {\n // Reachable without any enrolment knowledge: a caller-supplied `factors`\n // option whose intersection with what the SDK can complete is empty, or\n // flight-local pruning that removed the last factor.\n return { status: 'failed', reason: new UpgradeUnsatisfiableError() };\n }\n\n const challenge = this.requirement as UpgradeChallenge;\n\n // A prompt that throws synchronously, or rejects, is a FAILED COLLECTION.\n //\n // There is no answer, so there is nothing to prove and no proof to\n // replay — exactly the shape of a cancellation, which is why it settles\n // on the per-surface cancellation contract (fetch resolves the untouched\n // 401/403, axios and the imperative surface reject\n // `UpgradeCancelledError`, the hook may resolve `null`). Re-prompting\n // would call the same broken implementation again; letting the throw\n // escape left the controller in `collecting` forever, so every later\n // challenge joined a flight that could never settle. The thrown error\n // travels on `cause` so an integration bug is still visible.\n let answer: UpgradePromptResult;\n try {\n answer = await this.collect(challenge, factors, previousError);\n } catch (e) {\n return { status: 'failed', reason: new UpgradeCancelledError(e) };\n }\n\n if (answer.type === 'cancel') {\n return { status: 'failed', reason: new UpgradeCancelledError() };\n }\n\n const factor = this.registry.get(answer.factor);\n if (!factor) {\n previousError = new UpgradeUnsatisfiableError(`Unknown authentication factor: ${answer.factor}`);\n this.state = 'collecting';\n this.selected = null;\n continue;\n }\n\n try {\n await this.proveAndCommit(factor, answer.code);\n const obtained = this.readObtainedBar?.() ?? barOf(this.rankedRequirement ?? challenge);\n return { status: 'proven', obtained };\n } catch (e) {\n // A cancelled ceremony ends the elevation — the user made a choice, and\n // re-prompting them would be nagging, not recovery.\n if (e instanceof UpgradeFactorError && e.cancelled) {\n return { status: 'failed', reason: new UpgradeCancelledError() };\n }\n\n const terminal = this.classifyProofFailure(e);\n if (terminal) {\n return { status: 'failed', reason: terminal };\n }\n\n if (e instanceof UpgradeTransportError && e.kind === 'not_registered') {\n // The server stating a fact about this user, not the SDK guessing one:\n // drop that factor for THIS flight only and re-prompt with the rest.\n // Never persisted, and it does not affect a later flight.\n this.pruned.add(factor.type);\n }\n\n previousError = e as Error;\n // T5 — a recoverable failure returns to `collecting` with the prompt\n // re-shown; `requirement` and the waiters are untouched.\n this.state = 'collecting';\n this.selected = null;\n }\n }\n\n // The local attempt budget is spent, and that fact is TERMINAL.\n //\n // Returning `previousError` returned the last answer's own error, which is\n // usually a `wrong_code` with `retryable === true` — so every surface above\n // read \"ask them again\" for a flight that will never ask again, and the UI\n // re-prompted forever against a budget the server had already closed. The\n // budget being spent is what must travel; the last failure travels on\n // `cause`.\n return {\n status: 'failed',\n reason: new UpgradeTransportError({\n kind: 'attempts_exhausted',\n ...(previousError ? { cause: previousError } : {}),\n }),\n };\n }\n\n /** Opens (or re-opens) the prompt and wires the two prompt → controller edges. */\n private collect(\n challenge: UpgradeChallenge,\n factors: readonly UpgradeFactor[],\n previousError: Error | undefined,\n ): Promise<UpgradePromptResult> {\n const request: UpgradePromptRequest = {\n challenge,\n factors,\n ...(previousError ? { previousError } : {}),\n onFactorSelected: (factor: UpgradeFactorType) => {\n // T3 — the ONLY place `selected` becomes non-null.\n if (this.state === 'collecting') {\n this.state = 'proving';\n }\n this.selected = factor;\n this.rankedRequirement = this.requirement;\n },\n };\n\n // The prompt subscribes by assigning `request.onRequirementRaised`\n // synchronously, so the object is kept rather than the function: branch 2\n // reads the slot at the moment it needs it.\n this.openPrompt = request;\n return this.prompt(request);\n }\n\n /** Maps a proof failure to a terminal reason, or `undefined` when the user may retry. */\n private classifyProofFailure(e: unknown): Error | undefined {\n if (e instanceof UpgradeInsufficientError || e instanceof UpgradeUnavailableError) {\n return e;\n }\n if (e instanceof UpgradeSessionChangedError) {\n return e;\n }\n if (e instanceof UpgradeTransportError) {\n switch (e.kind) {\n case 'unavailable':\n // Wrapped, like `session_changed` below, because §13.3's table names\n // `UpgradeUnavailableError` as the rejection on the two throwing\n // surfaces. The fetch and axios adapters re-mapped this kind in\n // `coreOutcomeToError`, so only the imperative surface — which goes\n // straight to the controller — saw the unwrapped\n // `UpgradeTransportError`, and the two surfaces disagreed.\n return new UpgradeUnavailableError(e.message);\n case 'exhausted':\n case 'attempts_exhausted':\n case 'fatal':\n case 'protocol':\n case 'network':\n return e;\n case 'session_changed':\n return new UpgradeSessionChangedError(e.message);\n default:\n // `wrong_code`, `challenge_spent`, `not_registered`,\n // `prepare_not_required` — the user answers again.\n return undefined;\n }\n }\n return undefined;\n }\n\n /** Runs one full proof for a single factor. */\n private async proveAndCommit(factor: UpgradeFactor, code: string | undefined): Promise<void> {\n const prepared = factor.requiresPrepare ? await this.transport.prepare(factor.type) : undefined;\n const assertion = await factor.assert({ code, prepared, platform: this.platform });\n await this.transport.commit(factor.type, assertion);\n }\n}\n","/**\n * The request snapshot: ONE normalised, immutable description of the request\n * that carried a challenge, captured before dispatch.\n *\n * ## Why this module exists\n *\n * Every authorization decision the upgrade feature makes — is this URL in scope,\n * what does the diagnostic name, what does the replay re-send — must be made on\n * the SAME description of the request, and that description must be the one the\n * platform will actually act on. Three defects in this feature were the same\n * defect from three angles:\n *\n * 1. the scope check hand-parsed the caller's URL *string*, while the browser\n * normalises it before dispatch;\n * 2. the challenge context re-read the caller's live `input` at challenge\n * time, while the replay was pinned to the plan-time URL and body;\n * 3. the replay could re-send a different header set than the one that was\n * examined.\n *\n * So the request is normalised and frozen ONCE, here, and the snapshot is the\n * only thing the scope check, the challenge context, the diagnostics and the\n * replay are given. Nothing downstream holds a live reference to the caller's\n * `Request`, `RequestInit` or axios config.\n *\n * ## The normalisation rule, and why it is the platform's own parser\n *\n * **Resolve with `new URL(raw, base)` and compare `origin` on the result.**\n *\n * WHATWG URL parsing is not a string scan. For a special scheme it converts\n * backslashes to forward slashes and strips ASCII tab (0x09), LF (0x0A) and CR\n * (0x0D) *before* parsing. A scope check that inspects the raw string therefore\n * sees a same-origin relative path where the browser resolves a foreign origin:\n * `'/\\\\evil.example/x'`, `'/\\t/evil.example/x'` and `'\\\\\\\\evil.example/x'` all\n * resolve to `https://evil.example` (§13.4's SF-3, SF-4, SF-7). Each one would\n * attach a step-up-elevated credential to a request leaving for an\n * attacker-controlled host.\n *\n * **The percent-encoded forms are the mirror image, and they are POSITIVES.**\n * `'/%5Cevil.example/x'`, `'/%09/evil.example/x'` and `'/%2F%2Fevil.example/x'`\n * all resolve to the page's OWN origin and must stay in scope (SF-9..SF-12):\n * the parser decodes nothing that could change the authority, so `%5C` is not a\n * backslash, `%09` stays an ordinary path character and `%2F%2F` does not begin\n * an authority. They are legitimate same-origin requests with odd-looking paths.\n *\n * That asymmetry is the entire argument for resolving rather than filtering. A\n * list of rejected characters is a denylist against a parser, and the parser is\n * the authority: it is bypassed by the raw forms, whose bytes it rewrites before\n * it parses them, AND it is wrong in the opposite direction on the encoded\n * forms, which it refuses as hostile when the platform treats them as plain\n * path text. Resolving first gets both directions — and the vectors nobody has\n * thought of yet — for free.\n *\n * The operative consequence for anyone editing this file: hand the input to\n * `new URL` **undecoded and un-normalised**. Any decoding, trimming or\n * rewriting before that call reintroduces one half of the defect or the other.\n *\n * Off the browser there is no document, so there is no \"same origin\" to resolve\n * against. A relative URL is then resolved against a RESERVED sentinel origin\n * (`.invalid` is reserved by RFC 2606 and can never be a real host), which keeps\n * exactly one code path: a URL that resolves to the sentinel's origin is a\n * genuinely relative one, and anything that steers the authority elsewhere —\n * `//evil.example/x` included — resolves away from it and is no longer relative.\n * The sentinel is `https:` precisely because it must be a SPECIAL scheme: the\n * backslash and tab rules above apply only to special schemes, so a custom\n * scheme would be a different parser and would miss the vectors.\n */\n\nimport type { BodyKind } from './replay';\n\n/** One captured header. Lower-cased name, value verbatim. */\nexport type SnapshotHeader = readonly [name: string, value: string];\n\n/**\n * The frozen description of one request.\n *\n * Immutable by construction: `Object.freeze` on the snapshot and on its header\n * list, and the body already captured by value where the shape is mutable. It\n * carries no live reference to anything the caller can still change.\n */\nexport type RequestSnapshot = {\n /** Exactly what the caller passed, for diagnostics only. NEVER for a decision. */\n readonly raw: string;\n /**\n * The absolute, platform-normalised URL the request will actually be sent to,\n * or `raw` when it could not be resolved at all (a malformed URL).\n *\n * This is the value every decision and the replay use.\n */\n readonly href: string;\n /**\n * The URL to actually DISPATCH a replay to.\n *\n * Identical to `href` wherever a real base existed — a browser, or any host\n * that resolved the URL absolutely — which is the case every decision cares\n * about. It falls back to `raw` only when resolution used the reserved\n * sentinel base, i.e. a relative URL on a host with no document: the\n * sentinel's origin is a reserved name that resolves to nothing, so sending\n * `href` there would send the request into a hole. The transport's own base\n * (axios' `baseURL`, the runtime's fetch base) then resolves the captured\n * string, exactly as it did for the original send.\n *\n * It is still a captured STRING, never the caller's object, so the TOCTOU\n * this module exists to close stays closed on that path too.\n */\n readonly dispatchUrl: string;\n /** The resolved origin, or `undefined` when the URL could not be resolved. */\n readonly origin?: string;\n /** The resolved pathname, or the raw path when the URL could not be resolved. */\n readonly pathname: string;\n /** False when the URL could not be parsed even against the base. */\n readonly resolved: boolean;\n /**\n * True when the resolved origin is the base's own — the page's origin on a\n * browser, the sentinel's off it, which means \"genuinely relative\".\n *\n * This is what a same-origin path-prefix pattern (`backend: '/api'`) tests,\n * and it is the one bit that makes `'/\\\\evil.example/x'` fail where `'/api/x'`\n * passes.\n */\n readonly relativeToBase: boolean;\n /** Upper-cased, with the same precedence the transport itself applies. */\n readonly method: string;\n /** The effective header set, captured once. Frozen. */\n readonly headers: readonly SnapshotHeader[];\n /** How the body can be re-sent, which is what decides replayability. */\n readonly bodyKind: BodyKind;\n /**\n * The already-captured body. `undefined` means \"the caller passed no body\n * key\", which is distinct from an explicit `null`.\n */\n readonly body?: unknown;\n};\n\n/**\n * The reserved origin a relative URL is resolved against when there is no\n * document. RFC 2606 reserves `.invalid`, so no real deployment can collide\n * with it, and `https:` keeps the parser on its special-scheme rules.\n */\nexport const RELATIVE_BASE = 'https://passflow-relative.invalid';\n\n/**\n * The base relative URLs resolve against, read at CALL time.\n *\n * `document.baseURI` first, because that — not `location.href` — is what `fetch`\n * and XHR resolve a relative URL against, and a `<base href>` moves it.\n * Reading a global at module scope is the SSR failure this feature avoids\n * everywhere, so this is a function.\n */\nconst resolutionBase = (): string => {\n const scope = globalThis as {\n document?: { baseURI?: unknown };\n location?: { href?: unknown };\n };\n const baseUri = scope.document?.baseURI;\n if (typeof baseUri === 'string' && baseUri !== '') {\n return baseUri;\n }\n const href = scope.location?.href;\n return typeof href === 'string' && href !== '' && href !== 'about:blank' ? href : RELATIVE_BASE;\n};\n\n/** What a URL normalised to. Pure: it takes the base, it does not read one. */\nexport type NormalisedUrl = {\n href: string;\n origin?: string;\n pathname: string;\n resolved: boolean;\n relativeToBase: boolean;\n /** True when no real base existed and the reserved sentinel was used. */\n sentinelBase: boolean;\n};\n\n/**\n * Resolves one URL with the platform's own parser.\n *\n * `base` defaults to the document base (or the sentinel off the browser). The\n * ONLY comparison any caller may make afterwards is on `origin` — never on the\n * raw string.\n */\nexport const normaliseUrl = (raw: string, base: string = resolutionBase()): NormalisedUrl => {\n const sentinelBase = base === RELATIVE_BASE;\n let baseOrigin: string | undefined;\n try {\n baseOrigin = new URL(base).origin;\n } catch {\n baseOrigin = undefined;\n }\n\n let resolved: URL | undefined;\n try {\n resolved = new URL(raw, base);\n } catch {\n resolved = undefined;\n }\n\n if (!resolved) {\n // Unparseable even against a base. It is not relative and it is not any\n // origin, so every origin comparison below fails and the request is out of\n // scope — the conservative reading used throughout this feature.\n const path = raw.split(/[?#]/)[0] ?? raw;\n return { href: raw, pathname: path, resolved: false, relativeToBase: false, sentinelBase };\n }\n\n return {\n href: resolved.href,\n origin: resolved.origin,\n pathname: resolved.pathname,\n resolved: true,\n relativeToBase: baseOrigin !== undefined && resolved.origin === baseOrigin,\n sentinelBase,\n };\n};\n\n/** Lower-cases a header name once, so every later comparison is a plain `===`. */\nconst headerEntry = (name: string, value: unknown): SnapshotHeader | undefined =>\n typeof value === 'string' ? ([name.toLowerCase(), value] as const) : undefined;\n\n/**\n * Captures a header bag by value.\n *\n * Every shape the two transports produce arrives here: a DOM `Headers`, an\n * `AxiosHeaders`, a plain object, an entry array, or an array-valued node bag.\n * Copying is the point — the caller may still hold and mutate the original.\n */\nexport const captureHeaders = (headers: unknown): readonly SnapshotHeader[] => {\n const captured: SnapshotHeader[] = [];\n\n if (headers === undefined || headers === null) {\n return Object.freeze(captured);\n }\n\n // A `Headers` (or anything with the same iteration contract) enumerates\n // lower-cased, already-combined entries.\n const iterable = headers as { forEach?: unknown; entries?: unknown };\n if (typeof iterable.forEach === 'function' && typeof iterable.entries === 'function') {\n (headers as Headers).forEach((value, name) => {\n const entry = headerEntry(name, value);\n if (entry) {\n captured.push(entry);\n }\n });\n return Object.freeze(captured);\n }\n\n if (Array.isArray(headers)) {\n for (const pair of headers as unknown[]) {\n if (Array.isArray(pair) && pair.length >= 2) {\n const entry = headerEntry(String(pair[0]), pair[1]);\n if (entry) {\n captured.push(entry);\n }\n }\n }\n return Object.freeze(captured);\n }\n\n if (typeof headers === 'object') {\n for (const [name, value] of Object.entries(headers as Record<string, unknown>)) {\n // axios' node bag can hold an array per name; join it the way the wire does.\n const flat = Array.isArray(value) ? value.join(', ') : value;\n const entry = headerEntry(name, flat);\n if (entry) {\n captured.push(entry);\n }\n }\n }\n\n return Object.freeze(captured);\n};\n\n/** Reads a captured header. Case is already normalised, so this is exact. */\nexport const snapshotHeader = (snapshot: RequestSnapshot, name: string): string | undefined => {\n const wanted = name.toLowerCase();\n for (const [header, value] of snapshot.headers) {\n if (header === wanted) {\n return value;\n }\n }\n return undefined;\n};\n\n/** What a caller must supply to take a snapshot. Everything else is derived. */\nexport type RequestSnapshotInit = {\n url: string;\n method?: string;\n headers?: unknown;\n bodyKind: BodyKind;\n /** Pass the key only when the caller supplied a body, so `undefined` stays meaningful. */\n body?: unknown;\n /** Overrides the document base. Tests and non-browser hosts only. */\n base?: string;\n};\n\n/**\n * Takes the snapshot. Call it ONCE per request, BEFORE dispatch.\n *\n * Frozen on the way out: a snapshot that could be edited after the scope\n * decision would reintroduce the very TOCTOU this module removes.\n */\nexport const createRequestSnapshot = (init: RequestSnapshotInit): RequestSnapshot => {\n const normalised = init.base === undefined ? normaliseUrl(init.url) : normaliseUrl(init.url, init.base);\n\n const snapshot: RequestSnapshot = {\n raw: init.url,\n href: normalised.href,\n dispatchUrl: normalised.sentinelBase || !normalised.resolved ? init.url : normalised.href,\n ...(normalised.origin === undefined ? {} : { origin: normalised.origin }),\n pathname: normalised.pathname,\n resolved: normalised.resolved,\n relativeToBase: normalised.relativeToBase,\n method: (init.method ?? 'GET').toUpperCase(),\n headers: captureHeaders(init.headers),\n bodyKind: init.bodyKind,\n ...('body' in init ? { body: init.body } : {}),\n };\n\n return Object.freeze(snapshot);\n};\n\n/**\n * Applies the §5.5 credential swap to a captured header set, by EXACT match.\n *\n * Equality with the token we hold is what proves the header was ours; any other\n * value is forwarded untouched, because rewriting a header we do not own would\n * be a security bug. Returns the same frozen array when nothing changed, so the\n * common path allocates nothing.\n */\nexport const swapAuthorization = (\n headers: readonly SnapshotHeader[],\n swap?: { from: string; to: string },\n): readonly SnapshotHeader[] => {\n if (!swap || swap.from === swap.to) {\n return headers;\n }\n let changed = false;\n const next = headers.map<SnapshotHeader>(([name, value]) => {\n if (name === 'authorization' && value === swap.from) {\n changed = true;\n return [name, swap.to] as const;\n }\n return [name, value] as const;\n });\n return changed ? Object.freeze(next) : headers;\n};\n\n/** The captured headers as the plain object both transports accept. */\nexport const headersToObject = (headers: readonly SnapshotHeader[]): Record<string, string> => {\n const bag: Record<string, string> = {};\n for (const [name, value] of headers) {\n bag[name] = value;\n }\n return bag;\n};\n","/**\n * The HTTP adapter behind the `UpgradeTransport` port.\n *\n * It owns no axios instance. Both rounds arrive as narrow, function-shaped\n * dependencies, which keeps HTTP vocabulary one level away from the controller\n * and lets every test here be a synchronous fake instead of an HTTP mock.\n *\n * It is also STATELESS by design: no cached challenge id, no cached prepare\n * result, no attempt counter. A challenge is single-use and the server consumes\n * it atomically, so there is nothing a second attempt could legitimately reuse.\n * Statelessness is the whole of this module's obligation to \"never reuse a spent\n * challenge_id\", and it is checkable by calling `prepare()` twice.\n */\n\nimport type { UpgradeRefreshField, UpgradeStartWire } from '../api/model';\n\nimport type { UpgradeTransport } from './controller';\nimport {\n UpgradeTransportError,\n classifyUpgradeError,\n invalidatesSessionOnCommit,\n isUpgradeSdkError,\n isUpgradeTransportError,\n} from './errors';\nimport { DEFAULT_UPGRADE_FACTORS, type UpgradeAssertion, type UpgradeFactorType, type UpgradePrepared } from './factors';\nimport type { UpgradeSessionPin } from './session';\n\nexport type UpgradeTransportDeps = {\n /** `POST /auth/upgrade/start`. Returns the raw snake_case body; rejects with `PassflowError`. */\n startUpgrade(payload: { method: string; relying_party_id?: string }): Promise<UpgradeStartWire>;\n /**\n * `POST /auth/refresh` carrying `upgrade`. Resolves once the elevated tokens\n * are STORED.\n *\n * `pin` is the session and user identity the flight began under (§13.4\n * revision 6). The host verifies it immediately before it applies the rotated\n * tokens and discards them on mismatch — the check has to live inside the\n * token-applying path, because that is the only place it cannot be bypassed\n * by another entry point. The transport only carries the value; the facade\n * supplies it, since the flight's start is what it is pinned to.\n */\n commitRefresh(upgrade: UpgradeRefreshField, pin?: UpgradeSessionPin): Promise<void>;\n /** Reports whether this delivery mode can carry an upgrade at all. BFF cannot. */\n canCommit(): { ok: true } | { ok: false; reason: string };\n /** Optional relying-party id for passkey upgrades; forwarded verbatim. */\n relyingPartyId?: () => string | undefined;\n /**\n * Invalidates the session. Called ONLY for a commit 401 whose error id\n * positively means the session died (`invalidatesSessionOnCommit`) — a\n * genuine expiry during an elevation is still an expiry, and the transport is\n * the only code that can tell, because it knows it issued the request and can\n * read the error id. Everything else, including an id this SDK does not\n * recognise, leaves the session alone.\n */\n markSessionInvalid?: () => void;\n};\n\n/**\n * Factors that need no prepare round, derived from the factor table rather than\n * restated, so adding a factor cannot leave this set stale.\n */\nconst ONE_SHOT_METHODS: ReadonlySet<string> = new Set(\n DEFAULT_UPGRADE_FACTORS.filter((factor) => !factor.requiresPrepare).map((factor) => factor.type),\n);\n\n/**\n * Normalises the prepare round's wire body.\n *\n * `webauthn` is passed through by reference, never re-serialised: the ceremony\n * hands those exact bytes to the authenticator and the server re-parses them on\n * the way back.\n *\n * A missing `challenge_id` or `expires_at` is a protocol violation, not an\n * optional field. Producing `challengeId: undefined` would surface two round\n * trips later as an opaque 401 with nothing to point at.\n */\nexport const toPrepared = (wire: UpgradeStartWire): UpgradePrepared => {\n if (!wire || typeof wire !== 'object' || !wire.challenge_id || !wire.expires_at) {\n throw new UpgradeTransportError({\n kind: 'protocol',\n message: 'The upgrade start response is missing challenge_id or expires_at',\n });\n }\n\n return {\n challengeId: wire.challenge_id,\n method: wire.method,\n expiresAt: wire.expires_at,\n webauthn: wire.webauthn,\n codeSentTo: wire.code_sent_to,\n };\n};\n\n/**\n * A rejection that is already one of the SDK's own upgrade outcomes, other than\n * a classified transport failure.\n *\n * `UpgradeTransportError` is excluded because `classifyUpgradeError` passes it\n * through on its own and the commit round still has to read its id for the\n * invalidation predicate.\n */\nconst isAlreadySettled = (e: unknown): boolean => isUpgradeSdkError(e) && !isUpgradeTransportError(e);\n\n/** Builds the transport. Returns the port type, so the controller stays HTTP-agnostic. */\nexport const createUpgradeTransport = (deps: UpgradeTransportDeps): UpgradeTransport => ({\n async prepare(method: UpgradeFactorType): Promise<UpgradePrepared | undefined> {\n // The server actively REJECTS a prepare call for a one-shot factor, so\n // \"call it and see\" costs a guaranteed round trip and a 400 on every TOTP\n // upgrade. The controller already gates on `requiresPrepare`; this is the\n // second line of that same defence, for a caller that does not.\n if (ONE_SHOT_METHODS.has(method)) {\n return undefined;\n }\n\n try {\n const relyingPartyId = deps.relyingPartyId?.();\n const wire = await deps.startUpgrade(relyingPartyId ? { method, relying_party_id: relyingPartyId } : { method });\n return toPrepared(wire);\n } catch (e) {\n if (isAlreadySettled(e)) {\n throw e;\n }\n const error = classifyUpgradeError(e, 'start');\n // `prepare_not_required` is a recovery, not a failure: our static\n // `requiresPrepare` disagreed with the server's, which is version skew.\n // Resolving `undefined` lets the factor assert directly, turning a future\n // backend rename into one wasted round trip instead of a dead elevation.\n if (error.kind === 'prepare_not_required') {\n return undefined;\n }\n throw error;\n }\n },\n\n async commit(factor: UpgradeFactorType, assertion: UpgradeAssertion): Promise<void> {\n // Checked before the network, so a delivery mode that can never commit says\n // so instead of taking a code from the user and discarding it.\n const permitted = deps.canCommit();\n if (!permitted.ok) {\n throw new UpgradeTransportError({ kind: 'unavailable', message: permitted.reason });\n }\n\n try {\n await deps.commitRefresh({ type: factor, assertion });\n } catch (e) {\n // An outcome the SDK has already decided is NOT an HTTP failure to\n // classify. The host's token-application pin check and the facade's\n // epoch guard both raise session-change outcomes from inside this call,\n // and neither carries a `response`, so `classifyUpgradeError` read them\n // as `network` — §13.3's `session changed mid-flight` row reached every\n // waiter tagged `upgrade_transport`. Re-thrown untouched, and before the\n // invalidation decision: a mid-flight identity change must never\n // invalidate (§13.4 revision 6, C38/C41).\n if (isAlreadySettled(e)) {\n throw e;\n }\n\n const error = classifyUpgradeError(e, 'refresh');\n\n // Invalidate only on an id that POSITIVELY means the session died.\n //\n // The commit path's 401s are not ambiguous: `LocalizedRouter.HTTPError`\n // always writes a `PassflowError.id` into the body, and\n // `refreshErrorStatus` (`src/web/api/refresh_token.go:157-178`) returns\n // 401 for exactly five enumerated ids, every one of them the upgrade\n // protocol talking. A dead or refused refresh token is NOT among them —\n // it falls to that switch's `default: http.StatusInternalServerError`.\n //\n // So the old \"anything else on a 401 is a real expiry\" reasoning was\n // defending against a response this server cannot send, while admitting\n // one it can: `error.2fa.not_enabled` is a 401, was absent from the\n // recoverable list, and signed out a user who simply had no 2FA enrolled.\n // Inverted, the default is fail-open, which is the rule this feature is\n // built on: fail toward \"I don't know\" rather than \"log them out\".\n if (error.status === 401 && invalidatesSessionOnCommit(error)) {\n deps.markSessionInvalid?.();\n }\n\n throw error;\n }\n },\n});\n","/**\n * The upgrade core: ONE implementation, transport-agnostic by construction.\n *\n * Every behaviour lives here — recognise a challenge on both channels, decide\n * whether the URL is in scope, run or join the elevation, replay once behind the\n * re-entry guard, and decide the outcome. The four surfaces the SDK offers\n * (`withAuth`, `attachAuth`, `createClient` and the internal axios seam) are\n * ADAPTERS over this function, and the prohibition on them is absolute:\n *\n * **NO LOGIC IN AN ADAPTER.**\n *\n * An adapter translates transport shapes in and out and does nothing else. It\n * may not parse a challenge, compare a bar, decide scope, count a retry, choose\n * an error class or read a body. If an adapter would need a decision, the\n * decision belongs here instead. The reason is not elegance: an axios response\n * interceptor receives an `AxiosError`, not a callable `next`, and re-issues\n * through `instance.request(config)` — so four independent wirings with two\n * outcome vocabularies is what happens by default, and it is the largest\n * quantity of parallel behaviour in this feature. A row that passes on one\n * adapter and fails on another is a leak of logic out of this file, and the fix\n * is to move it back in, never to fork the expectation per surface.\n *\n * `CoreOutcome` is the settlement table as a discriminated union, which is what\n * makes that table the adapters' specification rather than a prose parallel.\n */\n\nimport {\n type AssuranceBar,\n type ChallengeChannel,\n type UpgradeChallenge,\n type UpgradeDiagnostic,\n type UpgradeDiagnosticReason,\n type UpgradeDiagnosticSink,\n readUpgradeChallenge,\n} from './challenge';\nimport { UpgradeCancelledError, type UpgradeElevation, UpgradeUnsatisfiableError, type Waiter, barOf } from './controller';\nimport { UpgradeInsufficientError, UpgradeSessionChangedError, UpgradeTransportError, UpgradeUnavailableError } from './errors';\nimport type { ReplayHints, ReplayPolicy } from './replay';\nimport type { RequestSnapshot } from './snapshot';\n\n/**\n * Where the SDK is allowed to act on a challenge.\n *\n * Two domains, each with its own rule, and NO derived default. Deriving the set\n * was the category error the previous model made: same-origin by default, the\n * document origin, \"the origin the first 401 came from\" — every one of those is\n * the SDK inferring a trust boundary it has no information about, and a\n * fabricated 403 body from an observed endpoint can provoke a genuine elevation\n * whose replay then carries the freshly elevated credential to that endpoint.\n *\n * The resolved scope is passed IN. No adapter and no wrapper derives it.\n */\nexport type InterceptionScope = {\n /**\n * Domain 1 — Passflow's own API, derived from `PassflowConfig.url`. Always in\n * scope on any surface the SDK is attached to, which is what makes the\n * zero-config half of the design work. `null` when no `url` is configured:\n * domain 1 is then EMPTY and nothing throws, but the first parseable challenge\n * raises a diagnostic naming `url`, because an app with no `url` would\n * otherwise silently drop `/auth/session/exchange`'s own 403.\n */\n passflow: { origin: string; pathPrefix?: string } | null;\n /**\n * Domain 2 — the customer's own backend, exactly as declared. An absolute\n * origin (optionally with a path prefix) or a same-origin path prefix\n * (`'/api'`). `[]` means nothing beyond domain 1 is in scope — and the\n * predicate must NOT early-return on it.\n */\n backend: string[];\n};\n\nexport type UpgradeInterceptorOptions = {\n /**\n * Starts or joins the single flight, registering this waiter.\n *\n * Typed to accept a `void`-returning host as well, so a caller that does not\n * use the controller's waiter bookkeeping still works — it just forfeits the\n * per-waiter outcome summary, and the core then trusts the server's verdict on\n * the replay for the `insufficient` decision, which it does anyway.\n */\n elevate: (challenge: UpgradeChallenge, waiter: Waiter) => Promise<UpgradeElevation | void>;\n /** REQUIRED. Resolved once, where the client is built. */\n scope: InterceptionScope;\n /** `'header'` requires the standards channel and never reads the body. */\n requireChallengeChannel?: 'header';\n /**\n * NARROWING ONLY. Returning `false` excludes a URL the two domains admitted;\n * returning `true` can never widen, so an always-true predicate cannot\n * restore the allow-all default this design abolished.\n *\n * It is given the snapshot's **normalised absolute `href`**, never the raw\n * string the caller passed: a narrowing decision is an authorization decision,\n * and those are made on the URL the platform will actually send to (§13.4\n * revision 6). A predicate written against relative paths must therefore\n * match on the path of an absolute URL — `new URL(url).pathname` — rather than\n * on `startsWith('/…')`.\n */\n shouldIntercept?: (url: string) => boolean;\n replay?: ReplayPolicy;\n /** Default `'resolve-original'`. The fetch adapter's only option. */\n onCancel?: 'resolve-original' | 'reject';\n /** §5.5. Snapshotted before the elevation and read again after it. */\n authorizationSwap?: () => { from: string; to: string } | undefined;\n /** Default sink is `console.warn`; pass a function to route elsewhere. */\n onUpgradeDiagnostic?: UpgradeDiagnosticSink;\n /** The bar the elevation reached, when the host can read it from the new token. */\n readObtainedBar?: () => AssuranceBar | undefined;\n};\n\n/**\n * How an adapter is given its options: a value, or a getter read per challenge.\n *\n * The getter form is the one the facade uses, and the reason is a defect this\n * seam actually had. `enable()` resolves the interception policy — the scope in\n * particular — and an adapter handed the RESULT of that resolution freezes it at\n * attach time: a client built at module scope, or in a child's `useMemo`, is\n * attached before the provider's enable effect runs, so it would hold\n * `backend: []` forever and never intercept the customer's own backend. A\n * getter also makes re-enabling with a NARROWER `backend` take effect on\n * clients that were already attached, instead of leaving them on the wider\n * scope.\n *\n * A plain value is still accepted: a caller wiring the adapters directly, with\n * a scope it resolved itself, has nothing to re-read.\n */\nexport type UpgradeInterceptorOptionsSource = UpgradeInterceptorOptions | (() => UpgradeInterceptorOptions);\n\n/** Resolves an options source. Call it ONCE per challenge, never per module. */\nexport const resolveInterceptorOptions = (source: UpgradeInterceptorOptionsSource): UpgradeInterceptorOptions =>\n typeof source === 'function' ? source() : source;\n\n/**\n * What the core is given about one challenged request, and what it can do with\n * it. The adapter builds this and nothing more.\n */\n/**\n * A live view of one caller's abort state, with optional subscription.\n *\n * `AbortSignal` satisfies this structurally, and so does the minimal\n * `{ get aborted() }` getter object the adapters build. The listener pair is\n * what makes the abort an EVENT rather than a flag polled after the fact.\n */\nexport type AbortView = {\n aborted: boolean;\n reason?: unknown;\n addEventListener?(type: 'abort', listener: () => void): void;\n removeEventListener?(type: 'abort', listener: () => void): void;\n};\n\nexport type ChallengeContext = {\n status: number;\n /** The shipped callback shape, which is why one parser serves both surfaces. */\n getHeader(name: string): string | null | undefined;\n /**\n * Reads the body. MUST be safe to call only in scope, and MUST NOT disturb the\n * original payload — on the fetch surface that means `res.clone().json()`, so\n * the response the caller receives still has `bodyUsed === false`. The core\n * calls it only when the scope and the channel policy both permit.\n */\n readBody(): Promise<unknown>;\n /**\n * The normalised, immutable snapshot of the request that was challenged —\n * §13.4 revision 6's invariant, and the ONLY description of the request this\n * function will look at.\n *\n * The adapter takes it once, before dispatch, and hands the same object to\n * the scope check, this context, the diagnostics and `planReplay`. Nothing\n * here re-reads the caller's `Request`, `RequestInit` or axios config, so a\n * mutation while the prompt is open cannot change what was authorized, what\n * was reported or what is replayed.\n */\n request: RequestSnapshot;\n /**\n * Replays the ORIGINAL request, once, with the elevated credential. The\n * adapter supplies it; the core decides whether and when it is called, and\n * marks it replayed.\n */\n replay(hints?: ReplayHints): Promise<ChallengeContext>;\n /** Where the adapter stores the core's per-request markers. */\n markers: { upgradeTransaction?: true; replayed?: true };\n /**\n * The adapter's native payload — a `Response`, an `AxiosResponse`, an\n * `AxiosError`. The core never inspects it; it only hands it back so the\n * adapter can return the real thing.\n */\n raw?: unknown;\n /** `false` when `planReplay` refused. The prompt is then never opened. */\n replayable?: boolean;\n /** Why the replay is unsupported, for the diagnostic. */\n replayUnsupportedReason?: string;\n /**\n * The caller's abort state. The SDK never aborts it.\n *\n * Read as a LIVE view, never a snapshot, because the core reads it after\n * awaiting the elevation. The two listener methods are optional and carry\n * REQ-C12: with them the core races the abort against the pending proof, so\n * the caller's own promise settles at abort time instead of waiting for a\n * prompt it has walked away from; without them (a bare `{ aborted }` view)\n * the core falls back to reading the flag once the elevation settles. The\n * elevation itself is never cancelled either way — it is shared.\n */\n signal?: AbortView;\n /** `'cors'` distinguishes the one actionable `'no-channel'` variant. */\n responseType?: string;\n /**\n * `true` when the adapter received this exchange as a REJECTION rather than\n * as a response — axios' `validateStatus` contract, where every non-2xx\n * rejects. `fetch` resolves a 401, so its adapter never sets it.\n *\n * The core classifies the exchange identically either way: this flag decides\n * only how a `replayed` outcome is handed back, so a replay that failed on\n * its own terms keeps failing on the surface's own convention instead of\n * being resolved as a value.\n */\n rejected?: boolean;\n};\n\n/**\n * Every terminal outcome, as the settlement table defines them.\n *\n * The invariant: **every path settles.** There is no input that produces a\n * pending promise.\n */\nexport type CoreOutcome =\n /** Not a challenge. Pass the response through untouched. */\n | { kind: 'not-a-challenge' }\n /** A challenge the SDK is not allowed to act on. Diagnostic only; no body was read. */\n | { kind: 'out-of-scope' }\n | { kind: 'replayed'; response: ChallengeContext }\n /**\n * The proof completed and the operation's replay was challenged again.\n *\n * `response` is the SECOND challenge response, which the fetch surface\n * resolves and tags (§13.3) — so the decision \"which response does the caller\n * receive\" stays here rather than becoming a per-adapter `??` chain. It is\n * absent only on the re-entry path, where the adapter already holds the object\n * it was handed.\n */\n | { kind: 'insufficient'; obtained: AssuranceBar; required: AssuranceBar; response?: ChallengeContext }\n | { kind: 'cancelled' }\n | { kind: 'exhausted'; error: Error }\n | { kind: 'unsatisfiable' }\n | { kind: 'unavailable' }\n | { kind: 'session-changed' }\n | { kind: 'transport-failure'; status?: number; retryable: boolean; error: Error }\n | { kind: 'replay-unsupported' }\n | { kind: 'aborted'; reason: unknown };\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Scope\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Parses a PATTERN — never a request URL.\n *\n * Request URLs are normalised once, before dispatch, by\n * `createRequestSnapshot`; by the time they reach this file they are already a\n * resolved `origin` plus a resolved `pathname`. The only strings left to parse\n * here are the `backend` patterns the developer wrote, which are configuration\n * and carry no attacker-controlled bytes.\n */\nconst parsePattern = (pattern: string): URL | undefined => {\n try {\n return new URL(pattern);\n } catch {\n return undefined;\n }\n};\n\nconst pathMatches = (path: string, prefix: string | undefined): boolean => {\n if (!prefix || prefix === '/') {\n return true;\n }\n const normalised = prefix.replace(/\\/+$/, '');\n return path === normalised || path.startsWith(`${normalised}/`);\n};\n\n/** Is this request inside domain 1? Origin-compare on the RESOLVED url. */\nconst inPassflowDomain = (request: RequestSnapshot, domain: InterceptionScope['passflow']): boolean => {\n if (!domain || request.origin === undefined) {\n return false;\n }\n return request.origin === domain.origin && pathMatches(request.pathname, domain.pathPrefix);\n};\n\n/**\n * Is this request inside a declared backend?\n *\n * A pattern starting with `/` is a **same-origin path prefix**, which is how a\n * SPA declares `backend: '/api'`. \"Same-origin\" is load-bearing, and it is now\n * decided the only way it can be decided correctly: the snapshot resolved the\n * caller's URL with the platform's own parser, against the document base, and\n * `relativeToBase` says whether the result landed on the base's own origin.\n *\n * That single bit is what separates `/api/x` (in scope) from `/\\evil.example/x`,\n * `/<TAB>/evil.example/x` and `//evil.example/x` (all out of scope, because the\n * parser resolves all three to `https://evil.example`) — while leaving\n * `/%5Cevil.example/x` and `/%2F%2Fevil.example/x` IN scope, because an encoded\n * byte is a path character and the parser never decodes it into an authority.\n * Revision 2's `//`-prefix special case is REMOVED rather than stacked on top:\n * two mechanisms for one decision is how the next vector gets in.\n *\n * An absolute pattern matches by origin, plus its own path prefix when it\n * carries one. Same-origin is never implicitly included: that was the derived\n * default this design removed; it is in scope only because `'/…'` says so.\n */\nconst inDeclaredBackend = (request: RequestSnapshot, patterns: string[]): boolean => {\n for (const pattern of patterns) {\n if (pattern === '') {\n continue;\n }\n\n if (pattern.startsWith('/')) {\n if (request.relativeToBase && pathMatches(request.pathname, pattern)) {\n return true;\n }\n continue;\n }\n\n const patternUrl = parsePattern(pattern) ?? parsePattern(`https://${pattern}`);\n if (!patternUrl || request.origin === undefined) {\n continue;\n }\n if (request.origin === patternUrl.origin && pathMatches(request.pathname, patternUrl.pathname)) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * The scope decision, identical for every surface, and made on the SNAPSHOT.\n *\n * Precedence, in one ordered rule: domain 1, then domain 2, then\n * `shouldIntercept` as a narrowing-only hook. There is no allow-all opt-in — a\n * developer who genuinely wants every origin lists every origin, which is a\n * sentence they can read in a diff.\n *\n * It takes a `RequestSnapshot` and not a string by design (§13.4 revision 6): a\n * string parameter is an invitation to decide authorization on an un-normalised\n * value, which is exactly the defect this signature removes.\n */\nexport const isInterceptionScope = (\n request: RequestSnapshot,\n scope: InterceptionScope,\n shouldIntercept?: (url: string) => boolean,\n): boolean => {\n const admitted = inPassflowDomain(request, scope.passflow) || inDeclaredBackend(request, scope.backend);\n if (!admitted) {\n return false;\n }\n return shouldIntercept ? shouldIntercept(request.href) !== false : true;\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Diagnostics\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** The default sink: `console.warn`, because `debug` is invisible in production. */\nconst warn = (diagnostic: UpgradeDiagnostic): void => {\n const detail = diagnostic.detail;\n const message = detail?.message ?? `[passflow] upgrade challenge not actioned: ${diagnostic.reason}`;\n // biome-ignore lint/suspicious/noConsole: this IS the fail-open diagnostic. §13.1/§13.4 make it mandatory that a deployment where step-up silently never fires is distinguishable from one where it was never required, and the default sink must be visible in production — `debug` is not, and an exception would turn a fail-open into a fail-closed. A consumer who wants it elsewhere passes `onUpgradeDiagnostic`.\n console.warn(message, { status: diagnostic.status, url: diagnostic.url, reason: diagnostic.reason, detail });\n};\n\ntype Reporter = (reason: UpgradeDiagnosticReason, detail?: UpgradeDiagnostic['detail']) => void;\n\n/** Builds the per-request reporter, which fills in the url and method the parser lacks. */\nconst reporterFor = (ctx: ChallengeContext, options: UpgradeInterceptorOptions): Reporter => {\n const sink = options.onUpgradeDiagnostic ?? warn;\n return (reason, detail) => {\n sink({\n status: ctx.status,\n url: ctx.request.href,\n reason,\n detail: { method: ctx.request.method, ...(detail ?? {}) },\n });\n };\n};\n\n/** Forwards a parse's own findings, which are knowable only inside the parse. */\nconst forwardParseDiagnostics = (\n diagnostics: UpgradeDiagnostic[],\n ctx: ChallengeContext,\n options: UpgradeInterceptorOptions,\n): void => {\n const sink = options.onUpgradeDiagnostic ?? warn;\n for (const diagnostic of diagnostics) {\n sink({ ...diagnostic, url: ctx.request.href, detail: { method: ctx.request.method, ...(diagnostic.detail ?? {}) } });\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Reading the challenge\n * ──────────────────────────────────────────────────────────────────────────── */\n\nconst JSON_CONTENT_TYPE = /\\bjson\\b/i;\n\n/**\n * Reads a challenge from an IN-SCOPE response.\n *\n * The body-handling rule is strict, and the original response always comes back\n * with `bodyUsed === false`:\n *\n * ```\n * status not in {401,403} → never read, never cloned\n * status in {401,403}, content-type JSON → ALSO parse a CLONE of the body, in\n * try/catch, and merge both channels\n * status in {401,403}, not JSON → header only; body never read\n * requireChallengeChannel: 'header' → header only; body never read\n * ```\n *\n * A header hit no longer suppresses the clone read. The short-circuit it\n * replaces (\"header parse succeeds → body NEVER read\") was an optimisation\n * mistaken for a safety property: reading a CLONE was always the mechanism that\n * kept the caller's body intact, and the merge rule cannot run behind a\n * first-match chain.\n */\nconst readChallenge = async (\n ctx: ChallengeContext,\n options: UpgradeInterceptorOptions,\n): Promise<{ challenge?: UpgradeChallenge; diagnostics: UpgradeDiagnostic[] }> => {\n const headerOnly = options.requireChallengeChannel === 'header';\n const contentType = ctx.getHeader('content-type') ?? '';\n const mayReadBody = !headerOnly && JSON_CONTENT_TYPE.test(contentType);\n\n let body: unknown;\n if (mayReadBody) {\n try {\n body = await ctx.readBody();\n } catch {\n // A malformed or already-consumed body is not an error path: the header\n // may still carry the challenge, and the caller's response is untouched.\n body = undefined;\n }\n }\n\n const input = mayReadBody\n ? { status: ctx.status, getHeader: (name: string) => ctx.getHeader(name) ?? undefined, body }\n : { status: ctx.status, getHeader: (name: string) => ctx.getHeader(name) ?? undefined };\n\n return readUpgradeChallenge(input, { requireChallengeChannel: options.requireChallengeChannel });\n};\n\n/** Header-only inspection, for a URL outside the declared scope. */\nconst readOutOfScopeChallenge = (ctx: ChallengeContext): UpgradeChallenge | undefined =>\n readUpgradeChallenge(\n { status: ctx.status, getHeader: (name: string) => ctx.getHeader(name) ?? undefined },\n { requireChallengeChannel: 'header' },\n ).challenge;\n\n/** The message that names the fix, which is the whole value of this diagnostic. */\nconst outOfScopeMessage = (url: string, scope: InterceptionScope): string =>\n scope.passflow === null\n ? `[passflow] a 401/403 from ${url} carried an upgrade challenge, but no Passflow endpoint is configured — set \\`url\\` on the Passflow config.`\n : `[passflow] a 401/403 from ${url} carried an upgrade challenge, but that URL is not covered by the provider's \\`backend\\` prop — add it there to enable step-up for this API.`;\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Classification\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** Maps an elevation failure to its settlement-table row. */\nconst classifyFailure = (e: unknown, obtained: AssuranceBar | undefined): CoreOutcome => {\n if (e instanceof UpgradeCancelledError) {\n return { kind: 'cancelled' };\n }\n if (e instanceof UpgradeUnsatisfiableError) {\n return { kind: 'unsatisfiable' };\n }\n if (e instanceof UpgradeUnavailableError) {\n return { kind: 'unavailable' };\n }\n if (e instanceof UpgradeSessionChangedError) {\n return { kind: 'session-changed' };\n }\n if (e instanceof UpgradeInsufficientError) {\n return { kind: 'insufficient', obtained: e.obtained, required: e.required };\n }\n if (e instanceof UpgradeTransportError) {\n // Both exhaustion kinds settle on the same row of §13.3's table: the\n // server's own budget (`exhausted`) and the controller's local attempt\n // budget (`attempts_exhausted`) are the same statement to a caller — stop,\n // another answer will not help — and both are terminal.\n if (e.kind === 'exhausted' || e.kind === 'attempts_exhausted') {\n return { kind: 'exhausted', error: e };\n }\n if (e.kind === 'unavailable') {\n return { kind: 'unavailable' };\n }\n if (e.kind === 'session_changed') {\n return { kind: 'session-changed' };\n }\n // FR1's non-retryable 400 class — unsupported factor, no session — plus the\n // network and protocol classes. Surfaced, never laundered into a logout:\n // re-authentication is the application's decision, taken on a state it can\n // see.\n return { kind: 'transport-failure', status: e.status, retryable: e.retryable, error: e };\n }\n return {\n kind: 'transport-failure',\n retryable: false,\n error: e instanceof Error ? e : new Error(String(e)),\n ...(obtained ? {} : {}),\n };\n};\n\n/**\n * Classifies the replayed response: did this operation actually succeed?\n *\n * Proof completion and a protected operation's outcome are different events. The\n * user can complete TOTP, the server can accept it and record the elevation, and\n * the replay can STILL be challenged — on a higher `acr` bar or on a freshness\n * demand the new proof does not satisfy. For AAL3 endpoints with TOTP-only users\n * that is the normal path, so it gets a first-class outcome carrying both bars\n * rather than a second 401 returned verbatim with nothing to point at.\n */\nconst classifyReplay = async (\n replayed: ChallengeContext,\n options: UpgradeInterceptorOptions,\n obtained: AssuranceBar,\n report: Reporter,\n): Promise<CoreOutcome> => {\n if (replayed.status !== 401 && replayed.status !== 403) {\n return { kind: 'replayed', response: replayed };\n }\n\n const { challenge, diagnostics } = await readChallenge(replayed, options);\n forwardParseDiagnostics(diagnostics, replayed, options);\n\n if (!challenge) {\n return { kind: 'replayed', response: replayed };\n }\n\n const required = barOf(challenge);\n report('elevated-insufficient', { obtained, required });\n return { kind: 'insufficient', obtained, required, response: replayed };\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * The abort race (REQ-C12)\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** Either the work finished, or this caller's signal fired first. */\ntype AbortRace<T> = { aborted: true; reason: unknown } | { aborted: false; value: T };\n\n/**\n * Awaits `work`, but settles early if `signal` aborts first.\n *\n * REQ-C12: an abort must settle the CALLER's promise promptly, without waiting\n * for a proof it has walked away from. The asymmetry that makes this safe is\n * that the elevation is shared and is NEVER cancelled here: `work` keeps\n * running for the other waiters, and only this caller stops awaiting it. The\n * caller of this helper owns what happens to a late-resolving `work` — see the\n * aborted branch in `handleChallenge`.\n *\n * Three ordering details, each one a defect if dropped:\n *\n * - **Already aborted** is checked before subscribing, because an\n * `AbortSignal` fires `abort` once and a listener attached afterwards never\n * hears it.\n * - **Work wins** still returns `aborted: false`, so the post-settle flag\n * check in `handleChallenge` keeps its existing meaning. A view with no\n * listeners takes that path for every abort, which is why the legacy\n * `{ aborted }` shape still behaves exactly as it did.\n * - **A rejection of `work` after the abort won** is absorbed by the caller,\n * not here: nothing is awaiting this promise any more, and an unhandled\n * rejection in a customer's app over a step-up they cancelled is not an\n * acceptable trade for a tidier helper.\n */\nconst raceAbort = async <T>(work: Promise<T>, signal: AbortView | undefined): Promise<AbortRace<T>> => {\n if (!signal) {\n return { aborted: false, value: await work };\n }\n if (signal.aborted) {\n return { aborted: true, reason: signal.reason };\n }\n if (typeof signal.addEventListener !== 'function') {\n return { aborted: false, value: await work };\n }\n\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<AbortRace<T>>((resolve) => {\n onAbort = () => resolve({ aborted: true, reason: signal.reason });\n signal.addEventListener?.('abort', onAbort);\n });\n\n try {\n return await Promise.race([work.then((value): AbortRace<T> => ({ aborted: false, value })), aborted]);\n } finally {\n if (onAbort) {\n signal.removeEventListener?.('abort', onAbort);\n }\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * The core\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Detect → scope → elevate → replay once → classify. The one implementation.\n *\n * \"Retry exactly once\" is structural rather than counted: there is no path from\n * the replay back to the elevation. On a surface that re-enters this function\n * with the replayed request — axios does, because the replay goes back through\n * `instance.request(config)` and hits the same response interceptor — the replay\n * marker is read first and the response is classified without elevating. That\n * guard is written here, once; the adapter only supplies the marker's storage.\n */\nexport const handleChallenge = async (ctx: ChallengeContext, options: UpgradeInterceptorOptions): Promise<CoreOutcome> => {\n if (ctx.status !== 401 && ctx.status !== 403) {\n return { kind: 'not-a-challenge' };\n }\n\n // ── the elevation's OWN two rounds are never challenges to act on ──\n //\n // `/auth/upgrade/start` and the committing `/auth/refresh` are themselves\n // access-token-gated, so a 401 on either can parse as a challenge. Acting on\n // one would open a second prompt from inside the first elevation. The marker\n // is positive evidence that we issued the request, and reading it here — not\n // in an adapter — is what keeps the rule identical on every surface.\n if (ctx.markers.upgradeTransaction) {\n return { kind: 'not-a-challenge' };\n }\n\n const report = reporterFor(ctx, options);\n\n // ── the re-entry guard: classify and settle, never elevate ──\n if (ctx.markers.replayed) {\n const inScope = isInterceptionScope(ctx.request, options.scope, options.shouldIntercept);\n if (!inScope) {\n return { kind: 'not-a-challenge' };\n }\n const { challenge, diagnostics } = await readChallenge(ctx, options);\n forwardParseDiagnostics(diagnostics, ctx, options);\n if (!challenge) {\n return { kind: 'not-a-challenge' };\n }\n const obtained = options.readObtainedBar?.() ?? {};\n const required = barOf(challenge);\n report('elevated-insufficient', { obtained, required });\n return { kind: 'insufficient', obtained, required, response: ctx };\n }\n\n // ── scope, before anything reads a body ──\n if (!isInterceptionScope(ctx.request, options.scope, options.shouldIntercept)) {\n // Out of scope the SDK inspects the `WWW-Authenticate` header ONLY and never\n // reads or clones the body, so declaring a scope cannot be widened by a\n // diagnostic. The cross-origin, body-only, out-of-scope case therefore stays\n // silent: a stated limitation, not an oversight. Parsing for diagnostics\n // never elevates and never replays, so the security decision is untouched.\n const challenge = readOutOfScopeChallenge(ctx);\n if (!challenge) {\n return { kind: 'not-a-challenge' };\n }\n report('challenge-out-of-scope', {\n header: ctx.getHeader('www-authenticate') ?? undefined,\n message: outOfScopeMessage(ctx.request.href, options.scope),\n });\n return { kind: 'out-of-scope' };\n }\n\n const { challenge, diagnostics } = await readChallenge(ctx, options);\n forwardParseDiagnostics(diagnostics, ctx, options);\n\n if (!challenge) {\n // Fail toward \"I don't know\" rather than \"log them out\": no prompt opens, no\n // elevation starts, no blocking state is entered, and the response goes back\n // to the caller untouched. A client-side miss costs a broken flow, never an\n // unauthorized one, because the server is the enforcement point and this is\n // not. But it is never SILENT — a deployment where step-up never fires must\n // be distinguishable from one where it was never required.\n const rawHeader = ctx.getHeader('www-authenticate');\n if (options.requireChallengeChannel !== 'header') {\n report('no-channel', {\n ...(rawHeader ? { header: rawHeader } : {}),\n message:\n ctx.responseType === 'cors' && !rawHeader\n ? `[passflow] a cross-origin ${ctx.status} from ${ctx.request.href} carried no readable challenge: \\`WWW-Authenticate\\` is not CORS-safelisted, so add \\`Access-Control-Expose-Headers: WWW-Authenticate\\` on that server, or have it emit the \\`upgrade_required\\` JSON body.`\n : `[passflow] a ${ctx.status} from ${ctx.request.href} carried no upgrade challenge on either channel; treated as an ordinary ${ctx.status}.`,\n });\n }\n return { kind: 'not-a-challenge' };\n }\n\n // ── can this request even be honoured? ──\n if (ctx.replayable === false) {\n // Prompting a user, elevating their session and only then discovering we\n // cannot honour it is the worst possible ordering: it spends a server\n // challenge and a user interaction for nothing.\n report('replay-unsupported', {\n message: `[passflow] a ${ctx.status} from ${ctx.request.href} carried an upgrade challenge, but the request cannot be replayed (${\n ctx.replayUnsupportedReason ?? 'unsupported body'\n }), so no prompt was opened.`,\n });\n return { kind: 'replay-unsupported' };\n }\n\n // §5.5's ordering: `from` is snapshotted BEFORE the elevation, because the\n // commit overwrites storage, and `to` is read after.\n const before = options.authorizationSwap?.();\n\n const waiter: Waiter = { kind: 'request', url: ctx.request.href, method: ctx.request.method };\n\n // Aborting the original request does NOT cancel the elevation — it is shared,\n // and one caller walking away must not cancel a prompt three other in-flight\n // requests are waiting on. What the abort DOES do (REQ-C12) is stop THIS\n // caller waiting: its promise settles with the abort its own `catch` already\n // expects from `fetch`, while the flight runs on untouched for the others.\n // `Promise.resolve` wraps the call so that an `elevate` which throws\n // SYNCHRONOUSLY still lands in `classifyFailure` below rather than escaping\n // this function — starting the elevation before the `try` is what made that\n // reachable, and a host whose `elevate` throws on, say, a missing endpoint\n // must still settle on the table and never log anyone out.\n let elevating: Promise<UpgradeElevation | void> | undefined;\n let race: AbortRace<UpgradeElevation | void>;\n try {\n elevating = Promise.resolve(options.elevate(challenge, waiter));\n race = await raceAbort(elevating, ctx.signal);\n } catch (e) {\n return classifyFailure(e, undefined);\n }\n\n if (race.aborted) {\n // The proof is still pending, so there is no waiter handle to report with\n // yet — the handle arrives only when the elevation resolves. Reporting is\n // therefore deferred to that moment, and NOTHING else is: no bar is read,\n // no `authorizationSwap` is applied, the replay marker is not set and the\n // request is not replayed. A proof that completes after its waiter walked\n // away must not act on that waiter's behalf.\n //\n // `failed` is this waiter's terminal either way — it is what the liveness\n // bound would have assigned — and it must still arrive, or `awaitReports`\n // holds the prompt and every other waiter to the 30s bound over an ordinary\n // input. `receiveReport` is idempotent and safe on a cleared controller, so\n // a late report can neither double-count nor wedge a later challenge.\n //\n // The rejection arm is the unhandled-rejection guard: the elevation's own\n // failure is already the controller's to classify for the waiters that are\n // still listening, and it is never laundered into a logout here.\n elevating?.then(\n (elevation) => elevation?.waiter.report({ outcome: 'failed' }),\n () => {\n // Absorbed deliberately: see above.\n },\n );\n return { kind: 'aborted', reason: race.reason };\n }\n\n const elevation = race.value;\n\n // The abort that landed while the elevation was already settling, or against\n // a signal view with no listeners to race.\n if (ctx.signal?.aborted) {\n // The waiter still has to REPORT. Released for its adapter to classify, it\n // otherwise stays `pending` and `awaitReports` polls to the 30s liveness\n // bound — holding every other waiter in the flight, and the prompt, on what\n // is an ordinary input rather than an adapter bug. `failed` is the terminal\n // that bound would have assigned anyway; this only makes it arrive now. The\n // two abort checks inside the replay `catch` below already report before\n // they read the signal, so this is the one branch that needed it.\n elevation?.waiter.report({ outcome: 'failed' });\n return { kind: 'aborted', reason: ctx.signal.reason };\n }\n\n const obtained = elevation?.obtained ?? options.readObtainedBar?.() ?? barOf(challenge);\n const after = options.authorizationSwap?.();\n const swap = after ? { from: before?.from ?? after.from, to: after.to } : undefined;\n\n // The marker is set BEFORE the replay, so a surface that re-enters this\n // function with the replayed request takes the guard above instead of\n // elevating again — the loop FR3 prohibits by name.\n ctx.markers.replayed = true;\n\n let replayed: ChallengeContext;\n try {\n replayed = await ctx.replay(swap ? { authorizationSwap: swap } : undefined);\n } catch (e) {\n // A re-challenged replay is a REFUSAL, not a failure.\n //\n // On the axios surfaces the replay goes back through `request(config)` and\n // re-enters this function, where the re-entry guard above classifies it and\n // `settleAxiosOutcome` REJECTS with `UpgradeInsufficientError` — so the\n // refusal arrives here as a throw. Reporting `failed` for it told the\n // controller's `summarise` nothing about the bar, and a flight whose only\n // waiter was refused was then summarised as `success`: the modal said\n // \"Verification complete\" for an operation the server declined. The fetch\n // surface already gets this right through `classifyReplay`; this is the\n // same decision for the other three, made here so it cannot drift again.\n if (e instanceof UpgradeInsufficientError) {\n elevation?.waiter.report({ outcome: 'insufficient', obtained: e.obtained, required: e.required });\n if (ctx.signal?.aborted) {\n return { kind: 'aborted', reason: ctx.signal.reason };\n }\n return { kind: 'insufficient', obtained: e.obtained, required: e.required };\n }\n\n elevation?.waiter.report({ outcome: 'failed' });\n if (ctx.signal?.aborted) {\n return { kind: 'aborted', reason: ctx.signal.reason };\n }\n // A real failure still throws: the replay's own network error is the\n // caller's error, not a step-up outcome.\n throw e;\n }\n\n const outcome = await classifyReplay(replayed, options, obtained, report);\n\n if (outcome.kind === 'insufficient') {\n elevation?.waiter.report({ outcome: 'insufficient', obtained: outcome.obtained, required: outcome.required });\n } else {\n elevation?.waiter.report({ outcome: 'replayed', obtained });\n }\n\n return outcome;\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Outcome → error, for the two throwing surfaces\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * The error class a non-fetch surface rejects with, or `undefined` when the\n * outcome is not a rejection.\n *\n * axios' own contract, expressed in `validateStatus`, is that a non-2xx response\n * REJECTS. Resolving a declined step-up there would invert the library's\n * contract: every caller's post-`await` success path would run after the user\n * clicked \"Not now\", and `await api.post('/transfer')` would resolve with only a\n * status check the customer was never instructed to write standing between that\n * and treating an unperformed elevated action as performed.\n *\n * Every class carries a literal `code` discriminant as well as being\n * `instanceof`-checkable, and the `code` is the testable tag: it survives\n * bundling, duplicate package copies and cross-realm boundaries.\n */\nexport const coreOutcomeToError = (outcome: CoreOutcome): Error | undefined => {\n switch (outcome.kind) {\n case 'cancelled':\n return new UpgradeCancelledError();\n case 'unsatisfiable':\n return new UpgradeUnsatisfiableError();\n case 'unavailable':\n return new UpgradeUnavailableError();\n case 'session-changed':\n return new UpgradeSessionChangedError();\n case 'insufficient':\n return new UpgradeInsufficientError(outcome.obtained, outcome.required);\n case 'exhausted':\n return outcome.error;\n case 'transport-failure':\n return outcome.error;\n default:\n // `not-a-challenge`, `out-of-scope`, `replayed`, `replay-unsupported` and\n // `aborted` are not rejections of ours: the first four hand the caller the\n // transport's own result, and an abort throws the signal's reason.\n return undefined;\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Outcome → settlement, per surface. The adapters translate, they do not decide.\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * What an adapter must do with an outcome: hand back an object, or throw.\n *\n * Both surfaces settle from this, so §13.3's table has exactly one encoding in\n * the codebase. An adapter that reached its own conclusion about which response\n * a caller receives, or which error class, would be the per-surface drift the\n * single core exists to prevent.\n */\nexport type Settlement<T> = { settle: 'resolve'; value: T } | { settle: 'reject'; error: unknown };\n\n/**\n * The fetch surface's settlement.\n *\n * `fetch` resolves on 401 and every caller is written as `if (!res.ok)`, so a\n * declined step-up comes back as the original response — tagged, so it is\n * discriminable — rather than as a rejection their code has never handled. The\n * two exceptions are an abort (which throws the signal's reason, as `fetch`\n * itself does) and the explicit `onCancel: 'reject'` opt-out.\n */\nexport const settleFetchOutcome = <T extends object>(\n outcome: CoreOutcome,\n original: T,\n options: UpgradeInterceptorOptions,\n): Settlement<T> => {\n switch (outcome.kind) {\n case 'replayed':\n return { settle: 'resolve', value: outcome.response.raw as T };\n case 'not-a-challenge':\n case 'out-of-scope':\n case 'replay-unsupported':\n // Untouched and untagged: the SDK either saw nothing to act on, or never\n // opened a prompt, so there is no step-up outcome to report.\n return { settle: 'resolve', value: original };\n case 'aborted':\n return { settle: 'reject', error: outcome.reason };\n case 'cancelled':\n return options.onCancel === 'reject'\n ? { settle: 'reject', error: new UpgradeCancelledError() }\n : { settle: 'resolve', value: tagUpgradeOutcome(original, outcome) };\n case 'insufficient':\n // The SECOND challenge is what the caller receives: it is the response\n // that actually refused the operation, and the bars travel in the tag.\n return { settle: 'resolve', value: tagUpgradeOutcome((outcome.response?.raw as T) ?? original, outcome) };\n default:\n return { settle: 'resolve', value: tagUpgradeOutcome(original, outcome) };\n }\n};\n\n/**\n * The axios surface's settlement.\n *\n * axios' own contract, expressed in `validateStatus`, is that a non-2xx\n * response rejects, so every non-success outcome rejects here — with the tagged\n * error class for a step-up outcome, and with the transport's own error when\n * the SDK did not act.\n */\nexport const settleAxiosOutcome = <T>(outcome: CoreOutcome, originalError: unknown): Settlement<T> => {\n switch (outcome.kind) {\n case 'replayed':\n // A replay that axios itself rejected — a 500, say, carrying no\n // challenge — is still a rejection: resolving it would hand the caller an\n // `AxiosError` as a success value and invert `validateStatus` for the one\n // request step-up touched.\n return outcome.response.rejected\n ? { settle: 'reject', error: outcome.response.raw ?? originalError }\n : { settle: 'resolve', value: outcome.response.raw as T };\n case 'aborted':\n return { settle: 'reject', error: outcome.reason };\n default: {\n const error = coreOutcomeToError(outcome);\n // `not-a-challenge`, `out-of-scope` and `replay-unsupported` produce no\n // error of ours, and the caller gets the rejection axios already had.\n return { settle: 'reject', error: error ?? originalError };\n }\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Fetch-surface discriminators\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * The fetch surface resolves the ORIGINAL 401/403 response, untouched, because\n * `fetch` does not reject on 401 — it resolves with `res.ok === false`, and\n * every caller is written as `if (!res.ok)`. Rejecting would introduce a failure\n * mode their code has never handled. The resolved value is never `null` or\n * `undefined`: that breaks `FetchLike` and throws `TypeError` in customer code\n * on the first `res.ok`.\n *\n * So every non-success outcome needs a discriminator, and they are backed by\n * `WeakSet`s keyed on the object the SDK returned. Two consequences a caller\n * must know, because both produce a confident `false` on a genuine outcome:\n *\n * - **Check before cloning.** `isUpgradeCancelled(res.clone())` is `false`: a\n * clone is a different object.\n * - **One copy of the SDK.** A predicate imported from a second copy of\n * `@passflow/core` in the same tree reads a different `WeakSet`.\n */\nconst cancelled = new WeakSet<object>();\nconst exhausted = new WeakSet<object>();\nconst insufficient = new WeakSet<object>();\nconst unsatisfiable = new WeakSet<object>();\nconst unavailable = new WeakSet<object>();\nconst sessionChanged = new WeakSet<object>();\nconst transportFailure = new WeakSet<object>();\n\n/**\n * Tags the response an adapter is about to resolve with the outcome that\n * produced it. The mapping is here, not in the adapter: choosing which set a\n * `CoreOutcome` belongs to is a decision.\n */\nexport const tagUpgradeOutcome = <T extends object>(response: T, outcome: CoreOutcome): T => {\n switch (outcome.kind) {\n case 'cancelled':\n cancelled.add(response);\n break;\n case 'exhausted':\n exhausted.add(response);\n break;\n case 'insufficient':\n insufficient.add(response);\n break;\n case 'unsatisfiable':\n unsatisfiable.add(response);\n break;\n case 'unavailable':\n unavailable.add(response);\n break;\n case 'session-changed':\n sessionChanged.add(response);\n break;\n case 'transport-failure':\n transportFailure.add(response);\n break;\n default:\n break;\n }\n return response;\n};\n\n/** \"You were challenged and you declined\" — as opposed to \"you were not authorised\". */\nexport const isUpgradeCancelled = (response: object): boolean => cancelled.has(response);\nexport const isUpgradeExhausted = (response: object): boolean => exhausted.has(response);\nexport const isUpgradeInsufficient = (response: object): boolean => insufficient.has(response);\nexport const isUpgradeUnsatisfiable = (response: object): boolean => unsatisfiable.has(response);\nexport const isUpgradeUnavailable = (response: object): boolean => unavailable.has(response);\nexport const isUpgradeSessionChanged = (response: object): boolean => sessionChanged.has(response);\n/** FR1's non-retryable 400 class. */\nexport const isUpgradeTransportFailure = (response: object): boolean => transportFailure.has(response);\n\nexport type { ChallengeChannel };\n","/**\n * The replay planner.\n *\n * Pure, no I/O, and it runs BEFORE the first call — a `Request` body must be\n * cloned while it is still unread, so by the time a 401 has come back it is too\n * late to decide whether a replay is possible. Planning first is also what lets\n * the core fail BEFORE opening a prompt: prompting the user, elevating their\n * session and only then discovering we cannot honour it is the worst possible\n * ordering, because it spends a server challenge and a user interaction for\n * nothing.\n *\n * ## Why replaying a challenged request is safe at all\n *\n * An RFC 9470 challenge is emitted by the authorization middleware —\n * `RequireAuthLevel` / `RequireFreshAuth` — BEFORE the handler runs, so the\n * request had no effect. That is categorically different from a 429 or a 502,\n * where the request may have partially executed, which is why\n * `AxiosClient.handleRateLimitError` correctly refuses to retry non-idempotent\n * requests and why this correctly does the opposite.\n *\n * The guarantee depends on the CUSTOMER's middleware placement, though, so\n * there is an escape hatch rather than an assumption: a backend that emits an\n * `insufficient_user_authentication` challenge after a side effect is violating\n * RFC 9470, and such a backend sets `'idempotent-only'`. Stating the\n * precondition is the design; silently assuming it would not be.\n *\n * ## Per-waiter binding\n *\n * A plan is bound to the request that carried the challenge: its own URL, its\n * own method and its own pre-captured body. No waiter's challenge, outcome or\n * response ever changes what another waiter replays, or where — requirement\n * AGGREGATION across waiters sharing a session is permitted and happens in the\n * controller; replay SUBSTITUTION is prohibited, and that prohibition is this\n * module's whole job.\n */\n\nimport { type RequestSnapshot, createRequestSnapshot, headersToObject, swapAuthorization } from './snapshot';\n\n/** Our own `fetch` shape. Declared here rather than imported so no DOM lib is required. */\nexport type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/** How a body can be re-sent, which is the only thing that decides replayability. */\nexport type BodyKind =\n /** No body at all. Re-issue the same input. */\n | 'none'\n /** A value the caller still holds and `fetch` re-reads: string, Blob, buffer, FormData, params. */\n | 'reusable'\n /** A `Request` with a body, cloned before the first send. */\n | 'request-clone'\n /** Consumed by the first send and unrecoverable: a `ReadableStream`, or a failed clone. */\n | 'one-shot';\n\n/** What the policy predicate is told about a request. */\nexport type UpgradeReplayInfo = { url: string; method: string; bodyKind: BodyKind };\n\n/**\n * Replay policy.\n *\n * The object form is §13.5's: size and streaming limits. The three string and\n * function forms are §5.1's escape hatch, kept because `'idempotent-only'`\n * expresses a constraint the object form cannot — a backend whose challenge may\n * arrive after a side effect.\n */\nexport type ReplayPolicy =\n | {\n /** Refuse to buffer a `Request` body larger than this many bytes, when the size is known. */\n maxBodyBytes?: number;\n /** A stream is never replayable; the field exists so the intent is written down. */\n allowStreamingBody?: false;\n }\n | 'always'\n | 'idempotent-only'\n | ((info: UpgradeReplayInfo) => boolean);\n\n/** Hints the core passes at replay time. The planner applies them; it decides nothing. */\nexport type ReplayHints = {\n /**\n * The §5.5 credential swap, already resolved by the core.\n *\n * Applied if and only if the outgoing `Authorization` header value EXACTLY\n * equals `from`: equality with the token we hold in storage is what proves the\n * header was ours. Any other value is forwarded untouched, because rewriting a\n * header we do not own would be a security bug.\n *\n * Without this the replay re-sends the stale `Authorization` header, every\n * elevation ends in a surfaced second 401, and the feature silently never\n * works. Cookie mode needs none of it: the browser attaches the current —\n * now elevated — cookie by itself.\n */\n authorizationSwap?: { from: string; to: string };\n};\n\nexport type ReplayPlan = {\n /** False when the body cannot be re-sent, or the policy refused. */\n replayable: boolean;\n bodyKind: BodyKind;\n /** Why it is not replayable, for the `'replay-unsupported'` diagnostic. */\n reason?: string;\n /** What to pass to `next()` on the FIRST call. */\n forwardInput: RequestInfo | URL;\n forwardInit?: RequestInit;\n /**\n * The normalised, immutable snapshot of this request — §13.4 revision 6.\n *\n * EXPOSED rather than hidden as a plan-time url, so the scope check, the\n * challenge context, the diagnostics and the replay all read the same object\n * and the request that is *authorized* cannot differ from the one that is\n * *sent*.\n */\n snapshot: RequestSnapshot;\n /** Re-issues the original request, once. Rejects if `!replayable`. */\n replay: (next: FetchLike, hints?: ReplayHints) => Promise<Response>;\n};\n\nconst IDEMPOTENT_METHODS: ReadonlySet<string> = new Set(['GET', 'HEAD', 'OPTIONS']);\n\n/** Duck-typed so no DOM lib is needed and a polyfilled `Request` still matches. */\nconst isRequest = (input: unknown): input is Request =>\n typeof input === 'object' &&\n input !== null &&\n typeof (input as Request).url === 'string' &&\n typeof (input as Request).clone === 'function' &&\n 'method' in (input as Request);\n\n/** The URL of whatever shape the caller passed, without touching the network. */\nexport const urlOf = (input: RequestInfo | URL): string => {\n if (typeof input === 'string') {\n return input;\n }\n if (isRequest(input)) {\n return input.url;\n }\n return String(input);\n};\n\n/** The method of whatever shape the caller passed. `init` wins, as `fetch` itself has it. */\nexport const methodOf = (input: RequestInfo | URL, init?: RequestInit): string => {\n const method = init?.method ?? (isRequest(input) ? input.method : undefined);\n return (method ?? 'GET').toUpperCase();\n};\n\n/**\n * Classifies an `init` body.\n *\n * `reusable` is every value the caller still holds and `fetch` re-reads from:\n * strings, `Blob`, `ArrayBuffer`, typed arrays, `DataView`, `URLSearchParams`\n * and `FormData`. A `ReadableStream` is the one shape that is consumed by the\n * first send, and the `duplex: 'half'` form is no different.\n */\nconst classifyInitBody = (body: BodyInit | null | undefined): BodyKind => {\n if (body === null || body === undefined) {\n return 'none';\n }\n if (typeof body === 'string') {\n return 'reusable';\n }\n // A stream exposes `getReader`; nothing else in the BodyInit union does.\n if (typeof (body as ReadableStream).getReader === 'function') {\n return 'one-shot';\n }\n return 'reusable';\n};\n\n/** The byte length of a body when it is knowable without consuming it. */\nconst knownBodyBytes = (body: BodyInit | null | undefined): number | undefined => {\n if (typeof body === 'string') {\n // Close enough for a guard rail, and it never under-counts for ASCII.\n return body.length;\n }\n const sized = body as { size?: unknown; byteLength?: unknown } | null | undefined;\n if (typeof sized?.size === 'number') {\n return sized.size;\n }\n if (typeof sized?.byteLength === 'number') {\n return sized.byteLength;\n }\n return undefined;\n};\n\n/**\n * Applies the policy. The planner asks; it never decides what the policy means.\n *\n * Exported because the axios adapter needs the same answer about an\n * `AxiosRequestConfig`, which has no `Request` and no `BodyInit` to plan over.\n * Two readings of `'idempotent-only'` — one here and one there — is exactly the\n * per-surface drift the single core forbids, so there is one reading and both\n * surfaces call it.\n */\nexport const policyPermits = (policy: ReplayPolicy | undefined, info: UpgradeReplayInfo, bodyBytes?: number): boolean => {\n if (policy === undefined || policy === 'always') {\n return true;\n }\n if (policy === 'idempotent-only') {\n return IDEMPOTENT_METHODS.has(info.method);\n }\n if (typeof policy === 'function') {\n return policy(info);\n }\n if (policy.maxBodyBytes !== undefined && bodyBytes !== undefined && bodyBytes > policy.maxBodyBytes) {\n return false;\n }\n return true;\n};\n\n/** A plan that refuses, so the core can return the original response untouched. */\nconst refuse = (\n bodyKind: BodyKind,\n reason: string,\n forwardInput: RequestInfo | URL,\n snapshot: RequestSnapshot,\n forwardInit?: RequestInit,\n): ReplayPlan => ({\n replayable: false,\n bodyKind,\n reason,\n forwardInput,\n forwardInit,\n snapshot,\n replay: () => Promise.reject(new Error(`This request cannot be replayed: ${reason}`)),\n});\n\n/**\n * The header set the replay sends: the snapshot's, with the credential swapped.\n *\n * ## Why precedence is resolved ONCE, here\n *\n * `fetch(input, init)` does not merge header sets. When `init.headers` is\n * present it **replaces** a `Request`'s headers entirely; when it is absent the\n * `Request`'s own headers are used. Applying the §5.5 swap to one of the two\n * objects and letting `fetch` pick the other is how a replay went out carrying\n * the PRE-elevation `Authorization` and the whole feature silently did nothing:\n * the elevated credential was written into the set that was then discarded.\n *\n * So the snapshot captures the EFFECTIVE set once, before dispatch, by the\n * `fetch` rule above, the swap is applied to that one set, and the replay sends\n * it explicitly. There is no second set left for anything to prefer.\n */\nconst replayHeaders = (snapshot: RequestSnapshot, hints?: ReplayHints): Record<string, string> =>\n headersToObject(swapAuthorization(snapshot.headers, hints?.authorizationSwap));\n\n/** The caller's `init` minus `headers`, which `replayHeaders` now owns outright. */\nconst initWithoutHeaders = (init: RequestInit | undefined): RequestInit | undefined => {\n if (!init || !('headers' in init)) {\n return init;\n }\n const { headers: _headers, ...rest } = init;\n return rest;\n};\n\n/**\n * The effective header set `fetch` itself would use, per the rule above.\n *\n * An explicit `undefined` counts as absent, which is how WebIDL reads a\n * dictionary member set to `undefined`.\n */\nconst effectiveHeaders = (input: RequestInfo | URL, init?: RequestInit): unknown => {\n if (init && 'headers' in init && init.headers !== undefined) {\n return init.headers;\n }\n return isRequest(input) ? input.headers : undefined;\n};\n\n/**\n * Captures an `init` body by VALUE, once, before the first send.\n *\n * Snapshotting the URL and the method was not enough: a `FormData` the caller\n * still holds is a live object, and `fetch` re-reads it on every send. A\n * transfer planned to Alice, `form.set('to', 'Mallory')` while the prompt is\n * open, and the replay carries Mallory — with the freshly elevated credential\n * attached, against a request no scope or policy decision ever saw. Same TOCTOU\n * class as the re-pointable `URL`, on the other half of the request.\n *\n * Only the MUTABLE shapes are copied. Strings and `Blob`s are immutable, so\n * holding the reference already is a snapshot; `ArrayBuffer`s, views,\n * `URLSearchParams` and `FormData` are not. The `FormData` copy is shallow by\n * design: its values are strings and `Blob`s, both immutable, so re-appending\n * them costs no buffering. A `ReadableStream` never reaches here —\n * `classifyInitBody` has already refused it as `one-shot`.\n *\n * The copy happens inside `planReplay`, AFTER `policyPermits`, so the existing\n * `maxBodyBytes` limit still governs what the SDK is willing to hold.\n */\nconst snapshotBody = (body: BodyInit | null): BodyInit | null => {\n if (body === null || typeof body === 'string') {\n return body;\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n const copy = new FormData();\n for (const [name, value] of body as unknown as Iterable<[string, FormDataEntryValue]>) {\n copy.append(name, value as string | Blob);\n }\n return copy;\n }\n if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) {\n return new URLSearchParams(body);\n }\n if (body instanceof ArrayBuffer) {\n return body.slice(0);\n }\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(body.buffer, body.byteOffset, body.byteLength).slice();\n }\n // A `Blob` (and anything exotic a polyfill hands us) is forwarded as given.\n return body;\n};\n\n/**\n * A `Request` carrying the snapshot's effective, credential-swapped headers.\n *\n * `Request` headers are immutable once constructed, so applying them means\n * building a new `Request` from the clone. Everything else is copied by the\n * `Request` constructor's own init form, which is the only way to preserve\n * `mode`, `credentials`, `cache`, `redirect`, `referrer`, `integrity` and the\n * signal without naming them.\n */\nconst requestWithSnapshotHeaders = (request: Request, snapshot: RequestSnapshot, hints?: ReplayHints): Request =>\n new Request(request, { headers: replayHeaders(snapshot, hints) });\n\n/**\n * Decides whether a request can be replayed, and produces the thunk that does it.\n *\n * Call it BEFORE the first send. The returned `forwardInput` / `forwardInit` are\n * what the first send must use: for a `Request` with a body they are the\n * original, with the spare clone held for the replay.\n */\nexport const planReplay = (input: RequestInfo | URL, init?: RequestInit, policy?: ReplayPolicy): ReplayPlan => {\n const url = urlOf(input);\n const method = methodOf(input, init);\n const headers = effectiveHeaders(input, init);\n\n /** One snapshot per plan, taken before the first send. */\n const snapshotFor = (bodyKind: BodyKind, body?: unknown): RequestSnapshot =>\n createRequestSnapshot({ url, method, headers, bodyKind, ...(body === undefined ? {} : { body }) });\n\n if (isRequest(input)) {\n // `body === null` covers GET/HEAD and any bodyless request: the same\n // `Request` can simply be re-issued.\n if (input.body === null || input.body === undefined) {\n const snapshot = snapshotFor('none');\n const info: UpgradeReplayInfo = { url, method, bodyKind: 'none' };\n if (!policyPermits(policy, info)) {\n return refuse('none', 'the replay policy refused this request', input, snapshot, init);\n }\n return {\n replayable: true,\n bodyKind: 'none',\n forwardInput: input,\n forwardInit: init,\n snapshot,\n // The snapshot's effective header set is applied to the `Request` and\n // `init.headers` is dropped, so `fetch` has only one set to choose\n // from. See `replayHeaders` for why that matters.\n replay: (next, hints) => next(requestWithSnapshotHeaders(input, snapshot, hints), initWithoutHeaders(init)),\n };\n }\n\n const snapshot = snapshotFor('request-clone');\n const info: UpgradeReplayInfo = { url, method, bodyKind: 'request-clone' };\n if (!policyPermits(policy, info)) {\n return refuse('request-clone', 'the replay policy refused this request', input, snapshot, init);\n }\n\n // The clone must be taken NOW, while the body is unread. We pay the buffering\n // cost only for `Request`-with-body inputs, which are the minority; string +\n // init is the common shape and costs nothing.\n let spare: Request;\n try {\n spare = input.clone();\n } catch {\n // `bodyUsed` already — nothing to recover. Degrade rather than throw: the\n // caller still gets their original response, just no step-up.\n return refuse('one-shot', 'the Request body was already consumed and cannot be cloned', input, snapshot, init);\n }\n\n return {\n replayable: true,\n bodyKind: 'request-clone',\n forwardInput: input,\n forwardInit: init,\n snapshot,\n replay: (next, hints) => next(requestWithSnapshotHeaders(spare, snapshot, hints), initWithoutHeaders(init)),\n };\n }\n\n const bodyKind = classifyInitBody(init?.body);\n if (bodyKind === 'one-shot') {\n return refuse('one-shot', 'a streaming request body is consumed by the first send', input, snapshotFor('one-shot'), init);\n }\n\n const info: UpgradeReplayInfo = { url, method, bodyKind };\n if (!policyPermits(policy, info, knownBodyBytes(init?.body))) {\n return refuse(bodyKind, 'the replay policy refused this request', input, snapshotFor(bodyKind), init);\n }\n\n // Captured ONCE, here, before the first send — not reread from the caller's\n // live `init` when the replay goes out. See `snapshotBody`.\n const body = init && 'body' in init ? snapshotBody(init.body ?? null) : undefined;\n const snapshot = snapshotFor(bodyKind, body);\n\n return {\n replayable: true,\n bodyKind,\n forwardInput: input,\n forwardInit: init,\n snapshot,\n // The SNAPSHOT is re-issued, never the caller's live `input`.\n //\n // The url is the snapshot's NORMALISED `href` — the absolute URL the scope\n // decision was actually made on. Dispatching the caller's object instead\n // would let a `URL` instance be re-pointed (`input.href = …`) while the\n // prompt is open, sending the elevated credential somewhere no scope\n // decision ever saw; dispatching the raw string would re-resolve it, and a\n // `<base href>` changing mid-flight would move it. The `Request` branches\n // above need no equivalent: a `Request`'s url and method are immutable and\n // its body clone is taken eagerly.\n //\n // The BODY and the HEADERS are snapshotted for the same reason and by the\n // same rule: every `reusable` body shape is reread by `fetch` on each send,\n // and a `Headers` instance the caller still holds is mutable. The rest of\n // `init` is re-sent as given — the caller's `signal` is forwarded\n // unchanged, so an abort during the replay behaves identically to an abort\n // during the original.\n replay: (next, hints) =>\n next(snapshot.dispatchUrl, {\n ...initWithoutHeaders(init),\n ...(body === undefined ? {} : { body: body as BodyInit | null }),\n headers: replayHeaders(snapshot, hints),\n method: snapshot.method,\n }),\n };\n};\n","/**\n * The AXIOS adapter over `handleChallenge`. Two of the four surfaces use it.\n *\n * **NO LOGIC IN AN ADAPTER.** It builds a `ChallengeContext` from an\n * `AxiosError` and settles through `settleAxiosOutcome`; it parses no\n * challenge, compares no bar, decides no scope, counts no retry and chooses no\n * error class. The replay-once guard in particular is NOT here: the replay goes\n * back through `request(config)` and re-enters this very interceptor, so the\n * guard has to be surface-independent, and it is — the core reads and writes\n * `markers.replayed`, and this file only says where that marker lives (on the\n * request config, where axios hands it back unchanged).\n *\n * ## Why the parameter is structurally typed\n *\n * `ResponseInterceptorHost` is declared by us and satisfied by any axios\n * instance. We never name axios' own types in the signature, so a customer on a\n * different axios major — or on a fetch-based client that mimics the\n * interceptor API — attaches without a type error, and this package's axios\n * version is not a constraint on theirs.\n *\n * ## Why it is the error interceptor\n *\n * axios' own contract, expressed in `validateStatus`, is that a non-2xx\n * response rejects, so a 401/403 arrives as an `AxiosError`. Resolving a\n * declined step-up would invert that contract: every caller's post-`await`\n * success path would run after the user clicked \"Not now\".\n */\n\nimport {\n type AbortView,\n type ChallengeContext,\n type UpgradeInterceptorOptions,\n type UpgradeInterceptorOptionsSource,\n handleChallenge,\n resolveInterceptorOptions,\n settleAxiosOutcome,\n} from './core';\nimport { isUpgradeSdkError } from './errors';\nimport { hasUpgradeReplayedMarker, hasUpgradeTransactionMarker, markUpgradeReplayed } from './markers';\nimport { type ReplayHints, type UpgradeReplayInfo, policyPermits } from './replay';\nimport { type RequestSnapshot, createRequestSnapshot, headersToObject, swapAuthorization } from './snapshot';\n\n/**\n * The only thing we require of a client: that it lets us add and remove a\n * response interceptor. Duck-typed deliberately — see the module comment.\n */\nexport type ResponseInterceptorHost = {\n interceptors: {\n response: {\n // biome-ignore lint/suspicious/noExplicitAny: structural mirror of axios' own looser signature; narrowing it here would reject real axios instances\n use(onFulfilled?: (value: any) => any, onRejected?: (error: any) => any): number;\n eject(id: number): void;\n };\n };\n};\n\n/** Re-issues a request from a config. Supplied by whoever owns the client. */\nexport type AxiosReissue = (config: AxiosConfigLike) => Promise<unknown>;\n\n/** The subset of `AxiosRequestConfig` this adapter reads. */\nexport type AxiosConfigLike = {\n url?: string;\n baseURL?: string;\n method?: string;\n data?: unknown;\n headers?: unknown;\n /**\n * The caller's own signal, forwarded to the core untouched. Typed as the\n * core's `AbortView` so an `AbortSignal`'s listener pair types through: that\n * pair is what lets the core settle this caller at abort time while a proof\n * is still pending (REQ-C12). A plain `{ aborted }` object is still accepted,\n * and then the core reads the flag after the elevation as it always did.\n */\n signal?: AbortView;\n};\n\n/** The subset of `AxiosResponse` this adapter reads. */\ntype AxiosResponseLike = {\n status: number;\n headers?: unknown;\n data?: unknown;\n config?: AxiosConfigLike;\n};\n\n/** The subset of `AxiosError` this adapter reads. */\ntype AxiosErrorLike = {\n response?: AxiosResponseLike;\n config?: AxiosConfigLike;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\n/**\n * Reads a header off an axios headers bag, in the callback shape the one parser\n * takes.\n *\n * axios hands back a plain object, an `AxiosHeaders` instance or (in node) an\n * array value per name. All three are flattened here, which is the whole reason\n * the parser takes a callback instead of a DOM `Headers`.\n */\nconst headerReader =\n (headers: unknown) =>\n (name: string): string | null | undefined => {\n if (!isRecord(headers)) {\n return undefined;\n }\n const direct = headers[name] ?? headers[name.toLowerCase()];\n const value =\n direct ??\n (typeof (headers as { get?: (n: string) => unknown }).get === 'function'\n ? (headers as { get: (n: string) => unknown }).get(name)\n : undefined);\n if (Array.isArray(value)) {\n return value.join(', ');\n }\n return typeof value === 'string' ? value : undefined;\n };\n\n/**\n * The request's absolute URL, as far as the config states it.\n *\n * A relative `url` with no `baseURL` stays relative, which is what lets a SPA's\n * `backend: '/api'` declaration match: the scope predicate understands a path\n * prefix, and inventing an origin here would be the derived model the design\n * removed.\n */\nconst urlOf = (config: AxiosConfigLike | undefined): string => {\n const url = config?.url ?? '';\n const base = config?.baseURL;\n if (!base) {\n return url;\n }\n // axios' own `isAbsoluteURL` is `/^([a-z][a-z\\d+\\-.]*:)?\\/\\//i`, so it treats\n // a PROTOCOL-RELATIVE url as absolute and does not prepend `baseURL` to\n // `//evil.example/x`. Reading it as a path and joining it onto `baseURL` would\n // hand the scope predicate a same-origin-looking url for a request axios\n // actually sends off-origin. Same regex here, so the two cannot disagree.\n if (/^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(url)) {\n return url;\n }\n return `${base.replace(/\\/+$/, '')}/${url.replace(/^\\/+/, '')}`;\n};\n\nconst methodOf = (config: AxiosConfigLike | undefined): string => (config?.method ?? 'GET').toUpperCase();\n\n/** How a config's body can be re-sent. axios serialises `data` on every send. */\nconst bodyKindOf = (data: unknown): UpgradeReplayInfo['bodyKind'] => {\n if (data === undefined || data === null) {\n return 'none';\n }\n // A node stream or a web stream is consumed by the first send.\n if (isRecord(data) && (typeof data.pipe === 'function' || typeof data.getReader === 'function')) {\n return 'one-shot';\n }\n return 'reusable';\n};\n\n/**\n * The config a replay is issued with: the original, re-pointed at the SNAPSHOT.\n *\n * url, method, headers and data all come from the frozen snapshot rather than\n * from the live config object, so a config the caller still holds — axios\n * instances are routinely built once and reused — cannot change where the\n * elevated credential goes or what it carries. The credential swap is applied\n * to the snapshot's header set by exact match: equality with the token we hold\n * is what proves the header was ours, and rewriting a header we do not own\n * would be a security bug.\n *\n * `baseURL` is cleared when the snapshot resolved to an absolute URL, because\n * the snapshot's `href` already includes it; leaving both would depend on\n * axios' `isAbsoluteURL` to not join them twice.\n */\nconst replayConfig = (config: AxiosConfigLike, snapshot: RequestSnapshot, hints?: ReplayHints): AxiosConfigLike => {\n const headers = headersToObject(swapAuthorization(snapshot.headers, hints?.authorizationSwap));\n const absolute = snapshot.dispatchUrl === snapshot.href && snapshot.resolved;\n return {\n ...config,\n ...(absolute ? { baseURL: undefined } : {}),\n url: snapshot.dispatchUrl,\n method: snapshot.method,\n headers,\n ...('body' in snapshot ? { data: snapshot.body } : {}),\n };\n};\n\n/**\n * Wraps an axios response as a `ChallengeContext`.\n *\n * The markers live on the request config: axios hands `error.config` back\n * unchanged, and a non-enumerable property would not survive `mergeConfig`'s\n * `Object.keys` walk (see `markers.ts`). Writing `markers.replayed` therefore\n * means stamping the config of the request we are about to re-issue, which is\n * what the `replay` thunk below does.\n */\nconst contextFor = (\n response: AxiosResponseLike | undefined,\n config: AxiosConfigLike | undefined,\n raw: unknown,\n reissue: AxiosReissue,\n options: UpgradeInterceptorOptions,\n /** The FIRST send's snapshot, threaded through every replayed context. */\n inherited?: RequestSnapshot,\n): ChallengeContext => {\n const effective = config ?? response?.config;\n const bodyKind = bodyKindOf(effective?.data);\n // Taken once per intercepted request and inherited by the replayed context:\n // a second snapshot taken off the replay's own config would be a second\n // description of one request, which is the thing §13.4 revision 6 forbids.\n const snapshot =\n inherited ??\n createRequestSnapshot({\n url: urlOf(effective),\n method: methodOf(effective),\n headers: effective?.headers,\n bodyKind,\n body: effective?.data,\n });\n const info: UpgradeReplayInfo = { url: snapshot.href, method: snapshot.method, bodyKind };\n const permitted = bodyKind !== 'one-shot' && policyPermits(options.replay, info);\n\n return {\n status: response?.status ?? 0,\n getHeader: headerReader(response?.headers),\n // Pre-read by axios into `data`; nothing is consumed by reading it again,\n // and the core still decides whether reading it is in scope.\n readBody: async () => response?.data,\n request: snapshot,\n replay: async (hints?: ReplayHints) => {\n const next = markUpgradeReplayed(replayConfig(effective ?? {}, snapshot, hints));\n try {\n const replayed = (await reissue(next)) as AxiosResponseLike;\n return contextFor(replayed, replayed?.config ?? next, replayed, reissue, options, snapshot);\n } catch (e) {\n // axios REJECTS a non-2xx, so the replay's own 401 — the second\n // challenge, which is the normal AAL3/TOTP-only path — arrives here as\n // a rejection rather than as a response. Handing it to the core as an\n // exchange is the only way the core can classify it; letting it escape\n // sent a raw `AxiosError`, complete with `config` and `response`, over\n // the SDK's public edge where §13.3 requires `upgrade_insufficient`.\n //\n // No logic is added: this adapter still parses nothing and compares no\n // bar. It only translates axios' rejection convention into the one\n // exchange shape the core reads, and flags that the exchange was\n // delivered as a rejection so the settlement can honour the same\n // convention on the way back out.\n //\n // A rejection with no response is not an exchange — a replay that never\n // reached the server is the caller's error, not a step-up outcome — and\n // neither is a settled upgrade outcome, which is what re-entering this\n // same interceptor on the attached surfaces produces.\n const failure = (isRecord(e) ? e : {}) as AxiosErrorLike;\n if (!failure.response || typeof failure.response.status !== 'number' || isUpgradeSdkError(e)) {\n throw e;\n }\n const replayed = contextFor(failure.response, failure.config ?? next, e, reissue, options, snapshot);\n return { ...replayed, rejected: true };\n }\n },\n markers: {\n ...(hasUpgradeTransactionMarker(effective) ? { upgradeTransaction: true as const } : {}),\n ...(hasUpgradeReplayedMarker(effective) ? { replayed: true as const } : {}),\n },\n raw,\n replayable: permitted,\n replayUnsupportedReason:\n bodyKind === 'one-shot'\n ? 'a streaming request body is consumed by the first send'\n : 'the replay policy refused this request',\n signal: effective?.signal,\n };\n};\n\n/**\n * The rejection handler itself: an `AxiosError` in, a replayed response or a\n * tagged rejection out.\n *\n * Shared by `attachUpgradeAuth` (the customer's own instance) and by the SDK's\n * internal seam, so both surfaces settle from the same table.\n */\nexport const createAxiosUpgradeHandler =\n (source: UpgradeInterceptorOptionsSource, reissue: AxiosReissue) =>\n async (error: unknown): Promise<unknown> => {\n // Resolved PER CHALLENGE. A handler built at `enable()` time that captured\n // the RESOLVED options would freeze the interception scope at attach time,\n // which is how a module-scope client ended up pinned to `backend: []`.\n const options = resolveInterceptorOptions(source);\n const typed = (isRecord(error) ? error : {}) as AxiosErrorLike;\n const ctx = contextFor(typed.response, typed.config, typed.response, reissue, options);\n\n const outcome = await handleChallenge(ctx, options);\n const settlement = settleAxiosOutcome<unknown>(outcome, error);\n if (settlement.settle === 'reject') {\n throw settlement.error;\n }\n return settlement.value;\n };\n\n/**\n * Attaches step-up handling to a client the developer already owns, MUTATING\n * it, and returns a disposer that ejects exactly the interceptor it added.\n *\n * It returns the disposer rather than the instance on purpose: it mutates, and\n * returning the instance would invite `const api = attachAuth(axios.create())`\n * and read as if it had built something. The disposer is also what a React\n * effect needs.\n *\n * `reissue` defaults to the host's own `request`, duck-typed, so the replay\n * goes back through the customer's interceptors, `baseURL` and adapter rather\n * than through some instance of ours.\n */\nexport const attachUpgradeAuth = (\n host: ResponseInterceptorHost,\n source: UpgradeInterceptorOptionsSource,\n reissue?: AxiosReissue,\n): (() => void) => {\n const own = (host as unknown as { request?: (config: AxiosConfigLike) => Promise<unknown> }).request;\n const send: AxiosReissue =\n reissue ??\n (typeof own === 'function'\n ? (config) => own.call(host, config)\n : () => Promise.reject(new Error('Passflow: this client exposes no request() to replay through.')));\n\n const id = host.interceptors.response.use(undefined, createAxiosUpgradeHandler(source, send));\n let ejected = false;\n return () => {\n if (ejected) {\n return;\n }\n ejected = true;\n host.interceptors.response.eject(id);\n };\n};\n","/**\n * The FETCH adapter over `handleChallenge`. One of four, and the thinnest.\n *\n * **NO LOGIC IN AN ADAPTER.** Everything here is translation: a `Response`\n * becomes a `ChallengeContext` on the way in, and a `CoreOutcome` becomes a\n * `Response` (or a throw) on the way out, through `settleFetchOutcome`. It\n * parses no challenge, compares no bar, decides no scope, counts no retry and\n * chooses no error class. If something in this file ever looks like a decision,\n * it belongs in `core.ts` instead — a row that passes here and fails on the\n * axios surface is a leak out of the core, not a difference between transports.\n *\n * The one genuinely transport-shaped obligation it carries is the body: a\n * replay needs the request body while it is still unread, so `planReplay` runs\n * BEFORE the first call and the core is told whether a replay is even possible\n * before any prompt opens. The caller's own response is never consumed — every\n * body read is taken from `clone()`, so `bodyUsed === false` holds on the object\n * the caller receives.\n *\n * SSR-safe: no global is read at module scope. `withAuth()`'s default `next` is\n * resolved inside the call, not here.\n */\n\nimport {\n type AbortView,\n type ChallengeContext,\n type UpgradeInterceptorOptionsSource,\n handleChallenge,\n resolveInterceptorOptions,\n settleFetchOutcome,\n} from './core';\nimport { type FetchLike, type ReplayHints, type ReplayPlan, planReplay } from './replay';\n\n/**\n * How many upgrade-aware fetch layers are DISPATCHING through this one, right\n * now, on this synchronous call path.\n *\n * ## The defect\n *\n * `markers` on the fetch surface is a per-wrapper object and is deliberately\n * never on the wire. That is right for one wrapper and wrong for two: with\n * `interceptFetch: true` AND an app-held `withAuth(fetch)` on the same path,\n * the inner layer elevates and replays, and if the server re-challenges the\n * replay the OUTER layer sees a 401 whose markers are empty and elevates again.\n * Two prompts and two challenges for one user action, against a server budget\n * that caps at a few attempts — the single-flight NFR, violated by stacking.\n *\n * ## Why a counter, and why this one\n *\n * Three fixes were on the table. (1) Tag the in-flight request: the only\n * identity both layers share is the caller's `input`/`init` objects, and\n * `fetch('/x')` has neither, so a `WeakSet` keyed on them silently misses the\n * commonest call shape — and a header would make the marker transmissible,\n * which is prohibited. (2) Have `registerFetchMiddleware` refuse to install\n * when a wrapper is already active on that path: it cannot know, because an\n * app-held `withAuth` wrapper is not registered anywhere and the global patch\n * cannot see it. (3) This: make \"a layer above me already owns this call\"\n * observable, and pass through when it is.\n *\n * The counter is safe against concurrency because it is only ever held across\n * SYNCHRONOUS code. `dispatch` increments, calls `next`, and decrements in\n * `finally` — all before the first `await` — so the window is exactly the inner\n * layer's own synchronous prologue, where it reads the counter. No other\n * request can interleave into a synchronous span, so this is a dynamic extent,\n * not ambient state, and it is SSR-safe for the same reason: it is never\n * non-zero across a suspension point.\n *\n * The limit, stated rather than hidden: an intervening wrapper that `await`s\n * before calling `fetch` breaks the extent, and both layers then elevate as\n * they do today. The fix narrows the defect to that case instead of closing it\n * absolutely — a marker that closed it absolutely would have to travel with the\n * request, which is the thing we must not do. The axios seam carries its own\n * config marker and is a separate channel; a stacked fetch-plus-axios path is\n * not covered here.\n */\nlet dispatchingLayers = 0;\n\n/**\n * A live view of the caller's signal.\n *\n * Read through getters because the core checks it AFTER awaiting the elevation:\n * a boolean copied at call time would always say \"not aborted\". The listener\n * pair is forwarded as well — that is what lets the core RACE the abort against\n * a pending proof (REQ-C12) instead of discovering it once the proof settles.\n * Both are bound to the caller's own signal; this adapter never aborts it and\n * never creates a signal of its own.\n */\nconst abortViewOf = (input: RequestInfo | URL, init?: RequestInit): AbortView | undefined => {\n const signal =\n init?.signal ?? (typeof input === 'object' && input !== null ? (input as Request).signal : undefined) ?? undefined;\n if (!signal) {\n return undefined;\n }\n return {\n get aborted(): boolean {\n return signal.aborted;\n },\n get reason(): unknown {\n return (signal as AbortSignal & { reason?: unknown }).reason;\n },\n ...(typeof signal.addEventListener === 'function'\n ? {\n addEventListener: (type: 'abort', listener: () => void) => signal.addEventListener(type, listener),\n removeEventListener: (type: 'abort', listener: () => void) => signal.removeEventListener(type, listener),\n }\n : {}),\n };\n};\n\n/** Reads a header off a `Response`, in the callback shape the one parser takes. */\nconst headerReader =\n (response: Response) =>\n (name: string): string | null | undefined =>\n response.headers?.get(name);\n\n/**\n * Wraps one `Response` as a `ChallengeContext`.\n *\n * `markers` is shared with the original context, which is the fetch surface's\n * answer to marker storage: the wrapper owns the call, so a per-request object\n * is the whole mechanism. The replayed response re-uses it, so the core's\n * re-entry guard reads the same state it wrote.\n */\nconst contextFor = (\n response: Response,\n plan: ReplayPlan,\n next: FetchLike,\n markers: ChallengeContext['markers'],\n abort: AbortView | undefined,\n): ChallengeContext => ({\n status: response.status,\n getHeader: headerReader(response),\n // A CLONE, always. The response the caller receives must still have an unread\n // body, and the core calls this only when the scope and the channel policy\n // both permit a body read.\n readBody: () => response.clone().json(),\n // The PLAN's snapshot, never a live `urlOf(input)` read at challenge time.\n // Reading the caller's input here was the asymmetry §13.4 revision 6 closes:\n // the request that was authorized and the request that is replayed must be\n // the same request, and the only way to guarantee that is for both to read\n // the one frozen object.\n request: plan.snapshot,\n replay: async (hints?: ReplayHints) => {\n const replayed = await plan.replay(next, hints);\n return contextFor(replayed, plan, next, markers, abort);\n },\n markers,\n raw: response,\n replayable: plan.replayable,\n replayUnsupportedReason: plan.reason,\n signal: abort,\n responseType: response.type,\n});\n\n/**\n * Wraps a `fetch`, returning a new function. Installs nothing and touches no\n * global — this is the SSR- and React-Native-safe entry point, and the one the\n * global patch itself is built from.\n */\nexport const upgradeFetch = (next: FetchLike, source: UpgradeInterceptorOptionsSource): FetchLike => {\n /**\n * Sends through `next` while announcing that THIS layer owns the call.\n *\n * Used for the first send and for every replay, so a nested layer passes the\n * replay through too — otherwise the inner layer would claim the replay that\n * this layer's elevation produced, which is the stacking defect itself.\n */\n const dispatch: FetchLike = (nestedInput, nestedInit) => {\n dispatchingLayers += 1;\n try {\n return next(nestedInput, nestedInit);\n } finally {\n dispatchingLayers -= 1;\n }\n };\n\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (dispatchingLayers > 0) {\n // A layer above us is handling this call: it plans the replay, it owns the\n // elevation, and one challenge is spent. Pure pass-through — no plan, no\n // body clone, no prompt. `dispatch` keeps the claim for layers below us.\n return dispatch(input, init);\n }\n\n // Resolved PER REQUEST, not at wrap time: a `withAuth()` wrapper built\n // before `enable()` must still see the scope `enable()` installed, and a\n // later `enable()` with a narrower `backend` must narrow this wrapper too.\n const options = resolveInterceptorOptions(source);\n\n // Before the first send: a `Request` body can only be cloned while unread.\n const plan = planReplay(input, init, options.replay);\n const response = await dispatch(plan.forwardInput, plan.forwardInit);\n\n const ctx = contextFor(response, plan, dispatch, {}, abortViewOf(input, init));\n const outcome = await handleChallenge(ctx, options);\n\n const settlement = settleFetchOutcome(outcome, response, options);\n if (settlement.settle === 'reject') {\n throw settlement.error;\n }\n return settlement.value;\n };\n};\n","/**\n * `createClient` — the greenfield surface, for a developer with no HTTP client\n * of their own.\n *\n * It is **not a fourth code path**: it is `axios.create(config)` followed by the\n * axios adapter, which is why it cannot drift from `attachAuth`. The attach is\n * passed in rather than imported so this module holds no scope, no controller\n * and no options — only the constructor.\n *\n * It also costs no new dependency: `axios` is already a hard dependency of this\n * package. This is the one place axios' own types are deliberately back on the\n * public surface — `attachAuth`'s structural `ResponseInterceptorHost` exists to\n * avoid exactly that, and the asymmetry is the point: a developer asking us to\n * BUILD their client has accepted axios, while one handing us their own must not\n * be pinned to our major.\n */\n\nimport axios, { type AxiosInstance, type CreateAxiosDefaults } from 'axios';\n\nimport type { ResponseInterceptorHost } from './axios-adapter';\n\n/**\n * axios' own create options plus nothing of ours, so `baseURL`, `headers`,\n * `timeout`, `adapter` and interceptor ordering all stay the customer's.\n *\n * A `baseURL` is NOT read as a declared backend: that would be the derived\n * interception model through a side door. An un-declared `baseURL` gets a\n * `'challenge-out-of-scope'` diagnostic naming `backend` as the fix.\n */\nexport type CreateClientConfig = CreateAxiosDefaults;\n\nexport const createUpgradeClient = (\n config: CreateClientConfig | undefined,\n attachAuth: (host: ResponseInterceptorHost) => () => void,\n): AxiosInstance => {\n const instance = axios.create(config);\n attachAuth(instance);\n return instance;\n};\n","/**\n * One `fetch` patch point, many middlewares.\n *\n * This module exists because of a bug, not a requirement (architecture §4.3,\n * reaffirmed by §13.5's \"what survives supersession\"). Two features that each do\n * `globalThis.fetch = wrapper` in an effect and restore their captured original\n * in cleanup compose as `A(B(native))`, and whichever cleanup runs first assigns\n * a fetch that predates the other patch. React guarantees no cleanup ordering\n * across sibling effects, nor across a parent/child boundary under Strict Mode\n * double-invocation, so one of the two interceptors silently disappears —\n * non-deterministically, and only in composed mounts.\n *\n * The fix is to make the patch a shared resource with reference-counted\n * ownership: the registry installs exactly one wrapper on the first\n * registration, composes the registered middlewares by priority on every call,\n * and removes the wrapper on the last removal. **Teardown is the registry's\n * responsibility, not any caller's** — no caller captures or restores\n * `globalThis.fetch` itself, which is precisely the ownership mistake being\n * retired.\n *\n * Composition is a Decorator chain, not a Chain of Responsibility: every\n * middleware MUST delegate to `next`. A link that may legitimately swallow a\n * request would mean a stranger's `fetch` call can vanish inside our wrapper, so\n * non-delegation is detected per invocation and reported as a named error rather\n * than left to surface as a hung promise (§9.4/R7).\n *\n * SSR safety is a stated non-functional requirement and is structural here:\n * `globalThis` is read only inside `install()` and inside the state reader,\n * never at module scope. Importing this module on a server touches no global and\n * throws nothing; `registerFetchMiddleware` returns a no-op unregister when\n * there is no `fetch` to patch (§9.4/R6).\n *\n * The module is generic. It knows nothing about upgrades, OIDC or Passflow\n * endpoints — only about ordering, ownership and delegation.\n */\n\n/** A `fetch`-shaped function: the platform `fetch`, or the next link in the chain. */\nexport type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * A registered interception layer.\n *\n * It receives the next link explicitly rather than closing over a captured\n * global, which is what lets the chain be rebuilt per call and reordered by\n * priority without any middleware knowing its neighbours.\n */\nexport type FetchMiddleware = (next: FetchLike, input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nexport type RegisterOptions = {\n /** Lower runs OUTER (further from the network). Ties break by registration order. */\n priority: number;\n /** Diagnostic label, surfaced in the error thrown when a middleware fails to delegate. */\n name: string;\n};\n\n/**\n * Outer layer. The OIDC AuthFlow interceptor must observe the response the\n * caller will actually receive — including a replayed one — because it follows a\n * `redirect_url` out of that body. Placed inside the upgrade middleware it would\n * inspect the raw 401, do nothing, and the page would then navigate on a\n * response the caller never saw.\n */\nexport const FETCH_PRIORITY_OIDC = 10;\n\n/**\n * Inner layer. The upgrade middleware must see the raw 401 and replace it before\n * anything upstream forms an opinion about it.\n */\nexport const FETCH_PRIORITY_UPGRADE = 20;\n\n/** Thrown when a registered middleware resolves without ever calling `next`. */\nexport class FetchMiddlewareContractError extends Error {\n /** The `name` the offending middleware was registered under. */\n readonly middlewareName: string;\n\n constructor(middlewareName: string) {\n super(\n `Passflow fetch middleware '${middlewareName}' resolved without calling next(). ` +\n 'Every registered middleware must delegate exactly once; swallowing a request would ' +\n \"drop a caller's fetch inside the Passflow wrapper.\",\n );\n this.name = 'FetchMiddlewareContractError';\n this.middlewareName = middlewareName;\n }\n}\n\ntype RegistryEntry = {\n readonly middleware: FetchMiddleware;\n readonly name: string;\n readonly priority: number;\n /** Registration sequence, used only as a stable tie-break within one priority. */\n readonly seq: number;\n};\n\n/** Registered entries, unsorted. Sorting happens per call so order is never stale. */\nlet entries: RegistryEntry[] = [];\n\n/** Monotonic registration counter. Never reset while entries are live. */\nlet nextSeq = 0;\n\n/** True once we have assigned our wrapper and have not restored over it. */\nlet installed = false;\n\n/** The `fetch` captured at install time — the innermost link of every chain. */\nlet capturedFetch: FetchLike | undefined;\n\n/** The exact function we assigned to `globalThis.fetch`, used for identity checks. */\nlet ourWrapper: FetchLike | undefined;\n\n/** Outer-first: ascending priority, then registration order. */\nconst byPriority = (a: RegistryEntry, b: RegistryEntry): number => a.priority - b.priority || a.seq - b.seq;\n\nconst globalScope = (): typeof globalThis | undefined => (typeof globalThis === 'undefined' ? undefined : globalThis);\n\n/**\n * Runs the chain from `index` outward-in, ending at the captured fetch.\n *\n * `chain` is a snapshot taken when the call started: a registration or removal\n * that lands mid-flight changes the next call, never this one. Each frame\n * records whether its middleware delegated, so a non-delegating middleware is\n * named in the error rather than leaving the caller to guess which layer ate the\n * request.\n */\nconst dispatch = async (\n chain: readonly RegistryEntry[],\n index: number,\n input: RequestInfo | URL,\n init?: RequestInit,\n): Promise<Response> => {\n if (index >= chain.length) {\n const target = capturedFetch;\n if (!target) {\n // Unreachable while installed: the capture and the assignment happen\n // together in install(). Explicit rather than a TypeError on undefined.\n throw new Error('Passflow fetch registry: no captured fetch to delegate to.');\n }\n return target(input, init);\n }\n\n const entry = chain[index];\n if (!entry) {\n return dispatch(chain, chain.length, input, init);\n }\n\n let delegated = false;\n const next: FetchLike = (nextInput, nextInit) => {\n delegated = true;\n return dispatch(chain, index + 1, nextInput, nextInit);\n };\n\n const response = await entry.middleware(next, input, init);\n if (!delegated) {\n throw new FetchMiddlewareContractError(entry.name);\n }\n return response;\n};\n\n/**\n * Assigns our single wrapper, capturing the current `fetch` as the chain's\n * innermost link.\n *\n * Returns false when there is nothing to patch — no global scope, or no callable\n * `fetch` — which is the SSR path. Nothing throws there: an import-time failure\n * in a server bundle is the only outcome that actually matters.\n */\nconst install = (): boolean => {\n const scope = globalScope();\n if (!scope || typeof scope.fetch !== 'function') {\n return false;\n }\n\n const native = scope.fetch.bind(scope) as FetchLike;\n const wrapper: FetchLike = (input, init) => dispatch([...entries].sort(byPriority), 0, input, init);\n\n capturedFetch = native;\n ourWrapper = wrapper;\n scope.fetch = wrapper as typeof scope.fetch;\n installed = true;\n return true;\n};\n\n/**\n * Removes our wrapper — but only if it is still the value we assigned.\n *\n * If a third party patched on top of us, their wrapper very likely captured ours\n * as its own next link. Restoring the captured fetch would clobber a stranger's\n * patch; so we leave ours in place and let it degrade to a pass-through, which\n * an empty middleware list already is. This is the pre-existing\n * unconditional-restore hazard in `passflow-provider.tsx` fixed by construction\n * (§9.4/R5).\n *\n * `installed` deliberately stays true in that case. Clearing it would let a\n * later registration install a SECOND wrapper while the first is still reachable\n * through the stranger's chain — the nesting collision this registry exists to\n * prevent.\n */\nconst uninstall = (): void => {\n const scope = globalScope();\n if (!scope || !ourWrapper) {\n return;\n }\n if (scope.fetch !== (ourWrapper as unknown as typeof scope.fetch)) {\n return;\n }\n\n if (capturedFetch) {\n scope.fetch = capturedFetch as typeof scope.fetch;\n }\n installed = false;\n ourWrapper = undefined;\n capturedFetch = undefined;\n};\n\n/**\n * Registers a middleware, installing the one global patch on the FIRST\n * registration, and returns an idempotent unregister that removes the patch on\n * the LAST removal.\n *\n * Safe in both directions and under Strict Mode's double-invoke: ownership is by\n * reference count over entry identity, so register/unregister/register of the\n * same middleware, and removals in any order relative to other registrations,\n * all converge. The returned function is bound to this one registration — a\n * second call does nothing, and registering the same middleware twice yields two\n * independent entries and two independent unregisters (§9.4/R8).\n */\nexport const registerFetchMiddleware = (middleware: FetchMiddleware, opts: RegisterOptions): (() => void) => {\n if (!installed && !install()) {\n // SSR, or a runtime with no fetch: nothing is patched and nothing is queued.\n // A no-op unregister keeps the caller's cleanup path identical either way.\n return () => {\n /* nothing was registered, so there is nothing to remove */\n };\n }\n\n const entry: RegistryEntry = {\n middleware,\n name: opts.name,\n priority: opts.priority,\n seq: nextSeq++,\n };\n entries.push(entry);\n\n let removed = false;\n return () => {\n if (removed) {\n return;\n }\n removed = true;\n entries = entries.filter((candidate) => candidate !== entry);\n if (entries.length === 0) {\n uninstall();\n }\n };\n};\n\n/**\n * Test and diagnostic read of the registry.\n *\n * `names` is in execution order, outermost first. `foreignPatch` is computed\n * live rather than remembered, so it reports a stranger's patch as soon as it\n * lands, not only once a teardown has declined to restore over it.\n */\nexport const fetchRegistryState = (): { installed: boolean; names: string[]; foreignPatch: boolean } => {\n const scope = globalScope();\n return {\n installed,\n names: [...entries].sort(byPriority).map((entry) => entry.name),\n foreignPatch: installed && !!scope && !!ourWrapper && scope.fetch !== (ourWrapper as unknown as typeof scope.fetch),\n };\n};\n","/**\n * The Facade: one object that assembles the controller, the transport and the\n * adapters, and the only construction surface a consumer touches.\n *\n * `UpgradeController`, the transport, `upgradeFetch` and the fetch registry stay\n * internal. Tests reach their types by importing this directory's modules\n * directly, not through the package entry, because a type on `lib/index.ts` is\n * a supported name whether or not anything public produces a value of it.\n * `UpgradeFacadeDeps` is declared here and exported for that in-tree use only.\n * `UpgradeSessionPin`, declared in `./session`, is the exception: it IS on the\n * entry, because `AuthService.readSessionPin()` is public and returns one.\n *\n * **Five members, and the rule is written down because `structural.md` names\n * Facade-as-God-Object as this family's characteristic failure: adding a sixth\n * requires deleting one.** `createClient` is deliberately NOT a member — it is\n * `axios.create(config)` followed by `attachAuth`, so it lives on the `Passflow`\n * instance where the constructor belongs, and it is not a fourth code path.\n *\n * ## The two gates\n *\n * Passflow's own API (domain 1) is derived from `PassflowConfig.url` and handled\n * with zero configuration. The customer's backend (domain 2) is touched only\n * when `backend` is declared AND an adapter is attached: declaring without\n * attaching observes nothing, attaching without declaring matches nothing\n * beyond domain 1. That double gate is why the elevated-credential replay\n * surface is unreachable in the default configuration — there is no default\n * interception surface at all.\n *\n * ## The session-change reset\n *\n * A challenge is server-bound to the session's `sid`, and an abandoned flight\n * must never elevate a NEW session. `UpgradeController.inFlight` is private with\n * no reset, so on `SignIn` / `SignOut` the controller is DISCARDED and a fresh\n * one constructed. A waiter whose elevation was running across that boundary is\n * settled with `UpgradeSessionChangedError` — never replayed — which §13.3's\n * table maps to the original response on fetch and a tagged rejection on axios.\n *\n * SSR-safe: no global is read at module scope, and nothing is installed until\n * `enable()` is called with `interceptFetch`.\n */\n\nimport { FETCH_PRIORITY_UPGRADE, registerFetchMiddleware } from '../interceptors/fetch-registry';\nimport type { PlatformAdapter } from '../platform';\n\nimport { readAcrValues } from './acr';\nimport { type AxiosReissue, type ResponseInterceptorHost, attachUpgradeAuth, createAxiosUpgradeHandler } from './axios-adapter';\nimport type { AssuranceBar, UpgradeChallenge, UpgradeDiagnosticSink } from './challenge';\nimport {\n UpgradeController,\n type UpgradeElevation,\n type UpgradeFlightSummary,\n type UpgradePromptHandler,\n type UpgradeTransport,\n type Waiter,\n} from './controller';\nimport type { InterceptionScope, UpgradeInterceptorOptions } from './core';\nimport { UpgradeSessionChangedError, UpgradeTransportError, UpgradeUnavailableError } from './errors';\nimport type { UpgradeFactor } from './factors';\nimport { upgradeFetch } from './fetch-adapter';\nimport type { FetchLike, ReplayPolicy } from './replay';\nimport type { UpgradeSessionPin } from './session';\nimport { type UpgradeTransportDeps, createUpgradeTransport } from './transport';\n\n/** Domain 1, as resolved once from `PassflowConfig.url`. `null` = unresolved. */\nexport type PassflowUpgradeDomain = { origin: string; pathPrefix?: string } | null;\n\n/**\n * What the facade needs from its host, as narrow function-shaped dependencies.\n *\n * Everything HTTP arrives pre-wired, which is what keeps this module free of\n * endpoints and of any opinion about delivery mode.\n */\nexport type UpgradeFacadeDeps = UpgradeTransportDeps & {\n /** The platform adapter, for the WebAuthn ceremony. */\n platform: PlatformAdapter;\n /** Domain 1. Resolved by the host from `url`; the facade never derives it. */\n passflowDomain: PassflowUpgradeDomain;\n /** Construction-time factor override, from `PassflowConfig.upgrade`. */\n factors?: readonly UpgradeFactor[];\n /** The bar the elevation reached, read from the freshly stored access token. */\n readObtainedBar?: () => AssuranceBar | undefined;\n /** §5.5's credential swap. Snapshotted before the elevation, read again after. */\n authorizationSwap?: () => { from: string; to: string } | undefined;\n /**\n * Installs (or clears, on `undefined`) the handler the SDK's OWN axios clients\n * consult inside their existing error interceptor — §4.4's internal seam,\n * which an ordinary customer-registered interceptor is structurally too late\n * to be.\n *\n * It receives a FACTORY rather than a handler because each of the SDK's\n * clients must replay through its own instance: a handler built around one\n * client's `request` would re-issue another client's request on the wrong\n * instance.\n */\n attachInternalSeam?: (factory: ((reissue: AxiosReissue) => (error: unknown) => Promise<unknown>) | undefined) => void;\n /** Subscribes to session changes (`SignIn` / `SignOut`). Returns unsubscribe. */\n onSessionChange?: (listener: () => void) => () => void;\n /**\n * The session and user identity the host can currently read, or `undefined`\n * when this delivery mode exposes none.\n *\n * Read ONCE per flight, when the flight starts, and handed to `commitRefresh`\n * so the token-applying path can refuse to write user A's tokens into user\n * B's session (§13.4 revision 6 / H2).\n */\n readSessionPin?: () => UpgradeSessionPin | undefined;\n};\n\n/**\n * Everything `enable()` accepts: the prompt, the interception scope and the\n * interception policy.\n *\n * `requireChallengeChannel`, `backend` and `shouldIntercept` are interception\n * policy and deliberately NOT on `PassflowConfig` — they are per-installation,\n * not client-wide.\n */\nexport type UpgradeEnableOptions = {\n /** REQUIRED. The js-sdk ships no UI. */\n prompt: UpgradePromptHandler;\n /**\n * Where the customer's own API is: an origin, an origin with a path prefix, or\n * a same-origin path prefix (`'/api'`). Nothing outside this and domain 1 is\n * ever observed, and there is no allow-all opt-in — a developer who wants\n * every origin lists every origin, which is a sentence they can read in a diff.\n */\n backend?: string | string[];\n /**\n * Patches `globalThis.fetch` through the registry, at the upgrade priority.\n * **Off by default**, and a different decision from enabling the UI: one prop\n * for both would bundle \"show me the 2FA prompt\" with \"patch the global\", and\n * only the second has the blast radius. It widens which call sites are\n * observed, never the scope.\n */\n interceptFetch?: boolean;\n /** Requires the standards channel. The body is then never read. */\n requireChallengeChannel?: 'header';\n /** NARROWING ONLY: `false` excludes an admitted URL; `true` cannot widen. */\n shouldIntercept?: (url: string) => boolean;\n replay?: ReplayPolicy;\n /** The fetch surface's only option. Default `'resolve-original'`. */\n onCancel?: 'resolve-original' | 'reject';\n onUpgradeDiagnostic?: UpgradeDiagnosticSink;\n factors?: readonly UpgradeFactor[];\n maxAttempts?: number;\n /** One terminal state per flight, after every waiter has reported. */\n onFlightSettled?: (summary: UpgradeFlightSummary) => void;\n /** Forwarded verbatim to the prepare round for passkey upgrades. */\n relyingPartyId?: string;\n};\n\n/** What the imperative seam resolves with (§13.4). */\nexport type UpgradeResult = { status: 'elevated'; acr?: string };\n\n/**\n * What the imperative seam accepts: a bar the CALLER states.\n *\n * Stating one is what makes `UpgradeInsufficientError` reachable on this\n * surface; an options-less call states no bar, has no protected operation to\n * replay, and resolves `{ status: 'elevated' }` (§13.3's `n/a` cell).\n */\nexport type UpgradeOptions = { acrValues?: string; maxAge?: number };\n\nexport type UpgradeFacade = {\n /**\n * Drives an elevation directly — §13.4's imperative seam.\n *\n * The facade is CALLABLE, which is why this signature is here rather than on\n * a method: the documented surface is `passflow.upgrade(options?)`, and the\n * same object also carries `enable`, `withAuth` and the rest. Resolves on\n * proof completion; rejects `UpgradeCancelledError`,\n * `UpgradeUnsatisfiableError`, `UpgradeUnavailableError`,\n * `UpgradeSessionChangedError`, `UpgradeTransportError` — and, when the\n * caller stated a bar the proof did not reach, `UpgradeInsufficientError`.\n */\n (options?: UpgradeOptions): Promise<UpgradeResult>;\n /**\n * Installs the prompt handler and the selected interceptors. Returns an\n * idempotent teardown. Calling `enable()` twice replaces the previous\n * installation and tears the first one down.\n */\n enable(options: UpgradeEnableOptions): () => void;\n /** True when a prompt handler is installed AND the delivery mode can commit. */\n readonly enabled: boolean;\n /**\n * Drives an elevation directly — headless clients, a \"verify now\" button,\n * tests. Single-flight with the interceptors: concurrent callers share one\n * prompt, because they share one controller.\n */\n elevate(challenge: UpgradeChallenge, waiter?: Waiter): Promise<UpgradeElevation>;\n /** Wraps a fetch and returns the wrapper. Touches no global. */\n withAuth(next?: FetchLike): FetchLike;\n /** Attaches to an axios instance the developer owns, MUTATING it. Returns `detach()`. */\n attachAuth(instance: ResponseInterceptorHost): () => void;\n};\n\n/** `backend` as declared → the list the scope predicate reads. */\nconst declaredBackends = (backend: string | string[] | undefined): string[] => {\n if (backend === undefined) {\n return [];\n }\n return (Array.isArray(backend) ? backend : [backend]).filter((pattern) => pattern !== '');\n};\n\ntype Installation = {\n controller: UpgradeController;\n options: UpgradeEnableOptions;\n scope: InterceptionScope;\n /** Bumped on every session change; a waiter from an older generation is stale. */\n generation: number;\n /**\n * The identity the CURRENT flight was pinned to, captured when that flight\n * started. Per flight rather than per installation, because an installation\n * outlives a flight.\n */\n pin?: UpgradeSessionPin;\n};\n\nexport const createUpgradeFacade = (deps: UpgradeFacadeDeps): UpgradeFacade => {\n let installation: Installation | undefined;\n\n /** Is this installation still the live one, or has the session moved on? */\n const isCurrent = (owner: Installation | undefined): boolean => owner !== undefined && installation === owner;\n\n /**\n * The transport an installation's controller is given: the host's rounds,\n * each one gated on its owner still being the live installation.\n *\n * The epoch cannot be checked only around `controller.elevate()`, because\n * prepare, commit and token storage all happen INSIDE that one await. A\n * flight whose session changed mid-answer would still commit against the\n * server and store the elevated tokens over the replacement session's, and\n * only then throw. So the check sits immediately before each round that has a\n * side effect, which is the only place it can be both before the commit and\n * after the user's answer.\n *\n * `markSessionInvalid` is gated the same way for the opposite reason: a\n * DISCARDED controller must not be able to reach into the session that\n * replaced it. Its callbacks become no-ops rather than acting on a session\n * they know nothing about.\n *\n * It stays stateless, so one per installation costs nothing; the\n * relying-party id still prefers the one `enable()` was given.\n */\n const transportFor = (owner: () => Installation | undefined): UpgradeTransport => {\n const assertCurrent = (): void => {\n if (!isCurrent(owner())) {\n // Classified, not bare: `classifyUpgradeError` passes an\n // `UpgradeTransportError` through untouched, and `session_changed` is\n // the kind the controller already maps to a terminal\n // `UpgradeSessionChangedError`. A bare throw would be re-bucketed as a\n // network failure by the transport's own catch.\n throw new UpgradeTransportError({ kind: 'session_changed' });\n }\n };\n\n return createUpgradeTransport({\n ...deps,\n relyingPartyId: () => installation?.options.relyingPartyId ?? deps.relyingPartyId?.(),\n startUpgrade: (payload) => {\n assertCurrent();\n return deps.startUpgrade(payload);\n },\n commitRefresh: (upgrade) => {\n assertCurrent();\n // The pin travels with the commit so the host can verify it at the\n // moment it APPLIES the rotated tokens. `assertCurrent` above is the\n // epoch half and runs first; the two are complementary — the epoch says\n // something changed, the pin says whether it is still the same\n // principal, and only the second can be checked inside the storing path.\n return deps.commitRefresh(upgrade, owner()?.pin);\n },\n ...(deps.markSessionInvalid\n ? {\n markSessionInvalid: (): void => {\n if (isCurrent(owner())) {\n deps.markSessionInvalid?.();\n }\n },\n }\n : {}),\n });\n };\n\n /**\n * Builds an installation and the controller that belongs to it.\n *\n * The box exists because the controller's transport must be able to name its\n * owner, and the owner is not constructed until the controller is in hand.\n */\n const buildInstallation = (options: UpgradeEnableOptions, generation: number): Installation => {\n const box: { current?: Installation } = {};\n const controller = new UpgradeController({\n transport: transportFor(() => box.current),\n prompt: options.prompt,\n platform: deps.platform,\n factors: options.factors ?? deps.factors,\n ...(options.maxAttempts === undefined ? {} : { maxAttempts: options.maxAttempts }),\n ...(deps.readObtainedBar ? { readObtainedBar: deps.readObtainedBar } : {}),\n ...(options.onFlightSettled ? { onFlightSettled: options.onFlightSettled } : {}),\n });\n\n const built: Installation = {\n controller,\n options,\n scope: { passflow: deps.passflowDomain, backend: declaredBackends(options.backend) },\n generation,\n };\n box.current = built;\n return built;\n };\n\n /**\n * The `elevate` the core is given.\n *\n * Two obligations live here and nowhere else: route every surface through the\n * ONE controller instance (that is what makes single-flight hold across the\n * fetch wrapper, an attached axios instance and the internal seam), and refuse\n * to hand back an elevation that belongs to a different session than the\n * challenge did.\n */\n const elevateFor = async (challenge: UpgradeChallenge, waiter: Waiter): Promise<UpgradeElevation> => {\n const current = installation;\n if (!current) {\n // Nothing installed means no prompt and no way to ask the user, which is\n // exactly the `unavailable` row of the settlement table.\n throw new UpgradeUnavailableError('Step-up verification is not enabled on this Passflow instance');\n }\n\n // Pin the flight to the principal it STARTS under. `active` is false only\n // between flights — it is set before the prompt opens and cleared in the\n // final synchronous step — so this is exactly \"the first waiter of a new\n // flight\", and every joiner reuses the pin the flight began with.\n if (!current.controller.active) {\n current.pin = deps.readSessionPin?.();\n }\n\n const elevation = await current.controller.elevate(challenge, waiter);\n\n if (!isCurrent(current)) {\n // The session changed under the flight. Replaying now would carry a NEW\n // session's elevated credential to a request challenged under the old\n // one. The prepare and commit rounds are guarded separately, inside this\n // installation's own transport, because they run inside the await above —\n // this check protects the replay, and only the replay.\n throw new UpgradeSessionChangedError();\n }\n\n return elevation;\n };\n\n /** The resolved interception options, read fresh per call so `enable()` is live. */\n const interceptorOptions = (): UpgradeInterceptorOptions => {\n const current = installation;\n const scope: InterceptionScope = current?.scope ?? { passflow: deps.passflowDomain, backend: [] };\n const options = current?.options;\n\n return {\n elevate: elevateFor,\n scope,\n ...(options?.requireChallengeChannel ? { requireChallengeChannel: options.requireChallengeChannel } : {}),\n ...(options?.shouldIntercept ? { shouldIntercept: options.shouldIntercept } : {}),\n ...(options?.replay === undefined ? {} : { replay: options.replay }),\n ...(options?.onCancel ? { onCancel: options.onCancel } : {}),\n ...(deps.authorizationSwap ? { authorizationSwap: deps.authorizationSwap } : {}),\n ...(options?.onUpgradeDiagnostic ? { onUpgradeDiagnostic: options.onUpgradeDiagnostic } : {}),\n ...(deps.readObtainedBar ? { readObtainedBar: deps.readObtainedBar } : {}),\n };\n };\n\n let teardown: (() => void) | undefined;\n\n const enable = (options: UpgradeEnableOptions): (() => void) => {\n // Calling enable() twice replaces the previous installation rather than\n // stacking a second prompt handler and a second global patch.\n teardown?.();\n\n installation = buildInstallation(options, 0);\n\n const disposers: Array<() => void> = [];\n\n if (deps.attachInternalSeam) {\n const attach = deps.attachInternalSeam;\n // `interceptorOptions` is handed over as a GETTER, not invoked here: the\n // handler resolves it per challenge, so a client attached before this\n // `enable()` — or before a later, narrower one — sees the live scope.\n attach((reissue) => createAxiosUpgradeHandler(interceptorOptions, reissue));\n disposers.push(() => attach(undefined));\n }\n\n if (options.interceptFetch) {\n // The registry, never a bare assignment: two features that each capture\n // and restore `globalThis.fetch` lose one interceptor non-deterministically.\n disposers.push(\n registerFetchMiddleware((next, input, init) => upgradeFetch(next, interceptorOptions)(input, init), {\n priority: FETCH_PRIORITY_UPGRADE,\n name: 'passflow-upgrade',\n }),\n );\n }\n\n if (deps.onSessionChange) {\n disposers.push(\n deps.onSessionChange(() => {\n const current = installation;\n if (!current) {\n return;\n }\n // Discard, do not reset: `inFlight` is private and has no reset, and\n // replacing the instance is both the cleanest and the only\n // non-invasive answer. The abandoned flight can no longer release a\n // waiter into the new session — `elevateFor` checks the generation.\n installation = buildInstallation(current.options, current.generation + 1);\n }),\n );\n }\n\n let torn = false;\n teardown = () => {\n if (torn) {\n return;\n }\n torn = true;\n for (const dispose of disposers) {\n dispose();\n }\n installation = undefined;\n teardown = undefined;\n };\n\n return teardown;\n };\n\n /**\n * The imperative seam: `passflow.upgrade(options?)`.\n *\n * There is no request to replay, so there is no challenge on the wire to\n * parse — the \"challenge\" is the bar the CALLER stated, synthesised here into\n * the one shape the controller ranks factors against. An options-less call\n * synthesises no bar at all, which is what makes it unable to produce\n * `insufficient`.\n */\n const upgradeImperatively = async (options?: UpgradeOptions): Promise<UpgradeResult> => {\n const level = options?.acrValues === undefined ? undefined : readAcrValues(options.acrValues).level;\n const challenge: UpgradeChallenge = {\n status: 401,\n ...(options?.acrValues === undefined ? {} : { acrValues: options.acrValues }),\n ...(level === undefined ? {} : { requiredLevel: level }),\n ...(options?.maxAge === undefined ? {} : { maxAge: options.maxAge }),\n };\n\n const stated = options?.acrValues !== undefined || options?.maxAge !== undefined;\n const requested: AssuranceBar = {\n ...(options?.acrValues === undefined ? {} : { acrValues: options.acrValues }),\n ...(level === undefined ? {} : { level }),\n ...(options?.maxAge === undefined ? {} : { maxAge: options.maxAge }),\n };\n\n const elevation = await elevateFor(challenge, { kind: 'imperative', ...(stated ? { requested } : {}) });\n const acr = elevation.obtained.acrValues;\n return { status: 'elevated', ...(acr === undefined ? {} : { acr }) };\n };\n\n // A callable object, not a plain record: §13.4's documented surface is\n // `passflow.upgrade(options?)` AND `passflow.upgrade.enable({...})`, and both\n // have to be the same thing a consumer holds. `enabled` is a getter, so the\n // members are defined rather than assigned.\n const facade = upgradeImperatively as UpgradeFacade;\n\n return Object.defineProperties(facade, {\n enable: { value: enable, enumerable: true },\n enabled: {\n enumerable: true,\n get(): boolean {\n return installation !== undefined && deps.canCommit().ok;\n },\n },\n elevate: {\n enumerable: true,\n value: (challenge: UpgradeChallenge, waiter?: Waiter) => elevateFor(challenge, waiter ?? { kind: 'imperative' }),\n },\n withAuth: {\n enumerable: true,\n value: (next?: FetchLike): FetchLike => {\n if (next) {\n // The developer's own function, wrapped as given: binding it would\n // change its `this` and break a method-shaped fetch.\n return upgradeFetch(next, interceptorOptions);\n }\n // Resolved inside the call, never at module or construction scope:\n // reading a global while a module is being imported is the SSR failure.\n const global = typeof globalThis === 'undefined' ? undefined : globalThis;\n if (!global || typeof global.fetch !== 'function') {\n throw new Error('Passflow: withAuth() needs a fetch to wrap — this runtime has none.');\n }\n return upgradeFetch(global.fetch.bind(global) as FetchLike, interceptorOptions);\n },\n },\n attachAuth: {\n enumerable: true,\n value: (instance: ResponseInterceptorHost): (() => void) => attachUpgradeAuth(instance, interceptorOptions),\n },\n });\n};\n","import type { AxiosInstance } from 'axios';\nimport {\n AppAPI,\n type AppSettings,\n AuthAPI,\n InvitationAPI,\n type InvitationsPaginatedList,\n type InviteLinkResponse,\n type PassflowAuthorizationResponse,\n type PassflowConfig,\n PassflowError,\n PassflowFederatedAuthPayload,\n type PassflowPasskeyAuthenticateStartPayload,\n type PassflowPasskeyRegisterStartPayload,\n type PassflowPasskeySettings,\n type PassflowPasswordPolicySettings,\n type PassflowPasswordlessResponse,\n type PassflowPasswordlessSignInCompletePayload,\n type PassflowPasswordlessSignInPayload,\n type PassflowSendPasswordResetEmailPayload,\n type PassflowSettingsAll,\n type PassflowSignInPayload,\n type PassflowSignUpPayload,\n type PassflowSuccessResponse,\n type PassflowTenantResponse,\n type PassflowValidationResponse,\n type RequestInviteLinkPayload,\n SettingAPI,\n TenantAPI,\n TwoFactorApiClient,\n type TwoFactorConfirmResponse,\n type TwoFactorDisableResponse,\n type TwoFactorRecoveryResponse,\n type TwoFactorRegenerateResponse,\n type TwoFactorSetupResponse,\n type TwoFactorStatusResponse,\n type TwoFactorVerifyResponse,\n UpgradeAPI,\n type UpgradeDomain,\n UserAPI,\n resolveUpgradeDomain,\n} from './api';\n\nimport type { AxiosClient, AxiosUpgradeHandler } from './api/axios-client';\nimport { DEFAULT_SCOPES, PASSFLOW_CLOUD_URL, SDK_VERSION } from './constants';\nimport { DeviceService } from './device';\nimport type { CurrentUrlInfo, PlatformAdapter } from './platform';\nimport { webAdapter } from './platform';\nimport { AuthService, InvitationService, TenantService, TokenCacheService, TwoFactorService, UserService } from './services';\nimport { StorageManager } from './storage';\nimport { type ErrorPayload, PassflowEvent, PassflowStore, type PassflowSubscriber } from './store';\nimport { TokenType, parseToken } from './token';\nimport {\n type AssuranceBar,\n type CreateClientConfig,\n type FetchLike,\n type ResponseInterceptorHost,\n type UpgradeFacade,\n createUpgradeClient,\n createUpgradeFacade,\n readAcrValues,\n} from './upgrade';\n\nimport type { ParsedTokens, SessionParams, Tokens } from './types';\nimport { isValidJWTFormat, sanitizeErrorMessage } from './utils/validation';\n\nexport class Passflow {\n /**\n * SDK version string.\n * Useful for debugging and reporting version-specific issues.\n * @example\n * ```typescript\n * console.log('Using Passflow SDK version:', Passflow.version);\n * ```\n */\n static readonly version: string = SDK_VERSION;\n\n // API clients\n private authApi: AuthAPI;\n private appApi: AppAPI;\n private userApi: UserAPI;\n private settingApi: SettingAPI;\n private tenantApi: TenantAPI;\n private invitationApi: InvitationAPI;\n private twoFactorApi: TwoFactorApiClient;\n private upgradeApi: UpgradeAPI;\n\n // Configuration\n private scopes: string[];\n private createTenantForNewUser: boolean;\n private doRefreshTokens = false;\n\n // Services\n private deviceService: DeviceService;\n private storageManager: StorageManager;\n private subscribeStore: PassflowStore;\n private authService: AuthService;\n private userService: UserService;\n private tenantService: TenantService;\n private invitationService: InvitationService;\n private tokenCacheService: TokenCacheService;\n private twoFactorService: TwoFactorService;\n\n // Public services\n public tenant: TenantService;\n public twoFactor: TwoFactorService;\n\n /**\n * Step-up (assurance upgrade) wiring. Always present, inert until\n * `enable({ prompt })` is called — constructing a `Passflow` must never patch\n * a global or install a prompt.\n */\n readonly upgrade: UpgradeFacade;\n\n /**\n * Passflow's own API as an interception scope (\"domain 1\"), resolved ONCE\n * here from `PassflowConfig.url` so no adapter or wrapper ever derives it.\n * `null` when no `url` was configured, which is empty — never a throw, and\n * never silent: the first parseable challenge then names `url` as the fix.\n */\n private readonly upgradeDomain: UpgradeDomain | null;\n\n // Session callbacks\n private createSessionCallback?: ({ tokens, parsedTokens }: { tokens?: Tokens; parsedTokens?: ParsedTokens }) => Promise<void>;\n private expiredSessionCallback?: () => Promise<void>;\n\n // State\n error?: Error;\n private platform: PlatformAdapter;\n get origin(): string {\n return this.platform.getCurrentUrl()?.origin ?? '';\n }\n url: string;\n appId?: string;\n\n constructor(config: PassflowConfig) {\n const { url, appId, scopes } = config;\n this.url = url || PASSFLOW_CLOUD_URL;\n this.appId = appId;\n this.platform = config.platform ?? webAdapter;\n\n // Initialize single StorageManager instance\n this.storageManager = new StorageManager({\n prefix: config.keyStoragePrefix ?? '',\n storage: this.platform.storage,\n });\n\n // Initialize single DeviceService instance with shared StorageManager\n this.deviceService = new DeviceService(this.storageManager);\n\n // Initialize API clients with shared instances\n this.authApi = new AuthAPI(config, this.storageManager, this.deviceService);\n this.appApi = new AppAPI(config, this.storageManager, this.deviceService);\n this.userApi = new UserAPI(config, this.storageManager, this.deviceService);\n this.settingApi = new SettingAPI(config, this.storageManager, this.deviceService);\n this.tenantApi = new TenantAPI(config, this.storageManager, this.deviceService);\n this.invitationApi = new InvitationAPI(config, this.storageManager, this.deviceService);\n this.twoFactorApi = new TwoFactorApiClient(config, this.storageManager, this.deviceService);\n this.upgradeApi = new UpgradeAPI(config, this.storageManager, this.deviceService);\n this.upgradeDomain = resolveUpgradeDomain(config.url);\n\n // Initialize PassflowStore\n this.subscribeStore = new PassflowStore();\n\n this.tokenCacheService = new TokenCacheService(this.storageManager, this.authApi, this.subscribeStore, this.platform);\n\n this.scopes = scopes ?? DEFAULT_SCOPES;\n this.createTenantForNewUser = config.createTenantForNewUser ?? false;\n\n // Initialize domain services with dependencies\n this.authService = new AuthService(\n this.authApi,\n this.deviceService,\n this.storageManager,\n this.subscribeStore,\n this.tokenCacheService,\n this.scopes,\n this.createTenantForNewUser,\n this.url,\n {\n createSession: this.createSessionCallback,\n expiredSession: this.expiredSessionCallback,\n },\n this.appId ?? '',\n config.tokenExchange,\n this.platform,\n );\n\n this.userService = new UserService(this.userApi, this.deviceService, this.platform);\n\n this.tenantService = new TenantService(this.tenantApi, this.scopes);\n this.tenant = this.tenantService;\n\n this.invitationService = new InvitationService(this.invitationApi);\n\n this.twoFactorService = new TwoFactorService(this.twoFactorApi, this.subscribeStore);\n this.twoFactor = this.twoFactorService;\n\n this.upgrade = createUpgradeFacade({\n // The prepare round. The commit round is `/auth/refresh`, which must go\n // through AuthService: it is the only path that STORES the rotated tokens,\n // so posting directly would elevate the session on the server and leave\n // the client holding stale ones.\n startUpgrade: (payload) => this.upgradeApi.startUpgrade(payload),\n commitRefresh: async (upgrade, pin) => {\n await this.authService.refreshToken(upgrade, pin);\n },\n // The identity the flight is pinned to, read once when it starts. The\n // check itself lives inside `refreshToken`, immediately before the\n // rotated tokens are applied — see `AuthService.assertSessionPin`.\n readSessionPin: () => this.authService.readSessionPin(),\n canCommit: () =>\n this.authService['tokenDeliveryManager'].isBFFMode()\n ? { ok: false, reason: 'Step-up verification cannot be committed through a BFF refresh endpoint' }\n : { ok: true },\n markSessionInvalid: () => this.authService['tokenDeliveryManager'].setSessionInvalid(),\n platform: this.platform,\n passflowDomain: this.upgradeDomain,\n ...(config.upgrade?.factors ? { factors: config.upgrade.factors } : {}),\n ...(config.upgrade?.relyingPartyId ? { relyingPartyId: () => config.upgrade?.relyingPartyId } : {}),\n readObtainedBar: () => this.readAssuranceBar(),\n // §5.5's credential swap. Called twice by the core — before the elevation\n // and after it — so returning the CURRENT token both times is what makes\n // `{from: stale, to: fresh}` come out right, and exact-match on `from` is\n // what proves the outgoing header was ours.\n authorizationSwap: () => {\n const token = this.storageManager.getToken(TokenType.access_token);\n return token ? { from: `Bearer ${token}`, to: `Bearer ${token}` } : undefined;\n },\n attachInternalSeam: (factory) => this.attachUpgradeSeam(factory),\n onSessionChange: (listener) => this.subscribeToSessionChange(listener),\n });\n\n // Check for tokens in query params if configured\n if (config.parseQueryParams) {\n this.checkAndSetTokens();\n }\n\n this.setTokensToCacheFromLocalStorage();\n }\n\n /**\n * Update the appId and propagate it to all API clients.\n * This ensures that all future API requests use the new appId in their headers.\n *\n * @param appId - The new application ID to set\n *\n * @example\n * ```typescript\n * // Update appId after discovery\n * passflow.setAppId('discovered-app-id-123');\n * ```\n */\n setAppId(appId: string): void {\n this.appId = appId;\n\n // Update all API clients\n this.authApi.setAppId(appId);\n this.appApi.setAppId(appId);\n this.userApi.setAppId(appId);\n this.settingApi.setAppId(appId);\n this.tenantApi.setAppId(appId);\n this.invitationApi.setAppId(appId);\n this.twoFactorApi.setAppId(appId);\n this.upgradeApi.setAppId(appId);\n\n // Update authService appId if needed\n if (this.authService) {\n // AuthService stores appId internally but doesn't have a setter\n // The API clients will handle the headers, so this is sufficient\n }\n }\n\n /**\n * Wraps a `fetch`, returning a new function with step-up handling attached.\n *\n * Installs nothing and touches no global — this is the SSR- and\n * React-Native-safe surface. Two verbs exist because the semantics genuinely\n * differ: an axios interceptor mutates, a fetch wrapper returns, and one name\n * would hide which happened.\n *\n * A challenge from Passflow's own API is handled with no further\n * configuration; a challenge from the developer's own backend needs that\n * backend declared via `upgrade.enable({ backend })`.\n *\n * @example\n * ```typescript\n * const authedFetch = passflow.withAuth(fetch);\n * const res = await authedFetch('https://api.example.com/transfer', { method: 'POST' });\n * if (!res.ok && isUpgradeCancelled(res)) {\n * // the user declined the step-up — not an authorization failure\n * }\n * ```\n */\n withAuth(next?: FetchLike): FetchLike {\n return this.upgrade.withAuth(next);\n }\n\n /**\n * Attaches step-up handling to an axios instance the caller already owns,\n * **mutating** it, and returns a `detach()` disposer.\n *\n * The parameter is typed structurally — duck-typed on\n * `interceptors.response.use` — so this package's axios version is not a\n * constraint on the caller's. It returns the disposer rather than the\n * instance on purpose: it mutates, so returning the instance would read as if\n * it had built something.\n *\n * @example\n * ```typescript\n * const api = axios.create({ baseURL: 'https://api.example.com' });\n * const detach = passflow.attachAuth(api);\n * ```\n */\n attachAuth(instance: ResponseInterceptorHost): () => void {\n return this.upgrade.attachAuth(instance);\n }\n\n /**\n * Builds a ready axios instance with step-up handling already wired, for a\n * caller with no HTTP client of their own.\n *\n * Pure passthrough of axios' own create options, so `baseURL`, `headers`,\n * `timeout` and `adapter` stay the caller's. A `baseURL` is NOT read as a\n * declared backend — that would derive an interception scope through a side\n * door; declare it with `upgrade.enable({ backend })`.\n *\n * @example\n * ```typescript\n * const api = passflow.createClient({ baseURL: 'https://api.example.com' });\n * ```\n */\n createClient(config?: CreateClientConfig): AxiosInstance {\n return createUpgradeClient(config, (instance) => this.upgrade.attachAuth(instance));\n }\n\n /**\n * The assurance bar the session currently holds, read from the `acr` claim of\n * the stored access token.\n *\n * The server scores `acr` and is the only authority; this is the client-side\n * read of what it last said, and it is what lets an `insufficient` outcome\n * report a real `obtained` bar instead of the bar the ceremony was ranked for.\n */\n private readAssuranceBar(): AssuranceBar | undefined {\n const token = this.storageManager.getToken(TokenType.access_token);\n if (!token) {\n return undefined;\n }\n try {\n const claims = parseToken(token) as { acr?: unknown; auth_time?: unknown };\n const acrValues = typeof claims.acr === 'string' ? claims.acr : undefined;\n if (!acrValues) {\n return undefined;\n }\n const level = readAcrValues(acrValues).level;\n return level === undefined ? { acrValues } : { acrValues, level };\n } catch {\n // An unparseable token tells us nothing about the bar, and guessing one\n // would be worse than reporting the ranked approximation.\n return undefined;\n }\n }\n\n /** Every axios client this instance owns, which is every surface a Passflow 401 can arrive on. */\n private axiosClients(): AxiosClient[] {\n return [\n this.authApi['axiosClient'],\n this.appApi['axiosClient'],\n this.userApi['axiosClient'],\n this.settingApi['axiosClient'],\n this.tenantApi['axiosClient'],\n this.invitationApi['axiosClient'],\n this.twoFactorApi['axiosClient'],\n this.upgradeApi['axiosClient'],\n ];\n }\n\n /**\n * Installs (or clears) §4.4's internal seam on every one of the SDK's own\n * clients.\n *\n * Each client gets a handler bound to its OWN `replay`, so a replayed request\n * goes back out through the instance that issued it. The seam is consulted\n * inside the existing error interceptor, which is where it has to live: axios\n * runs response interceptors FIFO and that handler converts the `AxiosError`\n * into a `PassflowError` with no headers and no config, so anything\n * registered afterwards is structurally too late.\n */\n private attachUpgradeSeam(\n factory: ((reissue: (config: unknown) => Promise<unknown>) => (error: unknown) => Promise<unknown>) | undefined,\n ): void {\n for (const client of this.axiosClients()) {\n if (!factory) {\n client.setUpgradeInterceptor(undefined);\n continue;\n }\n const handler = factory((config) => client.replay(config as Parameters<AxiosClient['replay']>[0]));\n client.setUpgradeInterceptor(handler as unknown as AxiosUpgradeHandler);\n }\n }\n\n /**\n * Notifies on a session change, which the facade answers by discarding its\n * controller.\n *\n * `SignIn` and `SignOut` both count: a challenge is server-bound to the\n * session's `sid`, so an elevation abandoned across either boundary must never\n * elevate the NEW session.\n */\n private subscribeToSessionChange(listener: () => void): () => void {\n const subscriber: PassflowSubscriber = {\n onAuthChange: () => listener(),\n };\n this.subscribeStore.subscribe(subscriber, [PassflowEvent.SignIn, PassflowEvent.SignOut]);\n return () => this.subscribeStore.unsubscribe(subscriber);\n }\n\n // Session management\n /**\n * Configure session callbacks and check current session status.\n *\n * **WARNING**: Calling this method multiple times will overwrite previously registered callbacks.\n * Only the most recent `createSession` and `expiredSession` callbacks will be active.\n *\n * @param params - Session configuration parameters\n * @param params.createSession - Callback invoked when a valid session exists or is created\n * @param params.expiredSession - Callback invoked when no valid session exists\n * @param params.doRefresh - Whether to automatically refresh expired tokens (default: false)\n * @returns Promise that resolves when session check is complete\n *\n * @example\n * ```typescript\n * await passflow.session({\n * createSession: async ({ tokens, parsedTokens }) => {\n * console.log('User is authenticated', parsedTokens?.access_token?.sub);\n * // Initialize user session in your app\n * },\n * expiredSession: async () => {\n * console.log('User session expired');\n * // Redirect to login or show login modal\n * },\n * doRefresh: true // Automatically refresh tokens if expired\n * });\n * ```\n */\n session: ({ createSession, expiredSession, doRefresh }: SessionParams) => Promise<void> = async ({\n createSession,\n expiredSession,\n doRefresh = false,\n }) => {\n this.createSessionCallback = createSession;\n this.expiredSessionCallback = expiredSession;\n this.doRefreshTokens = doRefresh;\n\n await this.submitSessionCheck();\n };\n\n private async submitSessionCheck() {\n let tokens;\n let parsedTokens;\n try {\n tokens = await this.authService.getTokens(this.doRefreshTokens);\n parsedTokens = this.tokenCacheService.getParsedTokens();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error || error instanceof PassflowError ? error.message : 'Session check failed',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n tokens = undefined;\n }\n\n if (tokens && this.createSessionCallback) {\n await this.createSessionCallback({ tokens, parsedTokens });\n }\n\n if (!tokens && this.expiredSessionCallback) {\n await this.expiredSessionCallback();\n }\n }\n\n // Event subscription\n /**\n * Subscribe to Passflow authentication events.\n *\n * @param subscriber - Subscriber function that receives event type and payload\n * @param events - Optional array of specific events to listen for. If omitted, subscribes to all events.\n *\n * @example\n * ```typescript\n * // Subscribe to all events\n * passflow.subscribe((event, payload) => {\n * console.log('Event:', event, payload);\n * });\n *\n * // Subscribe to specific events only\n * passflow.subscribe(\n * (event, payload) => {\n * if (event === PassflowEvent.SignIn) {\n * console.log('User signed in', payload.tokens);\n * }\n * },\n * [PassflowEvent.SignIn, PassflowEvent.SignOut]\n * );\n * ```\n */\n subscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]) {\n this.subscribeStore.subscribe(subscriber, events);\n\n // Initialize token cache service and token event listener\n this.tokenCacheService.initialize();\n }\n\n /**\n * Unsubscribe from Passflow authentication events.\n *\n * @param subscriber - The subscriber function to remove\n * @param events - Optional array of specific events to unsubscribe from. If omitted, unsubscribes from all events.\n *\n * @example\n * ```typescript\n * const subscriber = (event, payload) => console.log(event, payload);\n *\n * // Subscribe\n * passflow.subscribe(subscriber);\n *\n * // Later, unsubscribe\n * passflow.unsubscribe(subscriber);\n * ```\n */\n unsubscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]) {\n this.subscribeStore.unsubscribe(subscriber, events);\n }\n\n // Token handling\n /**\n * Handle OAuth redirect callback and extract tokens from URL query parameters.\n * This method should be called on the redirect page after authentication.\n *\n * @returns Tokens object if found in URL, undefined otherwise\n *\n * @example\n * ```typescript\n * // On your redirect page (e.g., /auth/callback)\n * const tokens = passflow.handleTokensRedirect();\n * if (tokens) {\n * console.log('Authentication successful', tokens.access_token);\n * // Tokens are automatically saved to storage\n * }\n * ```\n */\n handleTokensRedirect(): Tokens | undefined {\n return this.checkAndSetTokens();\n }\n\n private checkAndSetTokens(): Tokens | undefined {\n // Cache the URL once to avoid redundant calls (especially for async/expensive adapters)\n const currentUrl = this.platform.getCurrentUrl();\n\n // Check query params first, then fall back to hash\n // React SDK may put tokens in hash (more secure), while some OAuth flows use query params\n let urlParams = new URLSearchParams(currentUrl?.search ?? '');\n let fromHash = false;\n\n // If no access_token in query params, check hash\n if (!urlParams.get('access_token') && currentUrl?.hash) {\n // Hash format: #access_token=xxx&refresh_token=yyy\n const hashParams = new URLSearchParams(currentUrl.hash.substring(1));\n if (hashParams.get('access_token')) {\n urlParams = hashParams;\n fromHash = true;\n }\n }\n\n const access_token = urlParams.get('access_token');\n const refresh_token = urlParams.get('refresh_token');\n const id_token = urlParams.get('id_token');\n const scopes: string[] = urlParams.get('scopes')?.split(',') ?? this.scopes;\n let tokens: Tokens | undefined = undefined;\n\n if (access_token) {\n // Validate JWT format for access_token\n if (!isValidJWTFormat(access_token)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid access token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.cleanupUrlParams(fromHash, currentUrl);\n return undefined;\n }\n\n // Validate optional refresh_token if present\n if (refresh_token && !isValidJWTFormat(refresh_token)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid refresh token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.cleanupUrlParams(fromHash, currentUrl);\n return undefined;\n }\n\n // Validate optional id_token if present\n if (id_token && !isValidJWTFormat(id_token)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid ID token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.cleanupUrlParams(fromHash, currentUrl);\n return undefined;\n }\n\n tokens = {\n access_token,\n refresh_token: refresh_token ?? undefined,\n id_token: id_token ?? undefined,\n scopes,\n };\n\n // URL tokens mean we're in JSON mode, not cookie/BFF mode\n // Clear any stored delivery mode to ensure consistent behavior\n this.storageManager.clearDeliveryMode();\n this.storageManager.saveTokens(tokens);\n this.tokenCacheService.setTokensCache(tokens);\n\n this.subscribeStore.notify(PassflowEvent.SignIn, { tokens, parsedTokens: this.getParsedTokens() });\n this.submitSessionCheck();\n this.cleanupUrlParams(fromHash, currentUrl);\n this.error = undefined;\n return tokens;\n } else {\n this.error = this.checkErrorsFromURL();\n }\n return undefined;\n }\n\n private checkErrorsFromURL(): Error | undefined {\n const urlParams = new URLSearchParams(this.platform.getCurrentUrl()?.search ?? '');\n const error = urlParams.get('error');\n if (error) {\n // Sanitize error message to prevent XSS\n const sanitized = sanitizeErrorMessage(error);\n return new Error(sanitized);\n }\n return undefined;\n }\n\n private cleanupUrlParams(fromHash = false, currentUrl: CurrentUrlInfo | null = null): void {\n const url = currentUrl ?? this.platform.getCurrentUrl();\n if (!url) return; // no URL context (SSR or unsupported environment)\n\n if (fromHash) {\n // Clear hash completely - tokens should be removed from URL for security\n this.platform.replaceUrl(url.pathname + url.search);\n } else {\n const urlParams = new URLSearchParams(url.search);\n\n // Remove sensitive token parameters\n urlParams.delete('access_token');\n urlParams.delete('refresh_token');\n urlParams.delete('id_token');\n urlParams.delete('client_challenge');\n\n // Use replaceState to fully clear from browser history\n if (urlParams.size > 0) {\n this.platform.replaceUrl(`${url.pathname}?${urlParams.toString()}`);\n } else {\n this.platform.replaceUrl(url.pathname);\n }\n }\n }\n\n private setTokensToCacheFromLocalStorage(): void {\n let tokens = this.storageManager.getTokens();\n\n // If tokens don't have access_token, check for inconsistent state\n // This can happen if delivery_mode is set to BFF but tokens were actually saved in JSON mode\n if (!tokens?.access_token) {\n const deliveryMode = this.storageManager.getDeliveryMode();\n if (deliveryMode) {\n // Delivery mode is set (cookie/BFF mode)\n // Check if this is legitimate cookie mode or stale state\n\n if (tokens?.id_token && this.storageManager.hasCookieModeIdToken()) {\n // Legitimate cookie/BFF mode: we have id_token from cookie mode storage\n // Access token is in HttpOnly cookies, not localStorage - this is valid\n this.tokenCacheService.setTokensCache(tokens);\n return;\n }\n\n // Check if there are JSON mode tokens that should be used instead\n if (this.storageManager.hasJsonModeTokens()) {\n // Stale state: delivery_mode is set but JSON tokens exist\n // Clear delivery mode and use JSON tokens\n this.storageManager.clearDeliveryMode();\n tokens = this.storageManager.getTokens();\n } else {\n // No valid tokens in either mode - clean up completely\n this.storageManager.deleteTokens();\n return;\n }\n }\n }\n\n if (tokens) {\n this.tokenCacheService.setTokensCache(tokens);\n }\n }\n\n /**\n * Get cached tokens from memory without triggering a refresh.\n *\n * @returns Cached tokens or undefined if not cached\n *\n * @example\n * ```typescript\n * const tokens = passflow.getCachedTokens();\n * if (tokens) {\n * console.log('Access token:', tokens.access_token);\n * }\n * ```\n */\n getCachedTokens() {\n return this.tokenCacheService.getTokens();\n }\n\n /**\n * Get cached tokens from memory and automatically refresh if expired.\n *\n * @returns Promise resolving to tokens or undefined\n *\n * @example\n * ```typescript\n * const tokens = await passflow.getTokensWithRefresh();\n * // Tokens are guaranteed to be valid or undefined\n * ```\n */\n getTokensWithRefresh() {\n return this.tokenCacheService.getTokensWithRefresh();\n }\n\n /**\n * Get parsed JWT tokens with decoded claims.\n *\n * @returns Parsed token objects with decoded payloads\n *\n * @example\n * ```typescript\n * const parsed = passflow.getParsedTokens();\n * if (parsed?.access_token) {\n * console.log('User ID:', parsed.access_token.sub);\n * console.log('Expires at:', new Date(parsed.access_token.exp * 1000));\n * }\n * ```\n */\n getParsedTokens() {\n return this.tokenCacheService.getParsedTokens();\n }\n\n /**\n * Check if the cached tokens are expired.\n *\n * @returns True if tokens are expired, false otherwise\n *\n * @example\n * ```typescript\n * if (passflow.areTokensExpired()) {\n * await passflow.refreshToken();\n * }\n * ```\n */\n areTokensExpired() {\n return this.tokenCacheService.isExpired();\n }\n\n // Auth delegation methods\n /**\n * Check if the user is currently authenticated with valid tokens.\n *\n * @returns True if user has valid, non-expired tokens\n *\n * @example\n * ```typescript\n * if (passflow.isAuthenticated()) {\n * console.log('User is logged in');\n * } else {\n * console.log('User needs to sign in');\n * }\n * ```\n */\n isAuthenticated(): boolean {\n const tokens = this.storageManager.getTokens();\n if (!tokens || !tokens.access_token) return false;\n\n // Use cached parsed tokens instead of re-parsing\n const parsedTokens = this.tokenCacheService.getParsedTokens();\n if (!parsedTokens) return false;\n\n return this.authService.isAuthenticated(parsedTokens);\n }\n\n /**\n * Sign in a user with email/username and password.\n *\n * @param payload - Sign-in credentials and options\n * @param payload.email - User's email or username\n * @param payload.password - User's password\n * @param payload.scopes - Optional scopes to request (defaults to SDK scopes)\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If authentication fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.signIn({\n * email: 'user@example.com',\n * password: 'secure-password'\n * });\n * console.log('Signed in successfully', response.access_token);\n * } catch (error) {\n * console.error('Sign in failed', error.message);\n * }\n * ```\n */\n async signIn(payload: PassflowSignInPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.signIn(payload);\n\n return response;\n }\n\n /**\n * Register a new user account with email and password.\n *\n * @param payload - Registration details\n * @param payload.email - User's email address\n * @param payload.password - User's password\n * @param payload.username - Optional username\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If registration fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.signUp({\n * email: 'newuser@example.com',\n * password: 'secure-password',\n * username: 'newuser'\n * });\n * console.log('Account created', response.access_token);\n * } catch (error) {\n * console.error('Sign up failed', error.message);\n * }\n * ```\n */\n async signUp(payload: PassflowSignUpPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.signUp(payload);\n\n return response;\n }\n\n /**\n * Initiate passwordless authentication by sending a magic link or OTP.\n *\n * @param payload - Passwordless sign-in configuration\n * @param payload.email - User's email address\n * @param payload.method - Delivery method ('email' or 'sms')\n * @returns Promise with response indicating if the code was sent\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * // Send magic link via email\n * const response = await passflow.passwordlessSignIn({\n * email: 'user@example.com',\n * method: 'email'\n * });\n * console.log('Magic link sent:', response.success);\n * ```\n */\n passwordlessSignIn(payload: PassflowPasswordlessSignInPayload): Promise<PassflowPasswordlessResponse> {\n return this.authService.passwordlessSignIn(payload);\n }\n\n /**\n * Complete passwordless authentication by verifying the OTP or token.\n *\n * @param payload - Verification payload\n * @param payload.email - User's email address\n * @param payload.code - Verification code from email/SMS\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with validation response containing tokens\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.passwordlessSignInComplete({\n * email: 'user@example.com',\n * code: '123456'\n * });\n * console.log('Passwordless sign-in complete', response.access_token);\n * } catch (error) {\n * console.error('Invalid code', error.message);\n * }\n * ```\n */\n async passwordlessSignInComplete(payload: PassflowPasswordlessSignInCompletePayload): Promise<PassflowValidationResponse> {\n const response = await this.authService.passwordlessSignInComplete(payload);\n\n return response;\n }\n\n /**\n * Centralized error handler for all public methods.\n * Creates proper ErrorPayload, distinguishes PassflowError from generic Error,\n * notifies error event, and re-throws the error.\n *\n * @param error - The error to handle\n * @param context - Context description for the error\n * @throws The original error after handling\n */\n private handleError(error: unknown, context: string): never {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : `${context} failed`,\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n /**\n * Sign out the current user and clear all tokens.\n *\n * @returns Promise that resolves when sign-out is complete\n * @throws {PassflowError} If sign-out request fails\n *\n * @example\n * ```typescript\n * try {\n * await passflow.logOut();\n * console.log('User signed out successfully');\n * // Redirect to login page\n * } catch (error) {\n * console.error('Sign out failed', error.message);\n * }\n * ```\n */\n async logOut(): Promise<void> {\n try {\n // Only clear tokens after successful logout API call\n await this.authService.logOut();\n this.storageManager.deleteTokens();\n this.tokenCacheService.setTokensCache(undefined);\n this.twoFactorService.clearPartialAuthState();\n await this.submitSessionCheck();\n this.subscribeStore.notify(PassflowEvent.SignOut, {});\n } catch (error) {\n this.handleError(error, 'Log out');\n }\n }\n\n /**\n * Initiate federated authentication (OAuth) with a popup window.\n *\n * @param payload - Federated authentication configuration\n * @param payload.provider - OAuth provider (e.g., 'google', 'github', 'microsoft')\n * @param payload.scopes - Optional scopes to request\n *\n * @example\n * ```typescript\n * // Sign in with Google using a popup\n * passflow.federatedAuthWithPopup({\n * provider: 'google'\n * });\n *\n * // Listen for the result via subscribe\n * passflow.subscribe((event, payload) => {\n * if (event === PassflowEvent.SignIn) {\n * console.log('OAuth sign-in successful', payload.tokens);\n * }\n * });\n * ```\n */\n federatedAuthWithPopup(payload: PassflowFederatedAuthPayload): void {\n this.authService.federatedAuthWithPopup(payload);\n }\n\n /**\n * Initiate federated authentication (OAuth) with a full-page redirect.\n *\n * @param payload - Federated authentication configuration\n * @param payload.provider - OAuth provider (e.g., 'google', 'github', 'microsoft')\n * @param payload.scopes - Optional scopes to request\n * @param payload.redirectUrl - URL to redirect to after authentication\n *\n * @example\n * ```typescript\n * // Sign in with GitHub using redirect\n * passflow.federatedAuthWithRedirect({\n * provider: 'github',\n * redirectUrl: window.location.origin + '/auth/callback'\n * });\n * ```\n */\n federatedAuthWithRedirect(payload: PassflowFederatedAuthPayload): void {\n this.authService.federatedAuthWithRedirect(payload);\n }\n\n /**\n * Reset the SDK state by clearing all tokens and optionally throwing an error.\n *\n * @param error - Optional error message to throw after reset\n * @throws {Error} If error message is provided\n *\n * @example\n * ```typescript\n * // Clear tokens without error\n * passflow.reset();\n *\n * // Clear tokens and throw error\n * try {\n * passflow.reset('Session expired');\n * } catch (error) {\n * console.error('Reset error:', error.message);\n * }\n * ```\n */\n reset(error?: string) {\n this.storageManager.deleteTokens();\n this.tokenCacheService.setTokensCache(undefined);\n this.subscribeStore.notify(PassflowEvent.SignOut, {});\n if (error) {\n this.error = new Error(error);\n const errorPayload: ErrorPayload = {\n message: error,\n code: 'RESET_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw this.error;\n }\n }\n\n /**\n * Refresh the access token using the refresh token.\n *\n * @returns Promise with new authorization response containing refreshed tokens\n * @throws {Error} If no refresh token is found\n * @throws {PassflowError} If refresh request fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.refreshToken();\n * console.log('Token refreshed', response.access_token);\n * } catch (error) {\n * console.error('Failed to refresh token', error.message);\n * // Redirect to login\n * }\n * ```\n */\n async refreshToken(): Promise<PassflowAuthorizationResponse> {\n if (!this.tokenCacheService.parsedTokensCache?.refresh_token) {\n throw new Error('No refresh token found');\n }\n\n try {\n const response = await this.authService.refreshToken();\n\n return response;\n } catch (error) {\n if (error instanceof PassflowError) {\n throw error;\n } else {\n this.subscribeStore.notify(PassflowEvent.Error, {\n message: 'Failed to refresh token',\n originalError: error,\n });\n throw error;\n }\n }\n }\n\n /**\n * Send a password reset email to the user.\n *\n * @param payload - Password reset request\n * @param payload.email - User's email address\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * try {\n * await passflow.sendPasswordResetEmail({\n * email: 'user@example.com'\n * });\n * console.log('Password reset email sent');\n * } catch (error) {\n * console.error('Failed to send reset email', error.message);\n * }\n * ```\n */\n sendPasswordResetEmail(payload: PassflowSendPasswordResetEmailPayload): Promise<PassflowSuccessResponse> {\n return this.authService.sendPasswordResetEmail(payload);\n }\n\n /**\n * Reset password using a reset token (typically from URL after clicking email link).\n *\n * @param newPassword - The new password to set\n * @param scopes - Optional scopes to request after reset\n * @returns Promise with authorization response containing new tokens\n * @throws {PassflowError} If reset fails\n *\n * @example\n * ```typescript\n * // On password reset page (e.g., /reset-password?token=xyz)\n * try {\n * const response = await passflow.resetPassword('new-secure-password');\n * console.log('Password reset successful', response.access_token);\n * } catch (error) {\n * console.error('Password reset failed', error.message);\n * }\n * ```\n */\n async resetPassword(newPassword: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.resetPassword(newPassword, scopes);\n\n return response;\n }\n\n // App settings\n /**\n * Get application settings and configuration.\n *\n * @returns Promise with app settings including branding, features, and config\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const settings = await passflow.getAppSettings();\n * console.log('App name:', settings.name);\n * console.log('Passwordless enabled:', settings.passwordless_enabled);\n * ```\n */\n async getAppSettings(): Promise<AppSettings> {\n try {\n return await this.appApi.getAppSettings();\n } catch (error) {\n this.handleError(error, 'Get app settings');\n }\n }\n\n /**\n * Get all Passflow settings including password policy, passkey settings, etc.\n *\n * @returns Promise with all settings\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const settings = await passflow.getSettingsAll();\n * console.log('Password policy:', settings.password_policy);\n * console.log('Passkey settings:', settings.passkey);\n * ```\n */\n async getSettingsAll(): Promise<PassflowSettingsAll> {\n try {\n return await this.settingApi.getSettingsAll();\n } catch (error) {\n this.handleError(error, 'Get all settings');\n }\n }\n\n /**\n * Get password policy settings (min length, complexity requirements, etc.).\n *\n * @returns Promise with password policy configuration\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const policy = await passflow.getPasswordPolicySettings();\n * console.log('Min length:', policy.min_length);\n * console.log('Require uppercase:', policy.require_uppercase);\n * ```\n */\n async getPasswordPolicySettings(): Promise<PassflowPasswordPolicySettings> {\n try {\n return await this.settingApi.getPasswordPolicySettings();\n } catch (error) {\n this.handleError(error, 'Get password policy settings');\n }\n }\n\n /**\n * Get passkey (WebAuthn) configuration settings.\n *\n * @returns Promise with passkey settings\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const passkeySettings = await passflow.getPasskeySettings();\n * console.log('Passkeys enabled:', passkeySettings.enabled);\n * console.log('User verification:', passkeySettings.user_verification);\n * ```\n */\n async getPasskeySettings(): Promise<PassflowPasskeySettings> {\n try {\n return await this.settingApi.getPasskeySettings();\n } catch (error) {\n this.handleError(error, 'Get passkey settings');\n }\n }\n\n // Passkey methods\n /**\n * Register a new user with a passkey (WebAuthn).\n *\n * @param payload - Passkey registration configuration\n * @param payload.email - User's email address\n * @param payload.username - Optional username\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If passkey registration fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.passkeyRegister({\n * email: 'user@example.com',\n * username: 'myusername'\n * });\n * console.log('Passkey registered', response.access_token);\n * } catch (error) {\n * console.error('Passkey registration failed', error.message);\n * }\n * ```\n */\n async passkeyRegister(payload: PassflowPasskeyRegisterStartPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.passkeyRegister(payload);\n\n return response;\n }\n\n /**\n * Authenticate a user with a passkey (WebAuthn).\n *\n * @param payload - Passkey authentication configuration\n * @param payload.email - Optional user email to pre-fill\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If passkey authentication fails\n *\n * @example\n * ```typescript\n * try {\n * // Let user select from available passkeys\n * const response = await passflow.passkeyAuthenticate({});\n * console.log('Passkey sign-in successful', response.access_token);\n * } catch (error) {\n * console.error('Passkey authentication failed', error.message);\n * }\n * ```\n */\n async passkeyAuthenticate(payload: PassflowPasskeyAuthenticateStartPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.passkeyAuthenticate(payload);\n\n return response;\n }\n\n // Token management\n /**\n * Manually set tokens (useful after custom authentication flows).\n * This will save tokens to storage, update cache, and trigger SignIn event.\n *\n * @param tokensData - Tokens object to set\n * @param tokensData.access_token - JWT access token\n * @param tokensData.refresh_token - Optional refresh token\n * @param tokensData.id_token - Optional ID token\n * @param tokensData.scopes - Token scopes\n *\n * @example\n * ```typescript\n * // Set tokens from a custom auth flow\n * passflow.setTokens({\n * access_token: 'eyJhbGci...',\n * refresh_token: 'eyJhbGci...',\n * id_token: 'eyJhbGci...',\n * scopes: ['id', 'offline', 'email']\n * });\n * ```\n */\n setTokens(tokensData: Tokens): void {\n this.storageManager.saveTokens(tokensData);\n this.tokenCacheService.setTokensCache(tokensData);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: tokensData,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n }\n\n /**\n * Get current tokens from storage, optionally refreshing if expired.\n *\n * @param doRefresh - If true, automatically refresh expired tokens (default: false)\n * @returns Promise with tokens or undefined if not authenticated\n *\n * @example\n * ```typescript\n * // Get tokens without refresh\n * const tokens = await passflow.getTokens();\n *\n * // Get tokens and auto-refresh if expired\n * const freshTokens = await passflow.getTokens(true);\n * ```\n */\n async getTokens(doRefresh = false): Promise<Tokens | undefined> {\n return await this.authService.getTokens(doRefresh);\n }\n\n /**\n * Get a specific token from storage by type.\n *\n * @param tokenType - Type of token to retrieve ('access_token', 'refresh_token', 'id_token')\n * @returns Token string or undefined if not found\n *\n * @example\n * ```typescript\n * const accessToken = passflow.getToken('access_token');\n * if (accessToken) {\n * // Use token for API calls\n * fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` }\n * });\n * }\n * ```\n */\n getToken(tokenType: TokenType): string | undefined {\n return this.storageManager.getToken(tokenType);\n }\n\n // User passkey methods delegated to UserService\n /**\n * Get list of passkeys registered for the current user.\n *\n * @returns Promise with array of user's passkeys\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const passkeys = await passflow.getUserPasskeys();\n * passkeys.forEach(passkey => {\n * console.log('Passkey:', passkey.name, passkey.id);\n * });\n * ```\n */\n async getUserPasskeys() {\n try {\n return await this.userService.getUserPasskeys();\n } catch (error) {\n this.handleError(error, 'Get user passkeys');\n }\n }\n\n /**\n * Rename a user's passkey to a friendly name.\n *\n * @param name - New friendly name for the passkey\n * @param passkeyId - ID of the passkey to rename\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.renameUserPasskey('My MacBook Pro', 'passkey-123');\n * console.log('Passkey renamed successfully');\n * ```\n */\n async renameUserPasskey(name: string, passkeyId: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.userService.renameUserPasskey(name, passkeyId);\n } catch (error) {\n this.handleError(error, 'Rename user passkey');\n }\n }\n\n /**\n * Delete a passkey from the user's account.\n *\n * @param passkeyId - ID of the passkey to delete\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.deleteUserPasskey('passkey-123');\n * console.log('Passkey deleted successfully');\n * ```\n */\n async deleteUserPasskey(passkeyId: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.userService.deleteUserPasskey(passkeyId);\n } catch (error) {\n this.handleError(error, 'Delete user passkey');\n }\n }\n\n /**\n * Add a new passkey to the current user's account (requires active session).\n *\n * @param options - Optional passkey configuration\n * @param options.relyingPartyId - Optional RP ID for the passkey\n * @param options.passkeyUsername - Optional username to associate with passkey\n * @param options.passkeyDisplayName - Optional display name for the passkey\n * @returns Promise that resolves when passkey is added\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * // Add passkey with default settings\n * await passflow.addUserPasskey();\n *\n * // Add passkey with custom display name\n * await passflow.addUserPasskey({\n * passkeyDisplayName: 'My iPhone'\n * });\n * ```\n */\n async addUserPasskey(options?: {\n relyingPartyId?: string;\n passkeyUsername?: string;\n passkeyDisplayName?: string;\n }): Promise<void> {\n try {\n return await this.userService.addUserPasskey(options);\n } catch (error) {\n this.handleError(error, 'Add user passkey');\n }\n }\n\n // Tenant methods delegated to TenantService\n /**\n * Join a tenant invitation\n * @param token The invitation token\n * @param scopes Optional scopes to request\n * @returns Promise with invite response\n */\n async joinInvitation(token: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n try {\n const response = await this.tenant.joinInvitation(token, scopes);\n response.scopes = scopes ?? this.scopes;\n this.storageManager.saveTokens(response);\n this.tokenCacheService.setTokensCache(response);\n return response;\n } catch (error) {\n this.handleError(error, 'Join invitation');\n }\n }\n\n /**\n * Create a new tenant\n * @param name The name of the tenant\n * @param refreshToken Whether to refresh the token after creating the tenant\n * @returns Promise with tenant response\n */\n async createTenant(name: string, refreshToken?: boolean): Promise<PassflowTenantResponse> {\n try {\n const response = await this.tenant.createTenant(name);\n if (refreshToken) {\n await this.refreshToken();\n }\n return response;\n } catch (error) {\n this.handleError(error, 'Create tenant');\n }\n }\n\n // Invitation methods delegated to InvitationService\n /**\n * Request an invitation link for a user to join a tenant.\n *\n * @param payload - Invitation request configuration\n * @param payload.email - Email address to send invitation to\n * @param payload.tenantID - Tenant ID for the invitation\n * @param payload.send_to_email - Whether to send email automatically (default: true)\n * @returns Promise with invitation link response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const invitation = await passflow.requestInviteLink({\n * email: 'newuser@example.com',\n * tenantID: 'tenant-123',\n * send_to_email: true\n * });\n * console.log('Invitation link:', invitation.link);\n * ```\n */\n async requestInviteLink(payload: RequestInviteLinkPayload): Promise<InviteLinkResponse> {\n try {\n // default is true\n if (payload.send_to_email === undefined) {\n payload.send_to_email = true;\n }\n return await this.invitationService.requestInviteLink(payload);\n } catch (error) {\n this.handleError(error, 'Request invite link');\n }\n }\n\n /**\n * Gets a list of active invitations\n * @param options Optional parameters for filtering and pagination\n * @returns Promise with paginated list of invitations\n */\n async getInvitations(options: {\n tenantID: string;\n groupID?: string;\n skip?: number | string;\n limit?: number | string;\n }): Promise<InvitationsPaginatedList> {\n try {\n return await this.invitationService.getInvitations(options);\n } catch (error) {\n this.handleError(error, 'Get invitations');\n }\n }\n\n /**\n * Delete an invitation by its token.\n *\n * @param token - Invitation token to delete\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.deleteInvitation('invitation-token-123');\n * console.log('Invitation deleted');\n * ```\n */\n async deleteInvitation(token: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.invitationService.deleteInvitation(token);\n } catch (error) {\n this.handleError(error, 'Delete invitation');\n }\n }\n\n /**\n * Resend an invitation email.\n *\n * @param token - Invitation token to resend\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.resendInvitation('invitation-token-123');\n * console.log('Invitation email resent');\n * ```\n */\n async resendInvitation(token: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.invitationService.resendInvitation(token);\n } catch (error) {\n this.handleError(error, 'Resend invitation');\n }\n }\n\n /**\n * Get invitation details and link by invitation ID.\n *\n * @param invitationID - Invitation ID to retrieve\n * @returns Promise with invitation link response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const invitation = await passflow.getInvitationLink('invitation-123');\n * console.log('Invitation link:', invitation.link);\n * ```\n */\n async getInvitationLink(invitationID: string): Promise<InviteLinkResponse> {\n try {\n return await this.invitationService.getInvitationLink(invitationID);\n } catch (error) {\n this.handleError(error, 'Get invitation link');\n }\n }\n\n // Auth redirect helpers\n /**\n * Generate an authentication redirect URL for hosted login.\n *\n * @param options - Redirect URL configuration\n * @param options.url - Optional custom Passflow server URL\n * @param options.redirectUrl - URL to redirect to after authentication\n * @param options.scopes - Optional scopes to request\n * @param options.appId - Optional app ID to use\n * @returns Authentication redirect URL\n *\n * @example\n * ```typescript\n * const authUrl = passflow.authRedirectUrl({\n * redirectUrl: window.location.origin + '/auth/callback',\n * scopes: ['id', 'email', 'offline']\n * });\n * console.log('Auth URL:', authUrl);\n * // Use this URL for custom navigation logic\n * ```\n */\n authRedirectUrl(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): string {\n return this.authService.authRedirectUrl(options);\n }\n\n /**\n * Redirect to the Passflow hosted login page.\n *\n * @param options - Redirect configuration\n * @param options.url - Optional custom Passflow server URL\n * @param options.redirectUrl - URL to redirect to after authentication\n * @param options.scopes - Optional scopes to request\n * @param options.appId - Optional app ID to use\n *\n * @example\n * ```typescript\n * // Redirect to hosted login page\n * passflow.authRedirect({\n * redirectUrl: window.location.origin + '/auth/callback'\n * });\n * ```\n */\n authRedirect(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): void {\n this.authService.authRedirect(options);\n }\n\n /**\n * Get the current token delivery mode.\n * Returns the mode determined by the server (json_body, cookie, or mobile).\n *\n * @returns The current token delivery mode\n *\n * @example\n * ```typescript\n * import { TokenDeliveryMode } from '@passflow/core';\n *\n * const mode = passflow.getDeliveryMode();\n * if (mode === TokenDeliveryMode.Cookie) {\n * console.log('Using cookie-based authentication');\n * } else {\n * console.log('Using JSON body authentication');\n * }\n * ```\n */\n getDeliveryMode(): import('./token/delivery-manager').TokenDeliveryMode {\n return this.authService['tokenDeliveryManager'].getMode();\n }\n\n /**\n * Restore and validate session for cookie mode on page load.\n * Only applicable when using cookie-based token delivery.\n * Validates that HttpOnly cookies are still valid with the server.\n *\n * This method is automatically called on SDK initialization for cookie mode,\n * but can be called manually to re-validate the session.\n *\n * @returns Promise resolving to true if session is valid, false otherwise\n *\n * @example\n * ```typescript\n * // Check if session is valid (useful after page reload)\n * const isValid = await passflow.restoreSession();\n * if (isValid) {\n * console.log('Session restored successfully');\n * } else {\n * console.log('Session expired, please sign in again');\n * }\n * ```\n */\n async restoreSession(): Promise<boolean> {\n return await this.authService.restoreSession();\n }\n\n // Two-Factor Authentication methods\n\n /**\n * Get the current 2FA enrollment status for the authenticated user.\n *\n * @returns Promise with 2FA status including enabled state and policy\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const status = await passflow.getTwoFactorStatus();\n * console.log('2FA enabled:', status.enabled);\n * console.log('Recovery codes remaining:', status.recovery_codes_remaining);\n * ```\n */\n async getTwoFactorStatus(): Promise<TwoFactorStatusResponse> {\n try {\n return await this.twoFactorService.getStatus();\n } catch (error) {\n this.handleError(error, 'Get 2FA status');\n }\n }\n\n /**\n * Begin the 2FA setup process for the authenticated user.\n * Returns a secret and QR code to scan with an authenticator app.\n *\n * @returns Promise with setup response containing secret and QR code\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const setup = await passflow.beginTwoFactorSetup();\n * console.log('Scan this QR code:', setup.qr_code);\n * console.log('Or enter this secret:', setup.secret);\n * ```\n */\n async beginTwoFactorSetup(): Promise<TwoFactorSetupResponse> {\n try {\n return await this.twoFactorService.beginSetup();\n } catch (error) {\n this.handleError(error, 'Begin 2FA setup');\n }\n }\n\n /**\n * Confirm 2FA setup by verifying a TOTP code from the authenticator app.\n * Returns recovery codes that MUST be saved by the user.\n *\n * @param code - 6-digit TOTP code from authenticator app\n * @returns Promise with confirmation response including recovery codes\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * const result = await passflow.confirmTwoFactorSetup('123456');\n * console.log('SAVE THESE RECOVERY CODES:', result.recovery_codes);\n * // Display recovery codes to user for safekeeping\n * ```\n */\n async confirmTwoFactorSetup(code: string): Promise<TwoFactorConfirmResponse> {\n try {\n return await this.twoFactorService.confirmSetup(code);\n } catch (error) {\n this.handleError(error, 'Confirm 2FA setup');\n }\n }\n\n /**\n * Verify a TOTP code during login when 2FA is required.\n * Completes the authentication process and saves tokens.\n *\n * @param code - 6-digit TOTP code from authenticator app\n * @returns Promise with verification response containing tokens\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * // After signIn returns requires_2fa: true\n * if (passflow.isTwoFactorVerificationRequired()) {\n * const response = await passflow.verifyTwoFactor('123456');\n * console.log('Authentication complete', response.access_token);\n * }\n * ```\n */\n async verifyTwoFactor(code: string): Promise<TwoFactorVerifyResponse> {\n try {\n const response = await this.twoFactorService.verify(code);\n\n // Passflow layer handles token saving (NOT service layer)\n this.storageManager.saveTokens(response);\n this.tokenCacheService.setTokensCache(response);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n this.handleError(error, 'Verify 2FA');\n }\n }\n\n /**\n * Use a recovery code for authentication when TOTP is unavailable.\n * Completes the authentication process and saves tokens.\n *\n * @param code - Recovery code from the list provided during setup\n * @returns Promise with recovery response containing tokens and remaining codes count\n * @throws {PassflowError} If recovery code is invalid\n *\n * @example\n * ```typescript\n * // After signIn returns requires_2fa: true\n * const result = await passflow.useTwoFactorRecoveryCode('ABCD-1234');\n * console.log('Authenticated with recovery code');\n * console.log(`${result.remaining_recovery_codes} recovery codes remaining`);\n * ```\n */\n async useTwoFactorRecoveryCode(code: string): Promise<TwoFactorRecoveryResponse> {\n try {\n const response = await this.twoFactorService.useRecoveryCode(code);\n\n // Passflow layer handles token saving (NOT service layer)\n this.storageManager.saveTokens(response);\n this.tokenCacheService.setTokensCache(response);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n this.handleError(error, 'Use 2FA recovery code');\n }\n }\n\n /**\n * Disable 2FA for the authenticated user.\n * Requires verification with a current TOTP code.\n *\n * @param code - Current 6-digit TOTP code for verification\n * @returns Promise with success response\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * await passflow.disableTwoFactor('123456');\n * console.log('2FA disabled successfully');\n * ```\n */\n async disableTwoFactor(code: string): Promise<TwoFactorDisableResponse> {\n try {\n return await this.twoFactorService.disable(code);\n } catch (error) {\n this.handleError(error, 'Disable 2FA');\n }\n }\n\n /**\n * Regenerate recovery codes for the authenticated user.\n * Old recovery codes will be invalidated. Requires verification with a current TOTP code.\n *\n * @param code - Current 6-digit TOTP code for verification\n * @returns Promise with response containing new recovery codes\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * const result = await passflow.regenerateTwoFactorRecoveryCodes('123456');\n * console.log('New recovery codes:', result.recovery_codes);\n * // Display new recovery codes to user for safekeeping\n * ```\n */\n async regenerateTwoFactorRecoveryCodes(code: string): Promise<TwoFactorRegenerateResponse> {\n try {\n return await this.twoFactorService.regenerateRecoveryCodes(code);\n } catch (error) {\n this.handleError(error, 'Regenerate 2FA recovery codes');\n }\n }\n\n /**\n * Check if 2FA verification is currently required (local state check).\n * Returns true if user has signed in but needs to complete 2FA verification.\n *\n * @returns True if 2FA verification is pending, false otherwise\n *\n * @example\n * ```typescript\n * if (passflow.isTwoFactorVerificationRequired()) {\n * // Show 2FA code input UI\n * console.log('Please enter your 2FA code');\n * }\n * ```\n */\n isTwoFactorVerificationRequired(): boolean {\n return this.twoFactorService.isVerificationRequired();\n }\n\n /**\n * Get configured TOTP digit count for the current app\n * @returns Number of digits (6 or 8) configured for TOTP codes\n *\n * @example\n * ```typescript\n * const digits = passflow.getTotpDigits();\n * console.log(`TOTP codes should be ${digits} digits`);\n * ```\n */\n getTotpDigits(): 6 | 8 {\n return this.twoFactorService.getTotpDigits();\n }\n\n // Magic Link 2FA Setup Methods\n\n /**\n * Validate a magic link token for 2FA setup.\n * Used when a user clicks on an admin-generated magic link to set up 2FA.\n *\n * The magic link creates a scoped session that can ONLY be used for 2FA setup,\n * not for regular authentication.\n *\n * @param token - Magic link token from URL parameter\n * @returns Promise with validation response containing scoped session or error\n *\n * @example\n * ```typescript\n * // On the magic link landing page (e.g., /2fa-setup/:token)\n * const urlToken = extractTokenFromUrl();\n * const result = await passflow.validateTwoFactorSetupMagicLink(urlToken);\n *\n * if (result.success) {\n * console.log('Magic link validated');\n * console.log('User ID:', result.userId);\n * console.log('Session expires in:', result.expiresIn, 'seconds');\n * // Show 2FA setup form\n * } else {\n * console.error('Validation failed:', result.error?.message);\n * // Show error UI\n * }\n * ```\n */\n async validateTwoFactorSetupMagicLink(\n token: string,\n ): Promise<import('./api/model').TwoFactorSetupMagicLinkValidationResponse> {\n return await this.twoFactorService.validateTwoFactorSetupMagicLink(token);\n }\n\n /**\n * Get the current magic link session (if any).\n * Used by React SDK components to access session data.\n *\n * @returns Active magic link session or null if none/expired\n *\n * @example\n * ```typescript\n * const session = passflow.getMagicLinkSession();\n * if (session) {\n * console.log('Active session for user:', session.userId);\n * console.log('Expires at:', new Date(session.expiresAt));\n * }\n * ```\n */\n getMagicLinkSession(): import('./api/model').TwoFactorSetupMagicLinkSession | null {\n return this.twoFactorService.getMagicLinkSession();\n }\n\n /**\n * Check if a magic link session is currently active.\n *\n * @returns True if magic link session is active and not expired\n *\n * @example\n * ```typescript\n * if (passflow.hasMagicLinkSession()) {\n * // User can proceed with 2FA setup\n * } else {\n * // Redirect to error page or request new link\n * }\n * ```\n */\n hasMagicLinkSession(): boolean {\n return this.twoFactorService.hasMagicLinkSession();\n }\n\n /**\n * Clear the magic link session.\n * Called after successful 2FA setup or on error.\n *\n * @example\n * ```typescript\n * // After 2FA setup is complete\n * passflow.clearMagicLinkSession();\n * // Redirect to sign-in\n * ```\n */\n clearMagicLinkSession(): void {\n this.twoFactorService.clearMagicLinkSession();\n }\n}\n","/**\n * M2M Authentication Error Classes\n *\n * Custom error classes for M2M authentication failures with\n * OAuth 2.0 compliant error codes and detailed information.\n */\n\nimport type { M2MErrorCode, M2MRateLimitInfo } from './types';\n\n/**\n * M2M Authentication Error\n *\n * Thrown when M2M authentication fails. Contains OAuth 2.0 compliant\n * error codes and additional debugging information.\n *\n * @example\n * ```typescript\n * try {\n * const token = await m2m.getToken();\n * } catch (error) {\n * if (error instanceof M2MError) {\n * console.error(`Error: ${error.code} - ${error.message}`);\n * if (error.code === 'rate_limit_exceeded') {\n * console.log(`Retry after: ${error.rateLimitInfo?.reset}`);\n * }\n * }\n * }\n * ```\n */\nexport class M2MError extends Error {\n /** OAuth 2.0 error code */\n readonly code: M2MErrorCode;\n\n /** HTTP status code from the response */\n readonly status: number;\n\n /** URI with more information about the error (if provided) */\n readonly errorUri?: string;\n\n /** Rate limit information (if rate limited) */\n readonly rateLimitInfo?: M2MRateLimitInfo;\n\n /** Response headers from the server */\n readonly headers?: Record<string, string>;\n\n /** Original error (if this wraps another error) */\n readonly cause?: Error;\n\n /** Timestamp when the error occurred */\n readonly timestamp: string;\n\n constructor(options: {\n code: M2MErrorCode;\n message: string;\n status?: number;\n errorUri?: string;\n rateLimitInfo?: M2MRateLimitInfo;\n headers?: Record<string, string>;\n cause?: Error;\n }) {\n super(options.message);\n this.name = 'M2MError';\n this.code = options.code;\n this.status = options.status ?? 400;\n this.errorUri = options.errorUri;\n this.rateLimitInfo = options.rateLimitInfo;\n this.headers = options.headers;\n this.cause = options.cause;\n this.timestamp = new Date().toISOString();\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, M2MError);\n }\n }\n\n /**\n * Create an M2MError from an OAuth 2.0 error response\n */\n static fromOAuthError(\n errorResponse: {\n error: M2MErrorCode;\n error_description?: string;\n error_uri?: string;\n },\n status: number,\n headers?: Record<string, string>,\n ): M2MError {\n const rateLimitInfo = headers ? M2MError.parseRateLimitHeaders(headers) : undefined;\n\n return new M2MError({\n code: errorResponse.error,\n message: errorResponse.error_description ?? M2MError.getDefaultMessage(errorResponse.error),\n status,\n errorUri: errorResponse.error_uri,\n rateLimitInfo,\n headers,\n });\n }\n\n /**\n * Create an M2MError from a network or other error\n */\n static fromError(error: Error, code: M2MErrorCode = 'server_error'): M2MError {\n return new M2MError({\n code,\n message: error.message || 'An unexpected error occurred',\n status: 500,\n cause: error,\n });\n }\n\n /**\n * Parse rate limit headers from response\n */\n static parseRateLimitHeaders(headers: Record<string, string>): M2MRateLimitInfo | undefined {\n const limit = headers['x-ratelimit-limit'];\n const remaining = headers['x-ratelimit-remaining'];\n const reset = headers['x-ratelimit-reset'] || headers['retry-after'];\n\n if (limit && remaining && reset) {\n return {\n limit: parseInt(limit, 10),\n remaining: parseInt(remaining, 10),\n reset: parseInt(reset, 10),\n };\n }\n\n return undefined;\n }\n\n /**\n * Get default error message for an error code\n */\n static getDefaultMessage(code: M2MErrorCode): string {\n const messages: Record<M2MErrorCode, string> = {\n invalid_request: 'The request is missing a required parameter or is otherwise malformed.',\n invalid_client: 'Client authentication failed. Verify your client credentials.',\n invalid_grant: 'The provided authorization grant is invalid or expired.',\n invalid_scope: 'The requested scope is invalid, unknown, or exceeds the allowed scopes.',\n unauthorized_client: 'The client is not authorized to use this grant type.',\n unsupported_grant_type: 'The authorization grant type is not supported.',\n rate_limit_exceeded: 'Too many requests. Please retry after the rate limit window resets.',\n server_error: 'The authorization server encountered an unexpected error.',\n temporarily_unavailable: 'The authorization server is temporarily unavailable. Please try again later.',\n };\n\n return messages[code] || 'An unknown error occurred.';\n }\n\n /**\n * Check if the error is retryable\n */\n isRetryable(): boolean {\n return (\n this.code === 'server_error' ||\n this.code === 'temporarily_unavailable' ||\n this.code === 'rate_limit_exceeded' ||\n this.status >= 500\n );\n }\n\n /**\n * Get suggested wait time before retry (in milliseconds)\n */\n getRetryAfter(): number {\n if (this.rateLimitInfo?.reset) {\n const now = Math.floor(Date.now() / 1000);\n const waitSeconds = this.rateLimitInfo.reset - now;\n return Math.max(waitSeconds * 1000, 1000);\n }\n\n // Default to 1 second for server errors\n return 1000;\n }\n\n /**\n * Convert to JSON-serializable object\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n errorUri: this.errorUri,\n rateLimitInfo: this.rateLimitInfo,\n timestamp: this.timestamp,\n };\n }\n\n /**\n * Create a human-readable string representation\n */\n toString(): string {\n let str = `M2MError [${this.code}]: ${this.message}`;\n if (this.status) {\n str += ` (HTTP ${this.status})`;\n }\n return str;\n }\n}\n\n/**\n * Network error (connection failed, timeout, etc.)\n */\nexport class M2MNetworkError extends M2MError {\n constructor(message: string, cause?: Error) {\n super({\n code: 'temporarily_unavailable',\n message,\n status: 0,\n cause,\n });\n this.name = 'M2MNetworkError';\n }\n}\n\n/**\n * Token parsing error (invalid JWT format)\n */\nexport class M2MTokenParseError extends M2MError {\n constructor(message: string, cause?: Error) {\n super({\n code: 'invalid_request',\n message,\n status: 400,\n cause,\n });\n this.name = 'M2MTokenParseError';\n }\n}\n\n/**\n * Configuration error (missing or invalid config)\n */\nexport class M2MConfigError extends M2MError {\n constructor(message: string) {\n super({\n code: 'invalid_request',\n message,\n status: 400,\n });\n this.name = 'M2MConfigError';\n }\n}\n","/**\n * M2M (Machine-to-Machine) Authentication Types\n *\n * OAuth 2.0 Client Credentials Grant implementation for server-to-server\n * authentication without user involvement.\n */\n\n/**\n * M2M Client configuration options\n */\nexport type M2MClientConfig = {\n /** Passflow server URL */\n url: string;\n\n /** M2M application client ID */\n clientId: string;\n\n /** M2M application client secret */\n clientSecret: string;\n\n /** Scopes to request (default: []) */\n scopes?: string[];\n\n /** Target audiences for the token */\n audience?: string[];\n\n /** Automatically refresh tokens before expiry (default: false) */\n autoRefresh?: boolean;\n\n /** Seconds before expiry to trigger refresh (default: 30) */\n refreshThreshold?: number;\n\n /** Request timeout in milliseconds (default: 10000) */\n timeout?: number;\n\n /** Number of retry attempts on failure (default: 3) */\n retries?: number;\n\n /** Delay between retries in milliseconds (default: 1000) */\n retryDelay?: number;\n\n /** Custom retry strategy */\n retryStrategy?: RetryStrategy;\n\n /** Custom token cache implementation */\n cache?: M2MTokenCache;\n\n /** Callback for token requests (for logging/metrics) */\n onTokenRequest?: (request: M2MTokenRequestInfo) => void;\n\n /** Callback for token responses (for logging/metrics) */\n onTokenResponse?: (response: M2MTokenResponse) => void;\n\n /** Callback for errors (for logging/metrics) */\n onError?: (error: M2MErrorResponse) => void;\n};\n\n/**\n * Token request options (can override defaults per-request)\n */\nexport type M2MTokenRequestOptions = {\n /** Override default scopes for this request */\n scopes?: string[];\n\n /** Override default audience for this request */\n audience?: string[];\n\n /** Force a new token request, ignoring cache */\n forceRefresh?: boolean;\n};\n\n/**\n * Token request info (for logging callbacks)\n */\nexport type M2MTokenRequestInfo = {\n /** Client ID being used */\n clientId: string;\n\n /** Scopes being requested */\n scopes: string[];\n\n /** Audiences being requested */\n audience: string[];\n\n /** Request timestamp */\n timestamp: string;\n};\n\n/**\n * OAuth 2.0 token response from the authorization server\n */\nexport type M2MTokenResponse = {\n /** The access token (JWT) */\n access_token: string;\n\n /** Token type (always \"Bearer\") */\n token_type: 'Bearer';\n\n /** Token lifetime in seconds */\n expires_in: number;\n\n /** Granted scopes (space-separated string) */\n scope?: string;\n\n /** Timestamp when token was issued (added by client) */\n issued_at?: number;\n};\n\n/**\n * OAuth 2.0 error response from the authorization server\n */\nexport type M2MErrorResponse = {\n /** OAuth 2.0 error code */\n error: M2MErrorCode;\n\n /** Human-readable error description */\n error_description?: string;\n\n /** URI with more information about the error */\n error_uri?: string;\n};\n\n/**\n * Parsed M2M JWT token claims\n */\nexport type M2MTokenClaims = {\n /** Issuer (Passflow server URL) */\n iss: string;\n\n /** Subject (client_id) */\n sub: string;\n\n /** Audience (target APIs) */\n aud: string | string[];\n\n /** Issued at timestamp (Unix epoch seconds) */\n iat: number;\n\n /** Expiration timestamp (Unix epoch seconds) */\n exp: number;\n\n /** JWT ID (unique token identifier) */\n jti?: string;\n\n /** Token type (\"m2m\") */\n type: 'm2m';\n\n /** Client ID */\n client_id: string;\n\n /** Tenant ID (for tenant-scoped M2M apps) */\n tenant_id?: string;\n\n /** Granted scopes */\n scopes: string[];\n};\n\n/**\n * M2M error codes (OAuth 2.0 compliant)\n */\nexport type M2MErrorCode =\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'rate_limit_exceeded'\n | 'server_error'\n | 'temporarily_unavailable';\n\n/**\n * M2M error code enum for convenience\n */\nexport const M2MErrorCodes = {\n InvalidRequest: 'invalid_request' as const,\n InvalidClient: 'invalid_client' as const,\n InvalidGrant: 'invalid_grant' as const,\n InvalidScope: 'invalid_scope' as const,\n UnauthorizedClient: 'unauthorized_client' as const,\n UnsupportedGrantType: 'unsupported_grant_type' as const,\n RateLimitExceeded: 'rate_limit_exceeded' as const,\n ServerError: 'server_error' as const,\n TemporarilyUnavailable: 'temporarily_unavailable' as const,\n};\n\n/**\n * Custom token cache interface for external cache implementations\n */\nexport interface M2MTokenCache {\n /**\n * Get cached token by key\n * @param key Cache key (typically clientId)\n * @returns Cached token or null if not found/expired\n */\n get(key: string): Promise<M2MTokenResponse | null>;\n\n /**\n * Cache a token with TTL\n * @param key Cache key (typically clientId)\n * @param token Token to cache\n * @param ttl Time-to-live in seconds\n */\n set(key: string, token: M2MTokenResponse, ttl: number): Promise<void>;\n\n /**\n * Delete cached token\n * @param key Cache key (typically clientId)\n */\n delete(key: string): Promise<void>;\n}\n\n/**\n * Custom retry strategy interface\n */\nexport interface RetryStrategy {\n /**\n * Determine if the request should be retried\n * @param error The error that occurred\n * @param attempt Current attempt number (1-based)\n * @returns true if request should be retried\n */\n shouldRetry(error: { code: M2MErrorCode; status?: number }, attempt: number): boolean;\n\n /**\n * Get delay before next retry\n * @param attempt Current attempt number (1-based)\n * @returns Delay in milliseconds\n */\n getDelay(attempt: number): number;\n}\n\n/**\n * Internal token request payload sent to the authorization server\n */\nexport type M2MTokenRequestPayload = {\n grant_type: 'client_credentials';\n client_id: string;\n client_secret: string;\n scope?: string;\n audience?: string;\n};\n\n/**\n * Rate limit information from response headers\n */\nexport type M2MRateLimitInfo = {\n /** Maximum requests allowed in the window */\n limit: number;\n\n /** Remaining requests in the current window */\n remaining: number;\n\n /** Unix timestamp when the window resets */\n reset: number;\n};\n\n/**\n * Default configuration values\n */\nexport const M2M_DEFAULTS = {\n /** Default token endpoint path */\n TOKEN_ENDPOINT: '/oauth2/token',\n\n /** Default request timeout in milliseconds */\n TIMEOUT: 10000,\n\n /** Default number of retry attempts */\n RETRIES: 3,\n\n /** Default delay between retries in milliseconds */\n RETRY_DELAY: 1000,\n\n /** Default refresh threshold in seconds */\n REFRESH_THRESHOLD: 30,\n\n /** Content-Type for token requests */\n CONTENT_TYPE: 'application/x-www-form-urlencoded',\n} as const;\n","/**\n * M2M (Machine-to-Machine) Authentication Client\n *\n * OAuth 2.0 Client Credentials Grant implementation for server-to-server\n * authentication without user involvement.\n *\n * @example\n * ```typescript\n * const m2m = new M2MClient({\n * url: 'https://auth.yourapp.com',\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * scopes: ['users:read', 'orders:write'],\n * });\n *\n * const token = await m2m.getToken();\n * ```\n */\n\nimport { M2MConfigError, M2MError, M2MNetworkError, M2MTokenParseError } from './errors';\nimport type {\n M2MClientConfig,\n M2MErrorCode,\n M2MTokenCache,\n M2MTokenClaims,\n M2MTokenRequestOptions,\n M2MTokenRequestPayload,\n M2MTokenResponse,\n RetryStrategy,\n} from './types';\nimport { M2M_DEFAULTS } from './types';\n\n/**\n * Default in-memory token cache\n */\nclass InMemoryCache implements M2MTokenCache {\n private cache: Map<string, { token: M2MTokenResponse; expiresAt: number }> = new Map();\n\n get(key: string): Promise<M2MTokenResponse | null> {\n const entry = this.cache.get(key);\n if (!entry) return Promise.resolve(null);\n\n // Check if expired\n if (Date.now() >= entry.expiresAt) {\n this.cache.delete(key);\n return Promise.resolve(null);\n }\n\n return Promise.resolve(entry.token);\n }\n\n set(key: string, token: M2MTokenResponse, ttl: number): Promise<void> {\n this.cache.set(key, {\n token,\n expiresAt: Date.now() + ttl * 1000,\n });\n return Promise.resolve();\n }\n\n delete(key: string): Promise<void> {\n this.cache.delete(key);\n return Promise.resolve();\n }\n}\n\n/**\n * Default retry strategy with exponential backoff\n */\nconst defaultRetryStrategy: RetryStrategy = {\n shouldRetry(error: { code: M2MErrorCode; status?: number }, attempt: number): boolean {\n // Don't retry after max attempts\n if (attempt >= 3) return false;\n\n // Retry on server errors and rate limits\n return (\n error.code === 'server_error' ||\n error.code === 'temporarily_unavailable' ||\n error.code === 'rate_limit_exceeded' ||\n (error.status !== undefined && error.status >= 500)\n );\n },\n getDelay(attempt: number): number {\n // Exponential backoff: 1s, 2s, 4s\n return Math.pow(2, attempt - 1) * 1000;\n },\n};\n\n/**\n * M2M Authentication Client\n *\n * Implements OAuth 2.0 Client Credentials Grant for machine-to-machine\n * authentication. Provides automatic token caching, refresh, and retry logic.\n */\nexport class M2MClient {\n private readonly config: Required<\n Pick<M2MClientConfig, 'url' | 'clientId' | 'clientSecret' | 'timeout' | 'retries' | 'retryDelay' | 'refreshThreshold'>\n > &\n Pick<\n M2MClientConfig,\n 'scopes' | 'audience' | 'autoRefresh' | 'retryStrategy' | 'cache' | 'onTokenRequest' | 'onTokenResponse' | 'onError'\n >;\n\n private readonly cache: M2MTokenCache;\n private readonly retryStrategy: RetryStrategy;\n private readonly tokenEndpoint: string;\n\n /**\n * Create a new M2M client\n *\n * @param config - Client configuration\n * @throws {M2MConfigError} If required configuration is missing\n *\n * @example\n * ```typescript\n * const m2m = new M2MClient({\n * url: 'https://auth.yourapp.com',\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * });\n * ```\n */\n constructor(config: M2MClientConfig) {\n // Validate required config\n if (!config.url) {\n throw new M2MConfigError('M2M client requires a URL');\n }\n if (!config.clientId) {\n throw new M2MConfigError('M2M client requires a clientId');\n }\n if (!config.clientSecret) {\n throw new M2MConfigError('M2M client requires a clientSecret');\n }\n\n // Normalize URL (remove trailing slash)\n const url = config.url.replace(/\\/$/, '');\n\n this.config = {\n url,\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n scopes: config.scopes,\n audience: config.audience,\n autoRefresh: config.autoRefresh ?? false,\n refreshThreshold: config.refreshThreshold ?? M2M_DEFAULTS.REFRESH_THRESHOLD,\n timeout: config.timeout ?? M2M_DEFAULTS.TIMEOUT,\n retries: config.retries ?? M2M_DEFAULTS.RETRIES,\n retryDelay: config.retryDelay ?? M2M_DEFAULTS.RETRY_DELAY,\n retryStrategy: config.retryStrategy,\n cache: config.cache,\n onTokenRequest: config.onTokenRequest,\n onTokenResponse: config.onTokenResponse,\n onError: config.onError,\n };\n\n this.cache = config.cache ?? new InMemoryCache();\n this.retryStrategy = config.retryStrategy ?? defaultRetryStrategy;\n this.tokenEndpoint = `${url}${M2M_DEFAULTS.TOKEN_ENDPOINT}`;\n }\n\n /**\n * Get the cache key for this client\n */\n private getCacheKey(scopes?: string[], audience?: string[]): string {\n const scopeKey = scopes?.sort().join(',') || '';\n const audienceKey = audience?.sort().join(',') || '';\n return `m2m:${this.config.clientId}:${scopeKey}:${audienceKey}`;\n }\n\n /**\n * Request an access token from the authorization server\n *\n * @param options - Optional request overrides\n * @returns Token response\n * @throws {M2MError} On authentication failure\n *\n * @example\n * ```typescript\n * // Basic usage\n * const token = await m2m.getToken();\n *\n * // With options\n * const token = await m2m.getToken({\n * scopes: ['users:read'],\n * forceRefresh: true,\n * });\n * ```\n */\n async getToken(options?: M2MTokenRequestOptions): Promise<M2MTokenResponse> {\n const scopes = options?.scopes ?? this.config.scopes;\n const audience = options?.audience ?? this.config.audience;\n const cacheKey = this.getCacheKey(scopes, audience);\n\n // Check cache first (unless forced refresh)\n if (!options?.forceRefresh) {\n const cached = await this.cache.get(cacheKey);\n if (cached && !this.isTokenExpired(cached)) {\n return cached;\n }\n }\n\n // Request new token\n return this.requestToken(scopes, audience, cacheKey);\n }\n\n /**\n * Get a valid token, automatically refreshing if needed\n *\n * When autoRefresh is enabled, this will proactively refresh tokens\n * that are about to expire (within refreshThreshold seconds).\n *\n * @returns Valid token response\n * @throws {M2MError} On authentication failure\n *\n * @example\n * ```typescript\n * // Always returns a valid, non-expired token\n * const token = await m2m.getValidToken();\n * ```\n */\n async getValidToken(): Promise<M2MTokenResponse> {\n const scopes = this.config.scopes;\n const audience = this.config.audience;\n const cacheKey = this.getCacheKey(scopes, audience);\n\n // Check cache\n const cached = await this.cache.get(cacheKey);\n\n if (cached) {\n // If auto-refresh is enabled and token is about to expire, refresh it\n if (this.config.autoRefresh && this.isTokenExpired(cached, this.config.refreshThreshold)) {\n return this.requestToken(scopes, audience, cacheKey);\n }\n\n // If token is not expired, return it\n if (!this.isTokenExpired(cached)) {\n return cached;\n }\n }\n\n // Request new token\n return this.requestToken(scopes, audience, cacheKey);\n }\n\n /**\n * Request a new token from the authorization server\n */\n private async requestToken(scopes?: string[], audience?: string[], cacheKey?: string): Promise<M2MTokenResponse> {\n // Build request payload\n const payload: M2MTokenRequestPayload = {\n grant_type: 'client_credentials',\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n };\n\n if (scopes && scopes.length > 0) {\n payload.scope = scopes.join(' ');\n }\n\n if (audience && audience.length > 0) {\n payload.audience = audience.join(' ');\n }\n\n // Notify callback\n if (this.config.onTokenRequest) {\n this.config.onTokenRequest({\n clientId: this.config.clientId,\n scopes: scopes ?? [],\n audience: audience ?? [],\n timestamp: new Date().toISOString(),\n });\n }\n\n // Execute request with retries\n const token = await this.executeWithRetry(() => this.doTokenRequest(payload));\n\n // Add issued_at timestamp\n token.issued_at = Math.floor(Date.now() / 1000);\n\n // Cache the token\n if (cacheKey) {\n await this.cache.set(cacheKey, token, token.expires_in);\n }\n\n // Notify callback\n if (this.config.onTokenResponse) {\n this.config.onTokenResponse(token);\n }\n\n return token;\n }\n\n /**\n * Execute the actual HTTP request to the token endpoint\n */\n private async doTokenRequest(payload: M2MTokenRequestPayload): Promise<M2MTokenResponse> {\n // Build form-encoded body\n const body = new URLSearchParams();\n body.append('grant_type', payload.grant_type);\n body.append('client_id', payload.client_id);\n body.append('client_secret', payload.client_secret);\n if (payload.scope) {\n body.append('scope', payload.scope);\n }\n if (payload.audience) {\n body.append('audience', payload.audience);\n }\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(this.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': M2M_DEFAULTS.CONTENT_TYPE,\n Accept: 'application/json',\n },\n body: body.toString(),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n // Parse response body\n const data = await response.json();\n\n // Handle error response\n if (!response.ok) {\n const error = M2MError.fromOAuthError(\n {\n error: data.error || 'server_error',\n error_description: data.error_description || data.message,\n error_uri: data.error_uri,\n },\n response.status,\n headers,\n );\n\n if (this.config.onError) {\n this.config.onError({\n error: error.code,\n error_description: error.message,\n });\n }\n\n throw error;\n }\n\n return data as M2MTokenResponse;\n } catch (error) {\n clearTimeout(timeoutId);\n\n // Handle abort (timeout)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new M2MNetworkError(`Request timed out after ${this.config.timeout}ms`);\n }\n\n // Handle network errors\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw new M2MNetworkError(`Network error: ${error.message}`, error);\n }\n\n // Re-throw M2M errors\n if (error instanceof M2MError) {\n throw error;\n }\n\n // Wrap unknown errors\n throw M2MError.fromError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Execute a request with retry logic\n */\n private async executeWithRetry<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: M2MError | undefined;\n\n for (let attempt = 1; attempt <= this.config.retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n if (!(error instanceof M2MError)) {\n throw error;\n }\n\n lastError = error;\n\n // Check if we should retry\n if (\n attempt < this.config.retries &&\n this.retryStrategy.shouldRetry({ code: error.code, status: error.status }, attempt)\n ) {\n const delay = this.retryStrategy.getDelay(attempt);\n await this.sleep(delay);\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError ?? new M2MError({ code: 'server_error', message: 'Request failed after retries' });\n }\n\n /**\n * Sleep for a given duration\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Get the currently cached token without making a request\n *\n * @returns Cached token or null if not cached\n *\n * @example\n * ```typescript\n * const token = m2m.getCachedToken();\n * if (token && !m2m.isTokenExpired(token)) {\n * console.log('Using cached token');\n * }\n * ```\n */\n getCachedToken(): M2MTokenResponse | null {\n // Note: This is synchronous for the default in-memory cache\n // For async caches, use getToken() instead\n const cache = this.cache as InMemoryCache;\n if ('cache' in cache) {\n const cacheKey = this.getCacheKey(this.config.scopes, this.config.audience);\n const entry = (cache as unknown as { cache: Map<string, { token: M2MTokenResponse; expiresAt: number }> }).cache.get(\n cacheKey,\n );\n return entry?.token ?? null;\n }\n return null;\n }\n\n /**\n * Check if a token is expired or about to expire\n *\n * @param token - Token to check (uses issued_at + expires_in if available)\n * @param threshold - Seconds before actual expiry to consider expired (default: 0)\n * @returns true if expired or about to expire\n *\n * @example\n * ```typescript\n * if (m2m.isTokenExpired(token)) {\n * console.log('Token is expired');\n * }\n *\n * // Check if expiring within 5 minutes\n * if (m2m.isTokenExpired(token, 300)) {\n * console.log('Token expires soon');\n * }\n * ```\n */\n isTokenExpired(token?: M2MTokenResponse | null, threshold = 0): boolean {\n if (!token) return true;\n\n const now = Math.floor(Date.now() / 1000);\n const issuedAt = token.issued_at ?? now - token.expires_in;\n const expiresAt = issuedAt + token.expires_in;\n\n return now >= expiresAt - threshold;\n }\n\n /**\n * Parse token claims from a JWT access token\n *\n * @param token - JWT access token string\n * @returns Decoded token claims\n * @throws {M2MTokenParseError} If token format is invalid\n *\n * @example\n * ```typescript\n * const token = await m2m.getToken();\n * const claims = m2m.parseToken(token.access_token);\n * console.log('Client ID:', claims.client_id);\n * console.log('Scopes:', claims.scopes);\n * ```\n */\n parseToken(token: string): M2MTokenClaims {\n try {\n // JWT format: header.payload.signature\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new M2MTokenParseError('Invalid JWT format: expected 3 parts');\n }\n\n // Decode the payload (second part)\n const payload = parts[1];\n if (!payload) {\n throw new M2MTokenParseError('Invalid JWT format: missing payload');\n }\n const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n const claims = JSON.parse(decoded);\n\n // Ensure scopes is an array\n if (claims.scopes && typeof claims.scopes === 'string') {\n claims.scopes = claims.scopes.split(' ');\n } else if (!claims.scopes) {\n claims.scopes = [];\n }\n\n return claims as M2MTokenClaims;\n } catch (error) {\n if (error instanceof M2MTokenParseError) {\n throw error;\n }\n throw new M2MTokenParseError(`Failed to parse token: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Clear the token cache, forcing a new request on next getToken()\n *\n * @example\n * ```typescript\n * m2m.clearCache();\n * // Next getToken() will request a new token\n * const token = await m2m.getToken();\n * ```\n */\n clearCache(): void {\n const cacheKey = this.getCacheKey(this.config.scopes, this.config.audience);\n this.cache.delete(cacheKey);\n }\n\n /**\n * Revoke the current token\n *\n * Note: Requires the server to support token revocation (RFC 7009).\n * Not all Passflow deployments may support this endpoint.\n *\n * @throws {M2MError} If revocation fails\n *\n * @example\n * ```typescript\n * await m2m.revokeToken();\n * console.log('Token revoked');\n * ```\n */\n async revokeToken(): Promise<void> {\n const cached = this.getCachedToken();\n if (!cached) {\n return; // No token to revoke\n }\n\n const revokeEndpoint = `${this.config.url}/oauth2/revoke`;\n\n const body = new URLSearchParams();\n body.append('token', cached.access_token);\n body.append('client_id', this.config.clientId);\n body.append('client_secret', this.config.clientSecret);\n\n try {\n const response = await fetch(revokeEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': M2M_DEFAULTS.CONTENT_TYPE,\n },\n body: body.toString(),\n });\n\n // RFC 7009: Server should return 200 even if token was already revoked\n if (!response.ok && response.status !== 200) {\n const data = await response.json().catch(() => ({}));\n throw M2MError.fromOAuthError(\n {\n error: data.error || 'server_error',\n error_description: data.error_description || 'Token revocation failed',\n },\n response.status,\n );\n }\n\n // Clear cache\n this.clearCache();\n } catch (error) {\n if (error instanceof M2MError) {\n throw error;\n }\n throw M2MError.fromError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Get the configured URL\n */\n get url(): string {\n return this.config.url;\n }\n\n /**\n * Get the configured client ID\n */\n get clientId(): string {\n return this.config.clientId;\n }\n\n /**\n * Get the configured scopes\n */\n get scopes(): string[] | undefined {\n return this.config.scopes;\n }\n\n /**\n * Get the configured audience\n */\n get audience(): string[] | undefined {\n return this.config.audience;\n }\n}\n"],"names":["APP_ID_HEADER_KEY","AUTHORIZATION_HEADER_KEY","DEVICE_ID_HEADER_KEY","DEVICE_TYPE_HEADER_KEY","SDK_VERSION","packageJson","MINIMAL_DEFAULT_SCOPES","DEFAULT_SCOPES","PASSFLOW_CLOUD_URL","DEFAULT_GROUP_NAME","POPUP_WIDTH","POPUP_HEIGHT","POPUP_POLL_INTERVAL_MS","POPUP_TIMEOUT_MS","TOKEN_EXPIRY_BUFFER_SECONDS","USERNAME_MIN_LENGTH","USERNAME_MAX_LENGTH","ERROR_MESSAGE_MAX_LENGTH","parseMembership","raw","tenants","k","v","tnt","gk","roles","g","decodeBase64","base64","TokenService","storageManager","ttype","tokenString","token","parseToken","isTokenExpired","tokenType","bufferSeconds","base64Url","padded","decoded","jsonPayload","c","parsedToken","TokenType","TokenDeliveryMode","SessionState","TokenDeliveryManager","mode","persistedMode","persistedState","StorageManager","storage","prefix","tokens","deliveryMode","id_token","access_token","refresh_token","scopes","key","idToken","access","deviceId","url","DeviceService","newDeviceId","uuidv4","PopupClosedError","message","WebAdapter","options","startRegistration","startAuthentication","callback","handler","width","height","expectedReturnOrigin","popupWindow","resolve","reject","startTime","checkInterval","callbackSearch","callbackHash","str","enabled","webAdapter","PARTICIPATING_TOKEN","ABSENT","parseAcrToken","readAcrValues","levels","level","bar","requiredAcrLevel","INSUFFICIENT_USER_AUTHENTICATION","UPGRADE_REQUIRED","AUTH_PARAM","unescapeQuoted","value","parseAuthParams","header","params","match","name","quoted","bare","parseMaxAge","n","maxAgeValue","reading","parseChallengeHeader","status","parseChallengeBody","body","data","readChannel","channel","challenge","rawMaxAge","rawHeaderParams","readUpgradeChallenge","input","headerOnly","rawHeader","headerChallenge","bodyChallenge","diagnostics","report","reason","detail","channels","rawOf","contributing","requiredLevel","acrValues","maxAge","first","second","acrDisagrees","freshnessDisagrees","parseUpgradeChallenge","getHeader","UPGRADE_TRANSACTION_MARKER","UPGRADE_REPLAYED_MARKER","readMarker","config","markUpgradeTransaction","hasUpgradeTransactionMarker","markUpgradeReplayed","hasUpgradeReplayedMarker","RequestMethod","PassflowEndpointPaths","PassflowAdminEndpointPaths","resolveUpgradeDomain","parsed","PassflowError","error","Providers","OS","getOSFromDeviceType","deviceType","pathWithParams","template","result","TwoFactorPolicy","MAX_RETRIES","INITIAL_RETRY_DELAY_MS","axiosHeaderReader","response","readUpgradeChallengeFrom","isUpgradeChallengeResponse","AxiosClient","deviceService","appId","keyStoragePrefix","axios","axiosConfig","csrfToken","controller","e","upgradeError","endpoint","payload","method","retryCount","delayMs","retryAfter","retryAfterNum","retryDate","errorData","path","AppAPI","AuthAPI","refreshToken","accessToken","upgrade","os","defaultPayload","create_tenant","anonymous","isAdmin","newPassword","resetToken","passkeyData","challengeId","otp","headers","CLIAuthAPI","sessionId","request","InvitationAPI","invitationID","SettingAPI","TenantAPI","tenantId","groupId","userId","role","roleId","limit","skip","inviteId","email","TwoFactorApiClient","tfa_token","code","recovery_code","backendResponse","methodId","UpgradeAPI","UserAPI","passkeyId","relyingPartyId","passkeyDisplayName","passkeyUsername","PassflowEvent","PassflowStore","subscriber","events","eventSet","subscribedEvents","event","eventType","UpgradeErrorIds","RECOVERABLE_COMMIT_ERROR_IDS","SESSION_INVALIDATING_COMMIT_ERROR_IDS","RETRYABLE_KINDS","isRetryableUpgradeKind","kind","DEFAULT_MESSAGES","UpgradeTransportError","init","UPGRADE_ERROR_CODES","isUpgradeSdkError","isUpgradeTransportError","readErrorFacts","candidate","envelopeId","kindFromErrorId","errorId","kindFromStatus","classifyUpgradeError","facts","isRecoverableCommitFailure","invalidatesSessionOnCommit","UpgradeInsufficientError","obtained","required","UpgradeUnavailableError","UpgradeSessionChangedError","readSessionPin","claims","sessionPinMatches","pin","current","isValidJWTFormat","parts","base64UrlPattern","part","sanitizeErrorMessage","isValidEmail","trimmed","isValidPhoneNumber","phone","isValidUsername","username","isValidTOTPCode","digits","normalizeRecoveryCode","normalized","AuthService","authApi","subscribeStore","tokenCacheService","createTenantForNewUser","sessionCallbacks","tokenExchangeConfig","platform","errorPayload","oldScopes","refresh","is4xxError","sscopes","challenge_id","publicKey","webauthn","responseRegisterComplete","responseAuthenticateComplete","passflowPathWithProvider","queryParams","passflowURL","urlParams","tokensData","redirectUrl","externalUrl","parsedTokens","hasIdToken","sessionValid","sessionUnknown","doRefresh","InvitationService","invitationApi","ConsoleLogger","args","getDefaultLogger","TenantUserMembership","users","groups","memberships","r","uig","u","m","membership","TenantService","tenantApi","logger","context","responseData","passflowError","TokenCacheService","TwoFactorService","twoFactorApi","eventSubscriber","tfPayload","errorWithId","normalizedCode","codesCopy","tfaToken","storedState","UserService","userAPI","UpgradeFactorError","factor","cancelled","requireCode","totpFactor","recoveryCodesFactor","passkeyFactor","prepared","passkeys","credential","DEFAULT_UPGRADE_FACTORS","factorRegistry","factors","f","AAL3","CEREMONY_ORDER","rank","demanded","ceremony","needsAAL3","reachesAAL3Unaided","selectFactors","available","a","b","UpgradeCancelledError","cause","UpgradeUnsatisfiableError","DEFAULT_MAX_ATTEMPTS","DEFAULT_SETTLE_TIMEOUT_MS","deferred","res","rej","barOf","barMeets","mergeChallenges","incoming","isStrictlyStronger","acrRose","freshnessTightened","UpgradeController","waiter","registration","settled","statedBar","stronger","usable","settle","proof","summary","deadline","waiters","strongest","previousError","attempt","answer","terminal","assertion","RELATIVE_BASE","resolutionBase","scope","baseUri","href","normaliseUrl","base","sentinelBase","baseOrigin","resolved","headerEntry","captureHeaders","captured","iterable","entry","pair","flat","createRequestSnapshot","normalised","snapshot","swapAuthorization","swap","changed","next","headersToObject","bag","ONE_SHOT_METHODS","toPrepared","wire","isAlreadySettled","createUpgradeTransport","deps","permitted","resolveInterceptorOptions","source","parsePattern","pattern","pathMatches","inPassflowDomain","domain","inDeclaredBackend","patterns","patternUrl","isInterceptionScope","shouldIntercept","warn","diagnostic","reporterFor","ctx","sink","forwardParseDiagnostics","JSON_CONTENT_TYPE","readChallenge","contentType","mayReadBody","readOutOfScopeChallenge","outOfScopeMessage","classifyFailure","classifyReplay","replayed","raceAbort","work","signal","onAbort","aborted","handleChallenge","before","elevating","race","elevation","after","outcome","coreOutcomeToError","settleFetchOutcome","original","tagUpgradeOutcome","settleAxiosOutcome","originalError","exhausted","insufficient","unsatisfiable","unavailable","sessionChanged","transportFailure","isUpgradeCancelled","isUpgradeExhausted","isUpgradeInsufficient","isUpgradeUnsatisfiable","isUpgradeUnavailable","isUpgradeSessionChanged","isUpgradeTransportFailure","IDEMPOTENT_METHODS","isRequest","urlOf","methodOf","classifyInitBody","knownBodyBytes","sized","policyPermits","policy","info","bodyBytes","refuse","bodyKind","forwardInput","forwardInit","replayHeaders","hints","initWithoutHeaders","_headers","rest","effectiveHeaders","snapshotBody","copy","requestWithSnapshotHeaders","planReplay","snapshotFor","spare","isRecord","headerReader","bodyKindOf","replayConfig","absolute","contextFor","reissue","inherited","effective","failure","createAxiosUpgradeHandler","typed","settlement","attachUpgradeAuth","host","own","send","id","ejected","dispatchingLayers","abortViewOf","type","listener","plan","markers","abort","upgradeFetch","dispatch","nestedInput","nestedInit","createUpgradeClient","attachAuth","instance","FETCH_PRIORITY_OIDC","FETCH_PRIORITY_UPGRADE","FetchMiddlewareContractError","middlewareName","entries","nextSeq","installed","capturedFetch","ourWrapper","byPriority","globalScope","chain","index","target","delegated","nextInput","nextInit","install","native","wrapper","uninstall","registerFetchMiddleware","middleware","opts","removed","fetchRegistryState","declaredBackends","backend","createUpgradeFacade","installation","isCurrent","owner","transportFor","assertCurrent","buildInstallation","generation","box","built","elevateFor","interceptorOptions","teardown","stated","requested","acr","disposers","attach","torn","dispose","global","_Passflow","createSession","expiredSession","factory","client","currentUrl","fromHash","hashParams","sanitized","Passflow","M2MError","errorResponse","rateLimitInfo","remaining","reset","now","waitSeconds","M2MNetworkError","M2MTokenParseError","M2MConfigError","M2MErrorCodes","M2M_DEFAULTS","InMemoryCache","ttl","defaultRetryStrategy","M2MClient","audience","scopeKey","audienceKey","cacheKey","cached","timeoutId","fn","lastError","delay","ms","cache","threshold","expiresAt","revokeEndpoint"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../lib/constants/index.ts","../lib/token/membership.ts","../lib/token/service.ts","../lib/token/token.ts","../lib/token/delivery-manager.ts","../lib/storage/index.ts","../lib/device/index.ts","../lib/platform/types.ts","../lib/platform/web-adapter.ts","../lib/upgrade/acr.ts","../lib/upgrade/challenge.ts","../lib/upgrade/markers.ts","../lib/api/model.ts","../lib/api/axios-client.ts","../lib/api/app.ts","../lib/api/auth.ts","../lib/api/cli-auth.ts","../lib/api/invitation.ts","../lib/api/setting.ts","../lib/api/tenant.ts","../lib/api/two-factor.ts","../lib/api/upgrade.ts","../lib/api/user.ts","../lib/pkce.ts","../lib/store.ts","../lib/upgrade/errors.ts","../lib/upgrade/session.ts","../lib/utils/validation.ts","../lib/services/logger.ts","../lib/services/auth-service.ts","../lib/services/invitation-service.ts","../lib/services/tenant-user-membership.ts","../lib/services/tenant-service.ts","../lib/services/token-cache-service.ts","../lib/services/two-factor-service.ts","../lib/services/user-service.ts","../lib/upgrade/factors.ts","../lib/upgrade/select.ts","../lib/upgrade/controller.ts","../lib/upgrade/snapshot.ts","../lib/upgrade/transport.ts","../lib/upgrade/core.ts","../lib/upgrade/replay.ts","../lib/upgrade/axios-adapter.ts","../lib/upgrade/fetch-adapter.ts","../lib/upgrade/client.ts","../lib/interceptors/fetch-registry.ts","../lib/upgrade/facade.ts","../lib/passflow.ts","../lib/m2m/errors.ts","../lib/m2m/types.ts","../lib/m2m/client.ts"],"sourcesContent":["// @ts-ignore - package.json is outside the TypeScript project\nimport packageJson from '../../package.json';\n\nexport const APP_ID_HEADER_KEY = 'X-Passflow-Clientid';\nexport const AUTHORIZATION_HEADER_KEY = 'Authorization';\nexport const DEVICE_ID_HEADER_KEY = 'X-Passflow-DeviceId';\nexport const DEVICE_TYPE_HEADER_KEY = 'X-Passflow-DeviceType';\n\n/**\n * SDK version from package.json.\n * Useful for debugging and logging version information.\n */\nexport const SDK_VERSION = packageJson.version as string;\n\n/**\n * Minimal set of scopes for basic authentication.\n * Includes only essential scopes: user ID, token refresh, and OpenID Connect.\n * Use this for applications that need minimal permissions.\n */\nexport const MINIMAL_DEFAULT_SCOPES = ['id', 'offline', 'openid'];\n\n/**\n * Default scopes used by the SDK.\n * Includes comprehensive permissions: user ID, token refresh, tenant access, email, OIDC, and full tenant access.\n * Note: 'access:tenant:all' is a very permissive scope and may not be appropriate for all applications.\n * Consider using MINIMAL_DEFAULT_SCOPES or custom scopes for production applications.\n */\nexport const DEFAULT_SCOPES = ['id', 'offline', 'tenant', 'email', 'oidc', 'openid', 'access:tenant:all'];\n\nexport const PASSFLOW_CLOUD_URL = 'https://auth.passflow.cloud';\nexport const DEFAULT_GROUP_NAME = 'default';\n\n// Popup configuration\nexport const POPUP_WIDTH = 500;\nexport const POPUP_HEIGHT = 600;\nexport const POPUP_POLL_INTERVAL_MS = 100;\nexport const POPUP_TIMEOUT_MS = 60000; // 60 seconds\n\n// Token configuration\nexport const TOKEN_EXPIRY_BUFFER_SECONDS = 30; // Buffer time to consider token expired early\n\n// Validation constraints\nexport const USERNAME_MIN_LENGTH = 3;\nexport const USERNAME_MAX_LENGTH = 30;\nexport const ERROR_MESSAGE_MAX_LENGTH = 200;\n","export type Tenant = {\n id: string;\n name: string;\n};\n\nexport type TenantMembership = {\n tenant: Tenant;\n tenantRoles?: GroupMembership;\n groups?: GroupMembership[];\n};\n\nexport type Group = {\n id: string;\n name: string;\n};\n\nexport type GroupMembership = {\n group: Group;\n roles: string[];\n};\n\nexport type RawUserMembership = {\n [key: string]: {\n tenant_id: string;\n tenant_name: string;\n tenant_roles?: string[];\n root_group_id: string;\n groups: {\n [key: string]: string[];\n };\n group_names: { [key: string]: string };\n };\n};\n\nexport type UserMembership = {\n raw: RawUserMembership;\n tenants: TenantMembership[];\n};\n\nexport const parseMembership = (raw: RawUserMembership): UserMembership => {\n const tenants: TenantMembership[] = [];\n let k: string;\n for (k in raw) {\n const v = raw[k];\n if (v === undefined) {\n continue;\n }\n const tnt: TenantMembership = { tenant: { id: v.tenant_id, name: v.tenant_name } };\n tnt.groups = v.groups\n ? Object.keys(v.groups).map((gk) => {\n const roles = v.groups[gk] || [];\n return { group: { id: gk, name: v.group_names?.[gk] ?? 'unknown' }, roles };\n })\n : [];\n tnt.tenantRoles = tnt.groups?.find((g) => g.group.id === v.root_group_id);\n tenants.push(tnt);\n }\n return { raw, tenants };\n};\n","/**\n * Token Service\n *\n * JWT token parsing, validation, and utility functions.\n * Provides platform-agnostic Base64 decoding for SSR/Node.js support.\n * Handles token expiry calculations with configurable buffer time.\n *\n * @module token\n */\n\nimport { TOKEN_EXPIRY_BUFFER_SECONDS } from '../constants';\nimport { StorageManager } from '../storage';\n\nimport { parseMembership } from './membership';\nimport { Token, TokenType } from './token';\n\n/**\n * Decodes a Base64-encoded string in a platform-agnostic way.\n * Works in both browser (using atob) and Node.js (using Buffer) environments.\n *\n * @param base64 - The Base64 string to decode\n * @returns Decoded string\n */\nfunction decodeBase64(base64: string): string {\n // Browser environment\n if (typeof window !== 'undefined' && typeof window.atob === 'function') {\n return window.atob(base64);\n }\n\n // Node.js environment\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(base64, 'base64').toString('utf-8');\n }\n\n throw new Error('No Base64 decoding method available in this environment');\n}\n\nexport class TokenService {\n protected storageManager: StorageManager;\n\n constructor(storageManager: StorageManager) {\n this.storageManager = storageManager;\n }\n\n /**\n * Checks if a token is not exists or expired.\n *\n * @param {TokenType} ttype - The token type to check.\n * @returns {boolean} Returns true if the token is expired or not exists, false otherwise.\n */\n isTokenTypeExpired(ttype: TokenType): boolean {\n const tokenString = this.storageManager.getToken(ttype);\n if (!tokenString) return true;\n\n const token = parseToken(tokenString);\n return token ? isTokenExpired(token) : true;\n }\n\n /**\n * Parse token from storage by type.\n * Please be aware that this method does not check if the token signature and if the token is valid.\n *\n * @param {TokenType} tokenType - The token type to check.\n * @returns {Token | undefined} Returns token with parsed user membership or undefined.\n */\n parseTokenType(tokenType: TokenType): Token | undefined {\n const token = this.storageManager.getToken(tokenType);\n if (!token) return undefined;\n return parseToken(token);\n }\n}\n\n/**\n * Checks if a token is expired.\n *\n * @param {Token} token - The token to check.\n * @param {number} bufferSeconds - Time buffer in seconds to consider token expired early\n * This prevents race conditions where token expires during request\n * @returns {boolean} Returns true if the token is expired or will expire within buffer time, false otherwise.\n */\nexport function isTokenExpired(token: Token, bufferSeconds = TOKEN_EXPIRY_BUFFER_SECONDS): boolean {\n const currentUnixTime = Math.floor(Date.now() / 1000);\n return currentUnixTime + bufferSeconds > token.exp;\n}\n\n/**\n * Parse token from string. Please be aware that this method does not check if the token signature and if the token is valid.\n *\n * @param {string} tokenString - The token string representation.\n * @returns {Token } Returns token with parsed user membership or undefined.\n */\nexport function parseToken(tokenString: string): Token {\n const base64Url = tokenString.split('.')[1];\n\n if (!base64Url) throw new Error('Invalid token string');\n\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if necessary (some JWTs don't include padding)\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n\n // Use the platform-agnostic decoder\n const decoded = decodeBase64(padded);\n\n const jsonPayload = decodeURIComponent(\n decoded\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n\n const parsedToken = JSON.parse(jsonPayload) as Token;\n parsedToken.membership =\n parsedToken.passflow_tm && parsedToken.type !== 'invite' ? parseMembership(parsedToken.passflow_tm) : undefined;\n return parsedToken;\n}\n","import { RawUserMembership, UserMembership } from './membership';\n\nexport type Token = {\n aud: string[];\n exp: number;\n iat: number;\n iss: string;\n jti: string;\n sub: string;\n type: string;\n email?: string;\n phonenumber?: string;\n passflow_tm?: RawUserMembership;\n payload?: unknown;\n membership?: UserMembership;\n};\n\nexport type InvitationToken = Token & {\n email: string;\n inviter_id: string;\n inviter_name: string;\n redirect_url: string;\n tenant_name: string;\n};\n\n/**\n * Token types used in the Passflow SDK.\n *\n * Note: Some enum values intentionally differ from keys for API compatibility.\n * The values match what the Passflow API returns in token payloads.\n *\n * @example\n * // access_token key maps to 'access' value (API response format)\n * TokenType.access_token === 'access' // true\n */\nexport enum TokenType {\n /** ID token - contains user identity claims */\n id_token = 'id_token',\n /** Access token - for API authorization. Maps to 'access' in API responses */\n access_token = 'access',\n /** Refresh token - for obtaining new access tokens. Maps to 'refresh' in API responses */\n refresh_token = 'refresh',\n /** Invitation token - for accepting user invitations */\n invite_token = 'invite',\n /** Password reset token - for password reset flows */\n reset_token = 'reset',\n /** Web cookie token - for web-based session management */\n web_cookie = 'web-cookie',\n /** Management token - for administrative operations */\n management = 'management',\n /** Sign-in token - for authentication flows */\n signin = 'signin',\n /** Actor token - for impersonation and delegated access */\n actor = 'actor',\n /** Two-factor authentication challenge token - for 2FA flows */\n two_factor = '2fa',\n}\n","/**\n * Token Delivery Manager\n *\n * Manages token delivery mode state and session validity for the SDK.\n * Supports three delivery modes: JSON body, cookie, and mobile.\n * Persists delivery mode to storage for session continuity.\n *\n * @module token/delivery-manager\n */\n\nimport type { StorageManager } from '../storage';\n\nexport enum TokenDeliveryMode {\n JsonBody = 'json_body',\n Cookie = 'cookie',\n Mobile = 'mobile',\n /**\n * BFF (Backend-for-Frontend) mode.\n * Tokens are sent to BFF server which stores them in httpOnly cookies.\n * Only ID token is kept locally for user info display.\n */\n BFF = 'bff',\n}\n\nexport enum SessionState {\n Unknown = 'unknown', // Initial state, not yet determined\n Valid = 'valid', // Session is valid (cookie mode trust state)\n Invalid = 'invalid', // Received 401, session expired\n}\n\nexport class TokenDeliveryManager {\n private mode: TokenDeliveryMode = TokenDeliveryMode.JsonBody;\n private sessionState: SessionState = SessionState.Unknown;\n private isInitializedFlag = false;\n\n private readonly STORAGE_PREFIX = 'passflow_';\n private readonly DELIVERY_MODE_KEY = `${this.STORAGE_PREFIX}delivery_mode`;\n private readonly SESSION_STATE_KEY = `${this.STORAGE_PREFIX}session_state`;\n\n constructor(private storageManager: StorageManager) {\n this.loadPersistedMode();\n this.loadPersistedSessionState();\n }\n\n /**\n * Set the token delivery mode\n */\n setMode(mode: TokenDeliveryMode): void {\n this.mode = mode;\n this.isInitializedFlag = true;\n this.persistMode();\n }\n\n /**\n * Get the current token delivery mode\n */\n getMode(): TokenDeliveryMode {\n return this.mode;\n }\n\n /**\n * Check if currently in cookie mode\n */\n isCookieMode(): boolean {\n return this.mode === TokenDeliveryMode.Cookie;\n }\n\n /**\n * Check if currently in JSON body mode\n */\n isJsonMode(): boolean {\n return this.mode === TokenDeliveryMode.JsonBody;\n }\n\n /**\n * Check if currently in mobile mode\n */\n isMobileMode(): boolean {\n return this.mode === TokenDeliveryMode.Mobile;\n }\n\n /**\n * Check if currently in BFF mode\n */\n isBFFMode(): boolean {\n return this.mode === TokenDeliveryMode.BFF;\n }\n\n /**\n * Check if delivery mode has been initialized from a server response\n */\n isInitialized(): boolean {\n return this.isInitializedFlag;\n }\n\n /**\n * Mark session as valid (successful authentication or token refresh)\n */\n setSessionValid(): void {\n this.sessionState = SessionState.Valid;\n this.persistSessionState();\n }\n\n /**\n * Mark session as invalid (received 401 or logout)\n */\n setSessionInvalid(): void {\n this.sessionState = SessionState.Invalid;\n this.persistSessionState();\n }\n\n /**\n * Reset session state to unknown (used during authentication flows)\n */\n setSessionUnknown(): void {\n this.sessionState = SessionState.Unknown;\n this.persistSessionState();\n }\n\n /**\n * Check if session is valid\n */\n isSessionValid(): boolean {\n return this.sessionState === SessionState.Valid;\n }\n\n /**\n * Check if session state is unknown (not yet determined)\n */\n isSessionUnknown(): boolean {\n return this.sessionState === SessionState.Unknown;\n }\n\n /**\n * Check if session is invalid\n */\n isSessionInvalid(): boolean {\n return this.sessionState === SessionState.Invalid;\n }\n\n /**\n * Get current session state\n */\n getSessionState(): SessionState {\n return this.sessionState;\n }\n\n /**\n * Reset delivery manager to initial state\n */\n reset(): void {\n this.mode = TokenDeliveryMode.JsonBody;\n this.sessionState = SessionState.Unknown;\n this.isInitializedFlag = false;\n this.clearPersistedMode();\n this.clearPersistedSessionState();\n }\n\n /**\n * Load persisted delivery mode from storage\n */\n private loadPersistedMode(): void {\n try {\n const persistedMode = this.storageManager['storage'].getItem(this.DELIVERY_MODE_KEY);\n if (persistedMode) {\n // Validate that the persisted mode is a valid enum value\n if (Object.values(TokenDeliveryMode).includes(persistedMode as TokenDeliveryMode)) {\n this.mode = persistedMode as TokenDeliveryMode;\n this.isInitializedFlag = true;\n }\n }\n } catch (_error) {\n // Silently ignore storage errors during load\n }\n }\n\n /**\n * Load persisted session state from storage\n */\n private loadPersistedSessionState(): void {\n try {\n const persistedState = this.storageManager['storage'].getItem(this.SESSION_STATE_KEY);\n if (persistedState) {\n // Validate that the persisted state is a valid enum value\n if (Object.values(SessionState).includes(persistedState as SessionState)) {\n this.sessionState = persistedState as SessionState;\n }\n }\n } catch (_error) {\n // Silently ignore storage errors during load\n }\n }\n\n /**\n * Persist delivery mode to storage\n */\n private persistMode(): void {\n try {\n this.storageManager['storage'].setItem(this.DELIVERY_MODE_KEY, this.mode);\n } catch (_error) {\n // Silently ignore storage errors during persist\n }\n }\n\n /**\n * Persist session state to storage\n */\n private persistSessionState(): void {\n try {\n this.storageManager['storage'].setItem(this.SESSION_STATE_KEY, this.sessionState);\n } catch (_error) {\n // Silently ignore storage errors during persist\n }\n }\n\n /**\n * Clear persisted delivery mode from storage\n */\n private clearPersistedMode(): void {\n try {\n this.storageManager['storage'].removeItem(this.DELIVERY_MODE_KEY);\n } catch (_error) {\n // Silently ignore storage errors during clear\n }\n }\n\n /**\n * Clear persisted session state from storage\n */\n private clearPersistedSessionState(): void {\n try {\n this.storageManager['storage'].removeItem(this.SESSION_STATE_KEY);\n } catch (_error) {\n // Silently ignore storage errors during clear\n }\n }\n}\n","/**\n * Storage Manager\n *\n * Abstraction layer over browser localStorage for token and data persistence.\n * Supports custom storage implementations for testing and alternative platforms.\n * Handles key prefixing for multi-app scenarios.\n *\n * @module storage\n */\n\nimport { TokenType } from '../token';\nimport { TokenDeliveryMode, Tokens } from '../types';\n\nexport type Storage = {\n setItem: (key: string, value: string) => void;\n getItem: (key: string) => string | null;\n removeItem: (key: string) => void;\n};\n\nexport interface StorageManagerParams {\n storage?: Storage;\n prefix?: string;\n}\n\nexport class StorageManager {\n private keyStoragePrefix = '';\n readonly scopes = `${this.keyStoragePrefix}tokens_scopes`;\n readonly deviceId = `${this.keyStoragePrefix}passflowDeviceId`;\n readonly invitationToken = `${this.keyStoragePrefix}passflowInvitationToken`;\n readonly previousRedirectUrl = `${this.keyStoragePrefix}passflowPreviousRedirectUrl`;\n\n // Namespaced keys for cookie mode support\n private readonly STORAGE_PREFIX = 'passflow_';\n private readonly ID_TOKEN_KEY = `${this.STORAGE_PREFIX}id_token`;\n private readonly CSRF_TOKEN_KEY = `${this.STORAGE_PREFIX}csrf_token`;\n private readonly DELIVERY_MODE_KEY = `${this.STORAGE_PREFIX}delivery_mode`;\n\n private storage: Storage;\n\n constructor({ storage, prefix }: StorageManagerParams = {}) {\n this.storage = storage ?? localStorage;\n this.keyStoragePrefix = prefix ? `${prefix}_` : '';\n }\n\n /**\n * Save tokens to storage with conditional logic based on delivery mode\n * In cookie/BFF mode: ONLY save ID token (not access/refresh tokens)\n * In JSON mode: save all tokens (existing behavior)\n */\n saveTokens(tokens: Tokens, deliveryMode?: TokenDeliveryMode): void {\n const { id_token, access_token, refresh_token, scopes } = tokens;\n\n if (deliveryMode === TokenDeliveryMode.Cookie || deliveryMode === TokenDeliveryMode.BFF) {\n // Cookie/BFF mode: ONLY save ID token (access/refresh in HttpOnly cookies)\n if (id_token) {\n this.storage.setItem(this.ID_TOKEN_KEY, id_token);\n }\n // Do NOT save access_token or refresh_token in localStorage\n } else {\n // JSON mode: save all tokens (existing behavior)\n if (id_token) this.storage.setItem(this.getKeyForTokenType(TokenType.id_token), id_token);\n if (access_token) this.storage.setItem(this.getKeyForTokenType(TokenType.access_token), access_token);\n if (refresh_token) this.storage.setItem(this.getKeyForTokenType(TokenType.refresh_token), refresh_token);\n if (scopes) this.storage.setItem(this.scopes, scopes.join(','));\n }\n }\n\n getToken(tokenType: TokenType): string | undefined {\n const key = this.getKeyForTokenType(tokenType);\n return this.storage.getItem(key) ?? undefined;\n }\n\n /**\n * Get tokens from storage with conditional logic based on delivery mode\n * In cookie/BFF mode: return ID token only (access/refresh in HttpOnly cookies)\n * In JSON mode: return all stored tokens (existing behavior)\n */\n getTokens(): Tokens | undefined {\n const mode = this.getDeliveryMode();\n\n if (mode === TokenDeliveryMode.Cookie || mode === TokenDeliveryMode.BFF) {\n // Cookie/BFF mode: return ID token only (access/refresh in HttpOnly cookies)\n const idToken = this.storage.getItem(this.ID_TOKEN_KEY);\n if (!idToken) return undefined;\n return {\n id_token: idToken,\n // access_token and refresh_token are in HttpOnly cookies, not localStorage\n };\n }\n\n // JSON mode: return all stored tokens (existing behavior)\n const access = this.storage.getItem(this.getKeyForTokenType(TokenType.access_token));\n if (!access) return undefined;\n return {\n access_token: access,\n id_token: this.storage.getItem(this.getKeyForTokenType(TokenType.id_token)) ?? undefined,\n refresh_token: this.storage.getItem(this.getKeyForTokenType(TokenType.refresh_token)) ?? undefined,\n scopes: this.storage.getItem(this.scopes)?.split(',') ?? undefined,\n };\n }\n\n getScopes(): string[] | undefined {\n return this.storage.getItem(this.scopes)?.split(',') ?? undefined;\n }\n\n /**\n * Check if JSON mode tokens exist in storage (ignores delivery mode)\n * Used to detect stale state where delivery_mode is set but JSON tokens exist\n */\n hasJsonModeTokens(): boolean {\n const accessToken = this.storage.getItem(this.getKeyForTokenType(TokenType.access_token));\n return !!accessToken;\n }\n\n /**\n * Check if cookie mode ID token exists in storage\n * Used to detect legitimate cookie/BFF mode sessions\n */\n hasCookieModeIdToken(): boolean {\n const idToken = this.storage.getItem(this.ID_TOKEN_KEY);\n return !!idToken;\n }\n\n deleteToken(tokenType: TokenType): void {\n const key = this.getKeyForTokenType(tokenType);\n this.storage.removeItem(key);\n }\n\n deleteTokens(): void {\n // Clear JSON mode tokens\n this.storage.removeItem(this.getKeyForTokenType(TokenType.id_token));\n this.storage.removeItem(this.getKeyForTokenType(TokenType.access_token));\n this.storage.removeItem(this.getKeyForTokenType(TokenType.refresh_token));\n this.storage.removeItem(this.scopes);\n\n // Clear cookie mode ID token\n this.clearIdToken();\n\n // Clear delivery mode and CSRF token to ensure clean state for next session\n this.clearDeliveryMode();\n this.clearCsrfToken();\n }\n\n getDeviceId(): string | undefined {\n return this.storage.getItem(this.deviceId) ?? undefined;\n }\n\n setDeviceId(deviceId: string): void {\n this.storage.setItem(this.deviceId, deviceId);\n }\n\n deleteDeviceId(): void {\n this.storage.removeItem(this.deviceId);\n }\n\n setInvitationToken(token: string): void {\n this.storage.setItem(this.invitationToken, token);\n }\n\n getInvitationToken(): string | undefined {\n return this.storage.getItem(this.invitationToken) ?? undefined;\n }\n\n deleteInvitationToken(): void {\n this.storage.removeItem(this.invitationToken);\n }\n\n setPreviousRedirectUrl(url: string): void {\n this.storage.setItem(this.previousRedirectUrl, url);\n }\n\n getPreviousRedirectUrl(): string | undefined {\n return this.storage.getItem(this.previousRedirectUrl) ?? undefined;\n }\n\n deletePreviousRedirectUrl(): void {\n this.storage.removeItem(this.previousRedirectUrl);\n }\n\n // Delivery mode storage methods\n\n /**\n * Set the token delivery mode in storage\n */\n setDeliveryMode(mode: TokenDeliveryMode): void {\n try {\n this.storage.setItem(this.DELIVERY_MODE_KEY, mode);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n /**\n * Get the token delivery mode from storage\n */\n getDeliveryMode(): TokenDeliveryMode | undefined {\n try {\n const mode = this.storage.getItem(this.DELIVERY_MODE_KEY);\n if (mode && Object.values(TokenDeliveryMode).includes(mode as TokenDeliveryMode)) {\n return mode as TokenDeliveryMode;\n }\n } catch (_error) {\n // Silently ignore storage errors\n }\n return undefined;\n }\n\n /**\n * Clear the delivery mode from storage\n */\n clearDeliveryMode(): void {\n try {\n this.storage.removeItem(this.DELIVERY_MODE_KEY);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n // ID token storage methods (for cookie mode)\n\n /**\n * Get the ID token from storage (cookie mode)\n */\n getIdToken(): string | undefined {\n try {\n return this.storage.getItem(this.ID_TOKEN_KEY) ?? undefined;\n } catch (_error) {\n // Silently ignore storage errors\n return undefined;\n }\n }\n\n /**\n * Set the ID token in storage (cookie mode)\n */\n setIdToken(token: string): void {\n try {\n this.storage.setItem(this.ID_TOKEN_KEY, token);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n /**\n * Clear the ID token from storage\n */\n clearIdToken(): void {\n try {\n this.storage.removeItem(this.ID_TOKEN_KEY);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n // CSRF token storage methods\n\n /**\n * Get the CSRF token from storage\n */\n getCsrfToken(): string | undefined {\n try {\n return this.storage.getItem(this.CSRF_TOKEN_KEY) ?? undefined;\n } catch (_error) {\n // Silently ignore storage errors\n return undefined;\n }\n }\n\n /**\n * Set the CSRF token in storage\n */\n setCsrfToken(token: string): void {\n try {\n this.storage.setItem(this.CSRF_TOKEN_KEY, token);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n /**\n * Clear the CSRF token from storage\n */\n clearCsrfToken(): void {\n try {\n this.storage.removeItem(this.CSRF_TOKEN_KEY);\n } catch (_error) {\n // Silently ignore storage errors\n }\n }\n\n private getKeyForTokenType(tokenType: TokenType): string {\n return `${this.keyStoragePrefix}${tokenType}`;\n }\n}\n","/**\n * Device Service\n *\n * Manages device identification for security and tracking purposes.\n * Generates and persists unique device IDs using UUID v4.\n * Used for device-based authentication and session management.\n *\n * @module device\n */\n\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { StorageManager } from '../storage';\n\nexport class DeviceService {\n private storageManager: StorageManager;\n\n constructor(storageManager?: StorageManager) {\n this.storageManager = storageManager ?? new StorageManager();\n }\n\n getDeviceId(): string {\n const deviceId = this.storageManager.getDeviceId();\n if (!deviceId) {\n const newDeviceId = this.generateUniqueDeviceId();\n this.storageManager.setDeviceId(newDeviceId);\n return newDeviceId;\n }\n return deviceId;\n }\n\n generateUniqueDeviceId(): string {\n return uuidv4();\n }\n}\n","import type {\n AuthenticationResponseJSON,\n PublicKeyCredentialCreationOptionsJSON,\n PublicKeyCredentialRequestOptionsJSON,\n RegistrationResponseJSON,\n} from '@simplewebauthn/types';\n\nimport type { Storage } from '../storage';\n\n// ----------------------------------------------------------------\n// Storage\n// ----------------------------------------------------------------\n\n/**\n * Synchronous key-value storage interface.\n * Compatible with localStorage (web) and react-native-mmkv (RN).\n * Alias of the Storage type in lib/storage/index.ts to maintain a single source of truth.\n */\nexport type PlatformStorage = Storage;\n\n// ----------------------------------------------------------------\n// Navigation\n// ----------------------------------------------------------------\n\n/**\n * Result returned by getCurrentUrl().\n * Provides structured access to URL components so adapters don't\n * need to parse a URL string themselves.\n */\nexport type CurrentUrlInfo = {\n /** Full URL string, e.g. \"https://app.com/callback?access_token=abc#hash\" */\n href: string;\n /** Origin, e.g. \"https://app.com\" */\n origin: string;\n /** Pathname, e.g. \"/callback\" */\n pathname: string;\n /** Query string including \"?\", e.g. \"?access_token=abc\" */\n search: string;\n /** Hash including \"#\", e.g. \"#access_token=abc\" */\n hash: string;\n /** Hostname without port, e.g. \"app.com\" */\n hostname: string;\n};\n\n// ----------------------------------------------------------------\n// Auth UI\n// ----------------------------------------------------------------\n\n/**\n * Options passed to openAuthUrl(). Adapters can use these to decide\n * whether to open a popup, redirect, or use a deep link.\n */\nexport type OpenAuthUrlOptions = {\n /**\n * Preferred presentation mode. Adapters may ignore this and use\n * whatever makes sense for the platform.\n * - \"popup\": Open in a popup window (web default)\n * - \"redirect\": Full-page redirect (web fallback)\n * - \"deeplink\": Open with a deep link (React Native)\n */\n mode?: 'popup' | 'redirect' | 'deeplink';\n /** Popup width hint (web only) */\n width?: number;\n /** Popup height hint (web only) */\n height?: number;\n /** Origin to watch for the popup to return to (web popup mode only) */\n expectedReturnOrigin?: string;\n};\n\n/**\n * Result returned when the auth flow completes.\n * The adapter is responsible for detecting when auth is done and\n * returning the tokens from the callback URL.\n */\nexport type AuthCallbackResult = {\n /** Raw query string from the callback URL, e.g. \"?access_token=abc\" */\n callbackSearch: string;\n /** Raw hash from the callback URL, e.g. \"#access_token=abc\" */\n callbackHash: string;\n};\n\n// ----------------------------------------------------------------\n// Passkeys (optional)\n// ----------------------------------------------------------------\n\n/**\n * Optional passkey support. Adapters that do not support passkeys\n * return undefined from these methods. The calling code must check\n * for undefined before using the result.\n */\nexport type PasskeySupport = {\n /**\n * Start a passkey registration ceremony.\n * @returns Registration response, or undefined if not supported.\n */\n startRegistration(options: PublicKeyCredentialCreationOptionsJSON): Promise<RegistrationResponseJSON | undefined>;\n\n /**\n * Start a passkey authentication ceremony.\n * @returns Authentication response, or undefined if not supported.\n */\n startAuthentication(options: PublicKeyCredentialRequestOptionsJSON): Promise<AuthenticationResponseJSON | undefined>;\n};\n\n// ----------------------------------------------------------------\n// Errors\n// ----------------------------------------------------------------\n\n/**\n * Error thrown when the authentication popup is closed by the user\n * before authentication completes. Use `instanceof PopupClosedError`\n * for type-safe error discrimination instead of string matching.\n */\nexport class PopupClosedError extends Error {\n constructor(message = 'Authentication popup was closed') {\n super(message);\n this.name = 'PopupClosedError';\n }\n}\n\n// ----------------------------------------------------------------\n// Main Interface\n// ----------------------------------------------------------------\n\n/**\n * PlatformAdapter abstracts all platform-specific APIs that the\n * Passflow SDK needs. Implement this interface to add support for\n * a new platform (e.g. React Native).\n *\n * All methods have sensible defaults in WebAdapter. You only need\n * to implement what your platform supports.\n */\nexport interface PlatformAdapter {\n // ------ Storage ------\n\n /**\n * Synchronous key-value storage for tokens and SDK state.\n * Must be synchronous. Use localStorage on web, MMKV on React Native.\n */\n readonly storage: PlatformStorage;\n\n // ------ Navigation ------\n\n /**\n * Returns the current URL of the application.\n * On web: wraps window.location.\n * On React Native: implement using your routing library (e.g. Linking.getInitialURL).\n *\n * May return null in environments where URL is not meaningful (e.g. during SSR).\n */\n getCurrentUrl(): CurrentUrlInfo | null;\n\n /**\n * Navigates to the given URL, replacing the current history entry.\n * On web: window.history.replaceState(...)\n * On React Native: no-op or use your router's replace method.\n */\n replaceUrl(url: string): void;\n\n /**\n * Navigates to the given URL, performing a full navigation.\n * On web: window.location.href = url\n * On React Native: use Linking.openURL or your router.\n */\n navigateTo(url: string): void;\n\n // ------ Lifecycle ------\n\n /**\n * Register a callback invoked when the app comes to the foreground\n * (page becomes visible / app resumes).\n * Returns an unsubscribe function.\n *\n * On web: document.addEventListener('visibilitychange', ...)\n * On React Native: AppState.addEventListener('change', ...)\n */\n onForeground(callback: () => void): () => void;\n\n /**\n * Register a callback invoked when the app goes to the background\n * (page hidden / app suspended).\n * Returns an unsubscribe function.\n *\n * On web: document.addEventListener('visibilitychange', ...)\n * On React Native: AppState.addEventListener('change', ...)\n *\n * NOTE: This method is intentionally not called by the SDK internally.\n * It is part of the interface to support React Native adapters which need\n * to perform cleanup (e.g., flush queues) when the app is backgrounded.\n * Web adapters may implement it as a no-op or use visibilitychange.\n */\n onBackground(callback: () => void): () => void;\n\n /**\n * Register a callback invoked just before the page/app is closed.\n * Returns an unsubscribe function.\n *\n * On web: window.addEventListener('beforeunload', ...)\n * On React Native: implement as a no-op (RN handles cleanup differently).\n */\n onBeforeUnload(callback: () => void): () => void;\n\n /**\n * Returns true if the app is currently in the foreground (page visible).\n * On web: !document.hidden\n * On React Native: AppState.currentState === 'active'\n */\n isInForeground(): boolean;\n\n // ------ Auth UI ------\n\n /**\n * Opens a URL for federated/OAuth authentication.\n *\n * Web implementation:\n * - \"popup\" mode: Opens a popup via window.open(), polls for the callback,\n * resolves when the popup returns to expectedReturnOrigin.\n * - \"redirect\" mode: Full-page redirect (window.location.href = url).\n * Returns a Promise that never resolves (page navigates away).\n *\n * React Native implementation:\n * - Opens the URL via Linking.openURL() with a deep link scheme.\n * - Resolves when the deep link callback is received.\n *\n * @returns Promise that resolves with the callback URL tokens,\n * or rejects if auth fails or is cancelled.\n * In redirect mode on web, the promise may never resolve.\n */\n openAuthUrl(url: string, options?: OpenAuthUrlOptions): Promise<AuthCallbackResult>;\n\n // ------ Passkeys ------\n\n /**\n * Optional passkey support. If the platform does not support passkeys,\n * this property is undefined. The SDK checks for undefined before calling.\n */\n readonly passkeys?: PasskeySupport;\n\n // ------ Base64 ------\n\n /**\n * Encodes a string to Base64.\n * On web: btoa(str)\n * On React Native: use base64-js or Buffer.\n */\n btoa(str: string): string;\n\n /**\n * Decodes a Base64 string.\n * On web: atob(str) or Buffer fallback for SSR.\n * On React Native: use base64-js or Buffer.\n */\n atob(str: string): string;\n\n // ------ Cookies ------\n\n /**\n * Returns true if the platform supports HTTP cookies.\n * On web: true (with a quick write/read test).\n * On React Native: false. Cookie mode is not available.\n *\n * When this returns false, cookie token delivery mode must not be used.\n * The SDK will throw a descriptive error if cookie mode is configured\n * on a platform where cookies are not supported.\n */\n cookiesSupported(): boolean;\n\n // ------ Device ------\n\n /**\n * Returns the platform/device type.\n * Used to set the X-Device-Type header on API requests.\n */\n getDeviceType(): 'web' | 'ios' | 'android';\n}\n","import { startAuthentication, startRegistration } from '@simplewebauthn/browser';\nimport { POPUP_HEIGHT, POPUP_POLL_INTERVAL_MS, POPUP_TIMEOUT_MS, POPUP_WIDTH } from '../constants';\nimport {\n type AuthCallbackResult,\n type CurrentUrlInfo,\n type OpenAuthUrlOptions,\n type PasskeySupport,\n type PlatformAdapter,\n type PlatformStorage,\n PopupClosedError,\n} from './types';\n\nexport class WebAdapter implements PlatformAdapter {\n // ----------------------------------------------------------------\n // Storage\n // Replaces: StorageManager constructor default `storage ?? localStorage`\n // File: lib/storage/index.ts line 41\n // ----------------------------------------------------------------\n get storage(): PlatformStorage {\n if (typeof localStorage === 'undefined') {\n // SSR/Node environment: return a no-op storage that never throws\n return {\n getItem: () => null,\n // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op for SSR\n setItem: () => {},\n // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op for SSR\n removeItem: () => {},\n };\n }\n return localStorage;\n }\n\n // ----------------------------------------------------------------\n // Navigation\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * window.location.origin (passflow.ts:95, axios-client.ts:49)\n * window.location.search (passflow.ts:338,416, auth-service.ts:610,826)\n * window.location.hash (passflow.ts:342)\n * window.location.href (auth-service.ts:826)\n * window.location.pathname (passflow.ts:429,441,443)\n * window?.location?.hostname (user-service.ts:57)\n */\n getCurrentUrl(): CurrentUrlInfo | null {\n if (typeof window === 'undefined') return null;\n return {\n href: window.location.href,\n origin: window.location.origin,\n pathname: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n hostname: window.location.hostname,\n };\n }\n\n /**\n * Replaces:\n * window.history.replaceState({}, document.title, ...)\n * (passflow.ts:429, 441, 443)\n */\n replaceUrl(url: string): void {\n if (typeof window === 'undefined') return;\n window.history.replaceState({}, document.title, url);\n }\n\n /**\n * Replaces:\n * window.location.href = url\n * (auth-service.ts:795, 813, 845, passflow.ts indirectly)\n */\n navigateTo(url: string): void {\n if (typeof window === 'undefined') return;\n window.location.href = url;\n }\n\n // ----------------------------------------------------------------\n // Lifecycle\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * document.addEventListener('visibilitychange', handler) where !document.hidden\n * (token-cache-service.ts:134-145)\n */\n onForeground(callback: () => void): () => void {\n if (typeof document === 'undefined')\n return () => {\n /* no-op in SSR */\n };\n const handler = () => {\n if (!document.hidden) callback();\n };\n document.addEventListener('visibilitychange', handler);\n return () => document.removeEventListener('visibilitychange', handler);\n }\n\n /**\n * Replaces:\n * document.addEventListener('visibilitychange', handler) where document.hidden\n * (token-cache-service.ts:134-145, complementary to onForeground)\n *\n * NOTE: Not currently called by the SDK. Included in the interface for\n * React Native adapter compatibility (future use).\n */\n onBackground(callback: () => void): () => void {\n if (typeof document === 'undefined')\n return () => {\n /* no-op in SSR */\n };\n const handler = () => {\n if (document.hidden) callback();\n };\n document.addEventListener('visibilitychange', handler);\n return () => document.removeEventListener('visibilitychange', handler);\n }\n\n /**\n * Replaces:\n * window.addEventListener('beforeunload', ...)\n * (token-cache-service.ts:151)\n */\n onBeforeUnload(callback: () => void): () => void {\n if (typeof window === 'undefined')\n return () => {\n /* no-op in SSR */\n };\n window.addEventListener('beforeunload', callback);\n return () => window.removeEventListener('beforeunload', callback);\n }\n\n /**\n * Replaces:\n * typeof document !== 'undefined' && document.hidden\n * (token-cache-service.ts:112)\n */\n isInForeground(): boolean {\n if (typeof document === 'undefined') return true;\n return !document.hidden;\n }\n\n // ----------------------------------------------------------------\n // Auth UI\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * window.open(passflowURL, '_blank', `width=...,height=...`)\n * + polling loop with popupWindow.location.href/search\n * + window.location.href = origin (post-auth redirect)\n * (auth-service.ts:741-805)\n *\n * window.location.href = passflowURL (redirect mode)\n * (auth-service.ts:813)\n */\n openAuthUrl(url: string, options: OpenAuthUrlOptions = {}): Promise<AuthCallbackResult> {\n const mode = options.mode ?? 'popup';\n\n if (mode === 'redirect') {\n // Full-page redirect — page will reload; promise never resolves\n window.location.href = url;\n return new Promise(() => {\n /* intentionally never resolves — page navigates away */\n });\n }\n\n if (mode === 'deeplink') {\n return Promise.reject(\n new Error(\n 'openAuthUrl: \"deeplink\" mode is not supported on web. ' +\n 'Use \"popup\" or \"redirect\" instead, or provide a native platform adapter.',\n ),\n );\n }\n\n // Popup mode (default)\n const width = options.width ?? POPUP_WIDTH;\n const height = options.height ?? POPUP_HEIGHT;\n const expectedReturnOrigin = options.expectedReturnOrigin ?? window.location.origin;\n\n const popupWindow = window.open(url, '_blank', `width=${width},height=${height}`);\n\n if (!popupWindow) {\n // Popup blocked — fall back to redirect\n window.location.href = url;\n return new Promise(() => {\n /* intentionally never resolves — falling back to redirect */\n });\n }\n\n return new Promise<AuthCallbackResult>((resolve, reject) => {\n const startTime = Date.now();\n\n const checkInterval = setInterval(() => {\n if (popupWindow.closed) {\n clearInterval(checkInterval);\n reject(new PopupClosedError());\n return;\n }\n\n if (Date.now() - startTime > POPUP_TIMEOUT_MS) {\n clearInterval(checkInterval);\n popupWindow.close();\n reject(new Error('Authentication popup timed out'));\n return;\n }\n\n try {\n if (popupWindow.location.href.startsWith(expectedReturnOrigin)) {\n const callbackSearch = popupWindow.location.search;\n const callbackHash = popupWindow.location.hash;\n clearInterval(checkInterval);\n popupWindow.close();\n resolve({ callbackSearch, callbackHash });\n }\n } catch {\n // Expected cross-origin error — continue polling\n }\n }, POPUP_POLL_INTERVAL_MS);\n });\n }\n\n // ----------------------------------------------------------------\n // Passkeys\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * startRegistration({ optionsJSON: publicKey }) from @simplewebauthn/browser\n * (auth-service.ts:645-646, user-service.ts:65)\n *\n * startAuthentication({ optionsJSON: publicKey }) from @simplewebauthn/browser\n * (auth-service.ts:681)\n */\n readonly passkeys: PasskeySupport = {\n startRegistration: (options) => startRegistration({ optionsJSON: options }),\n startAuthentication: (options) => startAuthentication({ optionsJSON: options }),\n };\n\n // ----------------------------------------------------------------\n // Base64\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * btoa(publicKey.user.id)\n * (auth-service.ts:644, user-service.ts:64)\n */\n btoa(str: string): string {\n return btoa(str);\n }\n\n /**\n * Replaces:\n * window.atob(base64) / Buffer.from(base64, 'base64').toString('utf-8')\n * (token/service.ts:26-27)\n */\n atob(str: string): string {\n if (typeof window !== 'undefined' && typeof window.atob === 'function') {\n return window.atob(str);\n }\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n throw new Error('No Base64 decoding method available in this environment');\n }\n\n // ----------------------------------------------------------------\n // Cookies\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * document.cookie write/read test\n * (axios-client.ts:221-223)\n */\n cookiesSupported(): boolean {\n if (typeof document === 'undefined') return false;\n try {\n document.cookie = 'passflow_test=1; SameSite=Lax';\n const enabled = document.cookie.indexOf('passflow_test=1') !== -1;\n document.cookie = 'passflow_test=; expires=Thu, 01 Jan 1970 00:00:00 UTC';\n return enabled;\n } catch {\n return false;\n }\n }\n\n // ----------------------------------------------------------------\n // Device\n // ----------------------------------------------------------------\n\n /**\n * Replaces:\n * Hardcoded 'web' string in DEVICE_TYPE_HEADER_KEY header\n * (axios-client.ts:91)\n * OS.web enum value (auth-service.ts:244, 358, 637, 676, user-service.ts:55)\n */\n getDeviceType(): 'web' | 'ios' | 'android' {\n return 'web';\n }\n}\n\n/**\n * Singleton instance used as the default adapter.\n * Exported so consuming code can reference it without allocating a new instance.\n */\nexport const webAdapter = new WebAdapter();\n","/**\n * The one implementation of \"what level does this `acr_values` string demand\".\n *\n * `challenge.ts` and `select.ts` both import it, so there is exactly one answer\n * in this package (§13.2.1b). It is consistent with the Go model *where the Go\n * model parses a single value* (`Level(\"2\") === 2`) and deliberately diverges\n * where Go parses a list as a scalar: `token_acr.go`'s `Level()` is\n * `strconv.Atoi` over the whole string, so `Level(\"2 3\")` is 0 — the weakest\n * possible reading of the stronger demand, which inverts channel precedence in\n * exactly the third-party interop case the precedence rule exists for.\n *\n * ## The lexical contract\n *\n * `acr_values` is a space-delimited list of values the server will accept (OIDC\n * Core §3.1.2.1, RFC 9470 §3), and satisfying ANY listed value suffices. So the\n * bar to clear is the list's MINIMUM participating level — the weakest value\n * that satisfies the server, which is by definition what it is demanding.\n *\n * A token PARTICIPATES iff the whole token is decimal digits (`/^[0-9]+$/`).\n * `\"02\"` participates as 2, matching `strconv.Atoi`. Everything else does not:\n * `\"2gold\"`, `\"gold\"`, `\"2.0\"`, `\"-1\"`, `\"+2\"`, `\"1e2\"`, `\"urn:example:gold\"`,\n * the empty token. Two deliberate narrowings:\n *\n * - **Whole-token only.** `Number.parseInt(\"2gold\", 10)` reads 2, which would\n * contradict the rule that opaque values do not participate — and would let\n * a hostile or buggy channel smuggle a bar out of a value nobody meant as a\n * level.\n * - **Negatives excluded**, even though `strconv.Atoi` accepts them. A\n * negative assurance level has no meaning in the Passflow scale or in OIDC,\n * and admitting one would hand an attacker a bar below every real level.\n *\n * Every non-negative integer is in domain. Passflow's scale is 0..3, but a\n * third-party server may legally demand 4; the SDK never refuses a demand\n * client-side (§13.4) — it ranks factors for the highest bar it knows and lets\n * the server's verdict on the replay decide.\n *\n * ## \"unknown\" is not zero, and zero is not absent\n *\n * An opaque issuer-defined value (`urn:…`, `\"gold\"`) is legal OIDC and is NOT a\n * level at all: scoring it 0 would make it the weakest possible requirement, so\n * the other channel would always win and the anti-downgrade rule would itself\n * produce a downgrade. A literal `\"0\"`, by contrast, IS a participating level\n * that happens to demand nothing — OIDC defines `acr` 0 as authentication not\n * meeting even ISO/IEC 29115 level 1, so a server listing it is stating it\n * accepts unelevated authentication. Same net contribution (no constraint),\n * different diagnostics.\n */\n\n/** A token participates iff the WHOLE token is decimal digits. */\nconst PARTICIPATING_TOKEN = /^[0-9]+$/;\n\n/** What an `acr_values` list demands, plus what it discarded getting there. */\nexport type AcrReading = {\n /**\n * The bar this list demands: the minimum participating level, when that is a\n * real constraint. `undefined` means this list contributes no `acr`\n * constraint — it was absent, every token was non-participating, or the\n * minimum participating level was `0`.\n */\n level?: number;\n /** The raw list, verbatim, when one was present at all. */\n raw?: string;\n /** True when a list was present but no token participated (`\"urn:example:gold\"`). */\n unorderable: boolean;\n /** True when the minimum participating level was `0` — a demand of nothing. */\n zero: boolean;\n};\n\n/** The empty reading: no list, no constraint, nothing to report. */\nconst ABSENT: AcrReading = Object.freeze({ unorderable: false, zero: false });\n\n/**\n * Parses one `acr_values` token. Returns `undefined` for every value that does\n * not participate, which is the only distinction the comparator needs.\n */\nexport const parseAcrToken = (token: string): number | undefined =>\n PARTICIPATING_TOKEN.test(token) ? Number.parseInt(token, 10) : undefined;\n\n/**\n * Reduces an `acr_values` list to the bar it demands.\n *\n * The reducer is `min` over participating tokens; the cross-channel comparator\n * (`challenge.ts`) then takes the `max` over per-channel minima, which is the\n * monotonic lattice join and therefore can never resolve below a correctly\n * parsed channel.\n */\nexport const readAcrValues = (raw: string | undefined | null): AcrReading => {\n if (!raw) {\n return ABSENT;\n }\n\n const tokens = raw.split(/\\s+/).filter((token) => token !== '');\n if (tokens.length === 0) {\n return ABSENT;\n }\n\n const levels: number[] = [];\n for (const token of tokens) {\n const level = parseAcrToken(token);\n if (level !== undefined) {\n levels.push(level);\n }\n }\n\n if (levels.length === 0) {\n // A list of opaque values: unknown, not zero. It contributes no constraint\n // and must be reported, because only something that saw and discarded it\n // can say so.\n return { raw, unorderable: true, zero: false };\n }\n\n const bar = Math.min(...levels);\n // `acr_values=\"0\"` or `\"0 2\"`: the server accepts unelevated authentication,\n // so this list is identical in effect to an absent one. Reported rather than\n // left inert, so an accidental `\"0\"` is visible.\n return bar === 0 ? { raw, unorderable: false, zero: true } : { level: bar, raw, unorderable: false, zero: false };\n};\n\n/**\n * The level an `acr_values` list demands, or `undefined` for no constraint.\n *\n * The convenience form `select.ts` wants: it ranks factors and has no use for\n * the diagnostics.\n */\nexport const requiredAcrLevel = (raw: string | undefined | null): number | undefined => readAcrValues(raw).level;\n","/**\n * RFC 9470 step-up challenge parsing.\n *\n * A upgrade challenge is the server saying \"this session is valid, but not\n * strong enough\". It arrives in two shapes, both carrying the same\n * `WWW-Authenticate` vocabulary:\n *\n * - `401` from a protected resource (RFC 9470 §3), emitted by the\n * RequireAuthLevel / RequireFreshAuth middleware.\n * - `403` from `/auth/session/exchange`, which keeps its ADR-2 JSON body\n * (`{\"error\": \"upgrade_required\", ...}`) alongside the same header.\n *\n * Telling this apart from an ordinary `401` matters more than it looks: an\n * expired session and an insufficient one are the same status code with\n * opposite remedies. Clearing tokens on a upgrade challenge logs the user out\n * at the exact moment they were asked to prove *more*.\n */\n\nimport { type AcrReading, readAcrValues } from './acr';\n\n/** The error code RFC 9470 §3 defines for an insufficient authentication event. */\nexport const INSUFFICIENT_USER_AUTHENTICATION = 'insufficient_user_authentication';\n\n/** The ADR-2 error code `/auth/session/exchange` returns in its 403 body. */\nexport const UPGRADE_REQUIRED = 'upgrade_required';\n\n/**\n * What the server demands before it will serve the original request.\n *\n * Both fields are optional because the two middlewares challenge on different\n * axes: RequireAuthLevel names an `acrValues` and omits `maxAge`, while\n * RequireFreshAuth does the reverse. A challenge carrying neither is still\n * valid — it means \"re-prove a factor\" without further qualification.\n */\nexport type UpgradeChallenge = {\n /**\n * Required authentication context class (`acr`), e.g. `'2'`.\n *\n * After a two-channel merge this is the raw list of the channel that SET THE\n * BAR, with the header preferred on a numeric tie. A channel that contributed\n * no level never supplies it — showing `\"2gold\"` as the bar would\n * misrepresent what the server demanded.\n */\n acrValues?: string;\n /**\n * The parsed bar: `max` over each channel's `min` participating level\n * (§13.2.1a). `undefined` means unknown or unconstrained on this axis.\n *\n * Optional, and absent from the single-channel parsers' output, so\n * `parseChallengeHeader` / `parseChallengeBody` keep the exact shape they\n * ship with. `readUpgradeChallenge` always sets it when a level was parsed.\n */\n requiredLevel?: number;\n /** Maximum permitted age of the authentication event, in seconds. */\n maxAge?: number;\n /** The status that carried the challenge: 401 (protected resource) or 403 (session exchange). */\n status: number;\n /**\n * Every channel that PARSED AS A CHALLENGE in any field — not only the\n * channels that won a comparison. An opaque header contributes no constraint\n * yet must still be counted, because only something that saw and discarded it\n * can report it. `length === 2` means \"both channels carried a challenge\".\n *\n * Set by `readUpgradeChallenge`; absent from the single-channel parsers.\n */\n channels?: ChallengeChannel[];\n};\n\n/** Which wire channel a challenge was read from. */\nexport type ChallengeChannel = 'header' | 'body';\n\n/**\n * Matches one `auth-param` (RFC 9110 §11.2): a token, `=`, then either a\n * quoted-string or a bare token. Quoted values are captured separately from\n * bare ones so an embedded comma (legal inside quotes) does not split a value.\n */\nconst AUTH_PARAM = /([A-Za-z0-9!#$%&'*+\\-.^_`|~]+)\\s*=\\s*(?:\"((?:[^\"\\\\]|\\\\.)*)\"|([^\\s,]*))/g;\n\n/** Unescapes a quoted-string body per RFC 9110 §5.6.4 (`\\X` → `X`). */\nconst unescapeQuoted = (value: string): string => value.replace(/\\\\(.)/g, '$1');\n\n/**\n * Parses the auth-params out of a `WWW-Authenticate` value.\n *\n * Scans params across the whole header rather than isolating a single\n * challenge. Passflow only ever emits one `Bearer` challenge, and scanning\n * broadly means an unexpected additional scheme degrades to \"we still found\n * the Bearer params\" instead of failing shut.\n */\nconst parseAuthParams = (header: string): Record<string, string> => {\n const params: Record<string, string> = {};\n // `matchAll` needs the regex reset; AUTH_PARAM is module-level and stateful.\n AUTH_PARAM.lastIndex = 0;\n for (const match of header.matchAll(AUTH_PARAM)) {\n const name = match[1];\n if (!name) {\n continue;\n }\n const quoted = match[2];\n const bare = match[3];\n params[name.toLowerCase()] = quoted !== undefined ? unescapeQuoted(quoted) : (bare ?? '');\n }\n return params;\n};\n\n/**\n * `max_age` has three outcomes, not two, so it gets a three-valued return.\n *\n * - **absent** — the field was not sent. Silent; it contributes nothing and\n * there is nothing to report.\n * - **invalid** — `<= 0`, or non-numeric. It contributes nothing AND raises\n * `'zero-constraint-ignored'`, because a literal `0` cannot be both \"absent\"\n * and \"invalid\" while the parser is required to tell them apart.\n * - **valid** — a positive number of seconds.\n *\n * Treating `0` as invalid is a deliberate narrowing of RFC 9470 §3, which\n * permits `max_age=0` as a maximally strict freshness demand. Two reasons: this\n * parser already accepted only values `> 0` before §13, so preserving zero\n * would be a behaviour change on a shipped parser rather than a gap in it; and\n * under \"smaller positive wins\" a literal `0` from a buggy serializer or a\n * hostile body would force re-proof on EVERY request — an unauthenticated\n * denial-of-service against the user's own session. Recorded as a known\n * deviation so a later reader does not \"fix\" it.\n */\nexport type MaxAgeReading = { kind: 'absent' } | { kind: 'invalid'; raw: unknown } | { kind: 'valid'; value: number };\n\n/** Reads `max_age` into its three outcomes. See `MaxAgeReading`. */\nexport const parseMaxAge = (raw: unknown): MaxAgeReading => {\n if (raw === undefined || raw === null || raw === '') {\n return { kind: 'absent' };\n }\n const n = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);\n return Number.isFinite(n) && n > 0 ? { kind: 'valid', value: n } : { kind: 'invalid', raw };\n};\n\n/** The valid-only view, for the two single-channel parsers whose shape is shipped. */\nconst maxAgeValue = (raw: unknown): number | undefined => {\n const reading = parseMaxAge(raw);\n return reading.kind === 'valid' ? reading.value : undefined;\n};\n\n/**\n * Reads a upgrade challenge out of a `WWW-Authenticate` header, or returns\n * `undefined` when the header is absent or describes some other failure.\n *\n * The header must both use the `Bearer` scheme and carry\n * `error=\"insufficient_user_authentication\"`. A bare `WWW-Authenticate: Bearer`\n * (what an expired token produces) is deliberately NOT a upgrade challenge.\n */\nexport const parseChallengeHeader = (header: string | undefined | null, status: number): UpgradeChallenge | undefined => {\n if (!header || !/(^|[\\s,])Bearer\\b/i.test(header)) {\n return undefined;\n }\n\n const params = parseAuthParams(header);\n if (params.error !== INSUFFICIENT_USER_AUTHENTICATION) {\n return undefined;\n }\n\n return {\n acrValues: params.acr_values || undefined,\n maxAge: maxAgeValue(params.max_age),\n status,\n };\n};\n\n/**\n * Reads a upgrade challenge out of the ADR-2 `403` body. Used as a fallback\n * for `/auth/session/exchange`, whose JSON body is the documented SDK contract\n * even though it also sets the standard header.\n */\nexport const parseChallengeBody = (body: unknown, status: number): UpgradeChallenge | undefined => {\n if (!body || typeof body !== 'object') {\n return undefined;\n }\n\n const data = body as Record<string, unknown>;\n if (data.error !== UPGRADE_REQUIRED) {\n return undefined;\n }\n\n return {\n acrValues: typeof data.acr_values === 'string' && data.acr_values ? data.acr_values : undefined,\n maxAge: maxAgeValue(data.max_age),\n status,\n };\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Both channels, merged (§13.2)\n *\n * The shipped parser chained header → body and returned the first hit: safe by\n * accident, not by design. Accepting two channels for a security-relevant value\n * creates a DOWNGRADE surface — from Passflow the two cannot disagree, both\n * being rendered from one `model.PolicyResult`, but a third-party resource\n * server, a header-rewriting intermediary or a CDN can make them disagree — so\n * first-match is withdrawn and replaced by STRONGEST-WINS:\n *\n * - a channel's bar is the MINIMUM participating level of its `acr_values`\n * list (`acr.ts`), and the winner is the LARGER bar: `max` over per-channel\n * `min`, which is the monotonic lattice join;\n * - `max_age` merges independently, smaller positive winning;\n * - absence and invalidity are the identity element: a value that contributes\n * nothing leaves the other channel's value standing, and a contributing\n * value can only tighten.\n *\n * Per-attribute composition is intended: the strongest `acr` from one channel\n * plus the smallest positive `max_age` from the other yields a requirement\n * neither channel stated in full, which is a hybrid stricter than either.\n * Tightening is the safe direction for a client-side hint whose enforcement is\n * server-side. The normal case is exactly this — `RequireAuthLevel` emits\n * `acr_values` and omits `max_age`, `RequireFreshAuth` the reverse.\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** The bar a challenge demanded, or an elevation reached, on both axes. */\nexport type AssuranceBar = {\n /** The raw winning `acr_values` list, for display and diagnostics. */\n acrValues?: string;\n /** The parsed bar. `undefined` = unknown, or not constrained on this axis. */\n level?: number;\n /** The freshness constraint, when that is what was demanded. */\n maxAge?: number;\n};\n\n/** The reasons the SDK can decline to act on a challenge, or act on a suspect one. */\nexport type UpgradeDiagnosticReason =\n | 'no-channel'\n | 'header-required-but-absent'\n | 'channels-disagree'\n | 'channels-unorderable'\n | 'zero-constraint-ignored'\n | 'challenge-out-of-scope'\n | 'elevated-insufficient'\n | 'replay-unsupported';\n\n/**\n * One diagnostic record.\n *\n * A diagnostic is the only observable signal of the response-rewriting and the\n * misconfiguration this design warns about, so it routes through a callback\n * whose DEFAULT sink is `console.warn` — never `debug`, which is invisible in\n * every default configuration. A deployment where step-up silently never fires\n * must be distinguishable from one where it was never required.\n */\nexport type UpgradeDiagnostic = {\n status: number;\n /**\n * The request's URL. The parser does not know it — it is handed a status, a\n * header reader and a body — so it emits `''` and the core, which owns the\n * request context, fills it in before forwarding to the sink.\n */\n url: string;\n reason: UpgradeDiagnosticReason;\n detail?: {\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n /** Raw channel inputs, including values discarded as unorderable. */\n header?: string;\n body?: string;\n /** Which field was discarded, for `'zero-constraint-ignored'`. */\n field?: 'acr_values' | 'max_age';\n /** Which operation, for per-waiter reasons. */\n method?: string;\n /** A human-readable sentence naming the fix. The default sink prints it. */\n message?: string;\n };\n};\n\n/** Where diagnostics go. `console.warn` is the default sink, not the only one. */\nexport type UpgradeDiagnosticSink = (diagnostic: UpgradeDiagnostic) => void;\n\n/** What `readUpgradeChallenge` is given. The CALLER decides whether reading the body is in scope. */\nexport type ChallengeInput = {\n status: number;\n getHeader: (name: string) => string | undefined | null;\n /** Pre-read. Omit it — do not pass `undefined` lazily — when the body must not be read. */\n body?: unknown;\n};\n\nexport type ChallengeReadOptions = {\n /**\n * `'header'` takes the challenge from the header ONLY: the body is not read\n * for challenge purposes at all, and a 401/403 with no parseable header is a\n * non-challenge. For an integrator whose compliance rule names RFC 9470, or\n * who does not consider a third-party server's body authoritative. Two\n * intended consequences: a strictly stronger body is ignored, and since no\n * body is resolved the disagreement diagnostic never fires.\n *\n * There is deliberately no `'body'` value. Excluding a third-party API's\n * standard header is what `shouldIntercept` is for, and a `'body'` setting\n * would mandate a non-standard channel, which §13.1a forbids.\n */\n requireChallengeChannel?: 'header';\n};\n\n/**\n * What a parse found, and what it had to discard finding it.\n *\n * A pure parser returning its findings beats a sink parameter: it is\n * side-effect-free and directly testable against a fixture corpus, and\n * `'channels-disagree'`, `'zero-constraint-ignored'` and\n * `'channels-unorderable'` are knowable ONLY inside the parse — the shipped\n * parser discarded them before returning, which is why they could not be\n * surfaced at all.\n */\nexport type ChallengeReading = {\n challenge?: UpgradeChallenge;\n /** `[]` when there is nothing to report. Never `undefined`. */\n diagnostics: UpgradeDiagnostic[];\n};\n\n/** Reads one channel's fields, keeping the raw values for the diagnostics. */\ntype ChannelReading = {\n channel: ChallengeChannel;\n acr: AcrReading;\n maxAge: MaxAgeReading;\n /** The raw `acr_values` list as it arrived, for `detail.header` / `detail.body`. */\n raw?: string;\n};\n\nconst readChannel = (channel: ChallengeChannel, challenge: UpgradeChallenge, rawMaxAge: unknown): ChannelReading => ({\n channel,\n acr: readAcrValues(challenge.acrValues),\n maxAge: parseMaxAge(rawMaxAge),\n raw: challenge.acrValues,\n});\n\n/** Picks the `acr_values` auth-param out of a header, without re-parsing semantics. */\nconst rawHeaderParams = (header: string | undefined | null): Record<string, string> => (header ? parseAuthParams(header) : {});\n\n/**\n * Reads a step-up challenge from both channels at once, merging them field-wise\n * and reporting everything it discarded.\n *\n * The additive sibling of `parseUpgradeChallenge`, which keeps its shipped\n * three-parameter signature and delegates here. It keeps the `getHeader`\n * callback form — the reason the function works from both the fetch surface and\n * the axios surface, where `AxiosError.response` hands back a plain object\n * rather than a DOM `Headers`.\n */\nexport const readUpgradeChallenge = (input: ChallengeInput, options?: ChallengeReadOptions): ChallengeReading => {\n const { status } = input;\n if (status !== 401 && status !== 403) {\n return { diagnostics: [] };\n }\n\n const headerOnly = options?.requireChallengeChannel === 'header';\n const rawHeader = input.getHeader('www-authenticate');\n const headerChallenge = parseChallengeHeader(rawHeader, status);\n // Under `'header'` the body is not read for challenge purposes AT ALL — not\n // read-then-ignored. The caller is expected not to have read it either.\n const bodyChallenge = headerOnly ? undefined : parseChallengeBody(input.body, status);\n\n const diagnostics: UpgradeDiagnostic[] = [];\n const report = (reason: UpgradeDiagnosticReason, detail?: UpgradeDiagnostic['detail']): void => {\n diagnostics.push({ status, url: '', reason, ...(detail ? { detail } : {}) });\n };\n\n if (!headerChallenge && !bodyChallenge) {\n if (headerOnly) {\n // The flag asserts `Access-Control-Expose-Headers: WWW-Authenticate` is in\n // place. When it is not, this is what says so — warn, never block.\n report('header-required-but-absent', rawHeader ? { header: rawHeader } : undefined);\n }\n return { diagnostics };\n }\n\n const channels: ChannelReading[] = [];\n if (headerChallenge) {\n channels.push(readChannel('header', headerChallenge, rawHeaderParams(rawHeader).max_age));\n }\n if (bodyChallenge) {\n const body = input.body as Record<string, unknown> | undefined;\n channels.push(readChannel('body', bodyChallenge, body?.max_age));\n }\n\n const rawOf = (channel: ChallengeChannel): 'header' | 'body' => channel;\n\n for (const reading of channels) {\n if (reading.acr.unorderable) {\n // An opaque issuer-defined ACR is legal OIDC and contributes nothing. The\n // discarded value travels in the detail, which is what that field is for.\n report('channels-unorderable', { [rawOf(reading.channel)]: reading.raw });\n }\n if (reading.acr.zero) {\n report('zero-constraint-ignored', { field: 'acr_values', [rawOf(reading.channel)]: reading.raw });\n }\n if (reading.maxAge.kind === 'invalid') {\n report('zero-constraint-ignored', { field: 'max_age', [rawOf(reading.channel)]: String(reading.maxAge.raw) });\n }\n }\n\n // ── the `acr` axis: max over per-channel min, header preferred on a tie ──\n const contributing = channels.filter((reading) => reading.acr.level !== undefined);\n let requiredLevel: number | undefined;\n let acrValues: string | undefined;\n for (const reading of contributing) {\n const level = reading.acr.level as number;\n if (requiredLevel === undefined || level > requiredLevel) {\n requiredLevel = level;\n acrValues = reading.raw;\n }\n // On a numeric tie the HEADER's raw string is preserved: it is the standards\n // channel and the one a server operator can be held to. The parsed level is\n // identical either way, so the choice is only about which string a developer\n // sees in a diagnostic.\n else if (level === requiredLevel && reading.channel === 'header') {\n acrValues = reading.raw;\n }\n }\n\n // ── the freshness axis: smaller positive wins, independently of `acr` ──\n let maxAge: number | undefined;\n for (const reading of channels) {\n if (reading.maxAge.kind === 'valid' && (maxAge === undefined || reading.maxAge.value < maxAge)) {\n maxAge = reading.maxAge.value;\n }\n }\n\n // A disagreement is compared FIELD-WISE, so two channels constraining\n // different axes — the normal case — is never reported as one.\n if (channels.length === 2) {\n const [first, second] = channels as [ChannelReading, ChannelReading];\n const acrDisagrees =\n first.acr.level !== undefined && second.acr.level !== undefined && first.acr.level !== second.acr.level;\n const freshnessDisagrees =\n first.maxAge.kind === 'valid' && second.maxAge.kind === 'valid' && first.maxAge.value !== second.maxAge.value;\n if (acrDisagrees || freshnessDisagrees) {\n report('channels-disagree', { header: first.raw ?? second.raw, body: second.raw ?? first.raw });\n }\n }\n\n return {\n challenge: {\n acrValues,\n requiredLevel,\n maxAge,\n status,\n channels: channels.map((reading) => reading.channel),\n },\n diagnostics,\n };\n};\n\n/**\n * Resolves a upgrade challenge from a response, from either channel.\n *\n * The SHIPPED export: same name, same three parameters, same return, published\n * in `@passflow/core@0.14.1`. It is now a thin delegate over\n * `readUpgradeChallenge`, so there is one merge rule rather than two parsers\n * that can drift — but it discards the diagnostics, which is why a caller that\n * needs them (the core) calls the sibling directly.\n *\n * Returns `undefined` for every response that is not a upgrade challenge —\n * including ordinary `401`s, which callers must keep treating as \"session\n * expired\".\n */\nexport const parseUpgradeChallenge = (\n status: number,\n getHeader: (name: string) => string | undefined | null,\n body?: unknown,\n): UpgradeChallenge | undefined => readUpgradeChallenge({ status, getHeader, body }).challenge;\n","/**\n * Per-request markers for the upgrade transaction.\n *\n * Two 401s look identical on the wire and mean opposite things. A protected\n * resource's 401 says \"this session is not strong enough\"; the elevation's own\n * `POST /auth/refresh` 401 says \"that TOTP code was wrong, try again\". Neither\n * carries RFC 9470 vocabulary on the commit path — `refreshErrorStatus` maps the\n * four recoverable upgrade errors to a bare 401 with no `WWW-Authenticate` and no\n * `upgrade_required` body — so the response alone cannot tell them apart from an\n * expired session.\n *\n * The evidence that CAN tell them apart is the request: we know we issued it.\n * Both rounds of the elevation (`POST /auth/upgrade/start` and the committing\n * `POST /auth/refresh`) carry a marker on their axios config, set at the call\n * site immediately before dispatch. The response interceptor reads it off\n * `error.config`, which axios hands back unchanged, and suppresses\n * `setSessionInvalid()` for that one request only. An unrelated background 401\n * arriving while a prompt is open is unmarked and invalidates exactly as before.\n *\n * A second marker records that a request has already been replayed after an\n * elevation, so an adapter that re-issues through the same interceptor cannot\n * elevate twice for one challenge.\n *\n * ## Why these are enumerable, against the design's \"non-enumerable\"\n *\n * The design asks for non-enumerable properties, reasoning that they must never\n * reach the wire. Non-enumerable does not survive the trip: `AxiosClient.send`\n * spreads the caller's config into a fresh object (`{ method, url, ...options }`)\n * and axios' own `mergeConfig` iterates `Object.keys`, so both drop a\n * non-enumerable property before the request is dispatched — verified against\n * `axios@1.13` with a stub adapter. An enumerable key reaches both the adapter\n * and `error.config`.\n *\n * The property the design actually wanted is preserved regardless: axios\n * serializes only `data` (and `params`/`headers`), never unknown config keys, so\n * a marker is never transmitted. The same mechanism already carries\n * `_retryCount` through the rate-limit retry path.\n */\n\n/** Config key marking a request as one of the upgrade transaction's two rounds. */\nexport const UPGRADE_TRANSACTION_MARKER = '__passflowUpgradeTransaction';\n\n/** Config key marking a request as the single post-elevation replay (FR3). */\nexport const UPGRADE_REPLAYED_MARKER = '__passflowUpgradeReplayed';\n\n/** The markers as they appear on a request config. Both are absent unless set. */\nexport type UpgradeRequestMarkers = {\n /** Set by the upgrade transport on `prepare` and `commit`. */\n __passflowUpgradeTransaction?: true;\n /** Set by the replaying adapter before it re-issues the original request. */\n __passflowUpgradeReplayed?: true;\n};\n\nconst readMarker = (config: unknown, key: string): boolean =>\n typeof config === 'object' && config !== null && (config as Record<string, unknown>)[key] === true;\n\n/**\n * Returns a copy of `config` marked as part of the upgrade transaction.\n *\n * Copies rather than mutates: the caller may be reusing a config object, and a\n * marker that outlives its request would suppress invalidation for an unrelated\n * 401 — the exact over-broad behaviour the per-request rule replaces.\n */\nexport const markUpgradeTransaction = <T extends object>(config?: T): T & UpgradeRequestMarkers => ({\n ...((config ?? {}) as T),\n [UPGRADE_TRANSACTION_MARKER]: true,\n});\n\n/** Reports whether a request config carries the upgrade-transaction marker. */\nexport const hasUpgradeTransactionMarker = (config: unknown): boolean => readMarker(config, UPGRADE_TRANSACTION_MARKER);\n\n/** Returns a copy of `config` marked as the one permitted post-elevation replay. */\nexport const markUpgradeReplayed = <T extends object>(config: T): T & UpgradeRequestMarkers => ({\n ...config,\n [UPGRADE_REPLAYED_MARKER]: true,\n});\n\n/** Reports whether a request config has already been replayed after an elevation. */\nexport const hasUpgradeReplayedMarker = (config: unknown): boolean => readMarker(config, UPGRADE_REPLAYED_MARKER);\n","import type {\n AuthenticationResponseJSON,\n PublicKeyCredentialCreationOptionsJSON,\n PublicKeyCredentialRequestOptionsJSON,\n RegistrationResponseJSON,\n} from '@simplewebauthn/types';\nimport type { AxiosRequestConfig } from 'axios';\n\nimport type { PlatformAdapter } from '../platform';\nimport type { Tokens } from '../types';\nimport type { UpgradeAssertion, UpgradeFactor } from '../upgrade/factors';\n\nexport type RequestOptions<D> = {\n data?: D;\n config?: AxiosRequestConfig;\n};\n\nexport enum RequestMethod {\n GET = 'get',\n POST = 'post',\n PUT = 'put',\n PATCH = 'patch',\n DELETE = 'delete',\n}\n\nexport enum PassflowEndpointPaths {\n signin = '/auth/login',\n signup = '/auth/register',\n pkceExchange = '/auth/pkce_exchange',\n signInWithProvider = '/auth/federated/start/',\n passwordless = '/auth/passwordless/start',\n passwordlessComplete = '/auth/passwordless/complete',\n logout = '/user/logout',\n refresh = '/auth/refresh',\n validateSession = '/user/me',\n sendPasswordResetEmail = '/auth/password/reset',\n resetPassword = '/auth/password/change',\n appSettings = '/app/settings',\n passkeyRegisterStart = '/auth/passkey/register/start',\n passkeyRegisterComplete = '/auth/passkey/register/complete',\n passkeyAuthenticateStart = '/auth/passkey/authenticate/start',\n passkeyAuthenticateComplete = '/auth/passkey/authenticate/complete',\n passkeyValidate = '/auth/validate',\n settingsAll = '/settings',\n settingsPasswordPolicy = '/settings/password',\n settingsPasskey = '/settings/passkey',\n userPasskey = '/user/passkey',\n addUserPasskey = `${PassflowEndpointPaths.userPasskey}/add/start`,\n completeAddUserPasskey = `${PassflowEndpointPaths.userPasskey}/add/complete`,\n joinInvitation = '/user/tenant/join',\n tenantPath = '/user/tenant',\n invitationsPath = '/user/tenant/:tenantID/invitations',\n requestInvitation = '/user/invite',\n invitationDelete = '/user/invite/:invitationID',\n invitationResend = '/user/invite/:invitationID/resend',\n invitationGetLink = '/user/invite/:invitationID/link',\n twoFactor = '/user/2fa',\n twoFactorStatus = '/user/2fa/status',\n twoFactorSetupBegin = '/user/2fa/setup/begin',\n twoFactorSetupConfirm = '/user/2fa/setup/confirm',\n twoFactorVerify = '/auth/2fa/verify',\n twoFactorRecovery = '/auth/2fa/recovery',\n twoFactorRegenerateCodes = '/user/2fa/recovery-codes/regenerate',\n twoFactorSetupMagicLink = '/auth/2fa-setup', // :token param appended in API call\n // v2 2FA endpoints\n TwoFactorMethodsAvailable = '/v2/user/2fa/methods/available',\n TwoFactorMethodsRegistered = '/v2/user/2fa/methods',\n TwoFactorSetupStart = '/v2/user/2fa/setup/start',\n TwoFactorSetupComplete = '/v2/user/2fa/setup/complete',\n TwoFactorMethodRemove = '/v2/user/2fa/methods/:id',\n TwoFactorChallenge = '/v2/auth/2fa/challenge',\n TwoFactorVerifyV2 = '/v2/auth/2fa/verify',\n TwoFactorAlternative = '/v2/auth/2fa/alternative',\n TwoFactorTrustedDevices = '/v2/user/2fa/trusted-devices',\n TwoFactorTrustedDeviceRevoke = '/v2/user/2fa/trusted-devices/:id',\n // CLI Auth endpoints\n cliAuthStatus = '/cli/auth/status/:sessionId',\n cliAuthComplete = '/cli/auth/complete',\n // Assurance upgrade (step-up) endpoints. The commit round reuses `refresh`.\n upgradeStart = '/auth/upgrade/start',\n}\n\nexport enum PassflowAdminEndpointPaths {\n passkeyRegisterStart = '/admin/auth/passkey/register/start',\n passkeyRegisterComplete = '/admin/auth/passkey/register/complete',\n passkeyAuthenticateStart = '/admin/auth/passkey/authenticate/start',\n passkeyAuthenticateComplete = '/admin/auth/passkey/authenticate/complete',\n passkeyValidate = '/admin/auth/validate',\n logout = '/admin/auth/logout',\n}\n\n/**\n * BFF (Backend-for-Frontend) configuration for secure token storage.\n * When enabled, tokens are sent to the BFF server which stores them in httpOnly cookies.\n */\nexport type TokenExchangeConfig = {\n /**\n * Enable token exchange mode. When true, authorization code is sent to your server\n * for token exchange instead of being exchanged directly from the browser.\n */\n enabled: boolean;\n /**\n * URL to send authorization code for server-side token exchange.\n * Your server exchanges the code for tokens and stores them in httpOnly cookies.\n * @example '/api/auth/callback'\n */\n callbackUrl: string;\n /**\n * URL to call for token refresh. BFF reads refresh_token from httpOnly cookie\n * and returns new tokens (also stored in cookies).\n * @example '/api/auth/refresh'\n */\n refreshUrl?: string;\n /**\n * URL to call for logout. BFF clears httpOnly cookies.\n * @example '/api/auth/logout'\n */\n logoutUrl?: string;\n /**\n * URL to check authentication status (whether httpOnly cookies are valid).\n * @example '/api/auth/status'\n */\n statusUrl?: string;\n};\n\n/** @deprecated Use TokenExchangeConfig instead */\nexport type BFFConfig = TokenExchangeConfig;\n\nexport type PassflowConfig = {\n url?: string;\n appId?: string;\n scopes?: string[];\n createTenantForNewUser?: boolean;\n parseQueryParams?: boolean;\n keyStoragePrefix?: string;\n /**\n * Token exchange configuration for secure server-side token handling.\n * When enabled, authorization code is sent to your server which exchanges it\n * for tokens and stores them in httpOnly cookies. Tokens never touch the browser.\n */\n tokenExchange?: TokenExchangeConfig;\n /**\n * Platform adapter for cross-platform compatibility.\n * Defaults to WebAdapter (browser APIs) if not provided.\n * Pass a custom adapter to support React Native or other platforms.\n */\n platform?: PlatformAdapter;\n /**\n * Assurance upgrade (step-up) configuration. Construction-time only: the\n * prompt handler is deliberately NOT here, because it is a lifetime-scoped\n * resource that has to be torn down, which configuration cannot express.\n */\n upgrade?: PassflowUpgradeConfig;\n};\n\nexport type PassflowUpgradeConfig = {\n /** Relying-party id forwarded verbatim to `POST /auth/upgrade/start` for passkey upgrades. */\n relyingPartyId?: string;\n /** Overrides the factor set offered during an upgrade. Defaults to what this SDK can complete. */\n factors?: readonly UpgradeFactor[];\n};\n\n/**\n * Passflow's own API, as an interception scope — \"domain 1\".\n *\n * A challenge arriving from here is handled with zero configuration, because\n * `url` is an input the SDK already takes and whatever it points at is\n * unambiguously ours.\n */\nexport type UpgradeDomain = {\n /** The API origin, e.g. `https://api.passflow.dev`. */\n origin: string;\n /** The path prefix when `url` carries one, normalised without a trailing slash. */\n pathPrefix?: string;\n};\n\n/**\n * Resolves domain 1 from `PassflowConfig.url`.\n *\n * `url` is OPTIONAL, and the absent case is specified rather than guessed: the\n * domain is EMPTY (`null`), and nothing throws. Throwing would break apps that\n * never enabled step-up, and substituting a default origin — `PASSFLOW_CLOUD_URL`,\n * the document origin, the origin the first 401 came from — would be the SDK\n * inferring a trust boundary it has no information about. An empty domain is not\n * silent: the first parseable challenge on an attached surface must raise a\n * diagnostic naming `url` as the fix.\n *\n * Note this deliberately does NOT mirror `AxiosClient`'s `url || PASSFLOW_CLOUD_URL`\n * fallback. That fallback decides where to send our own traffic; this decides\n * whose 401s may drive a security prompt, and the two questions do not share an\n * answer.\n */\nexport const resolveUpgradeDomain = (url?: string): UpgradeDomain | null => {\n if (!url) {\n return null;\n }\n try {\n const parsed = new URL(url);\n const prefix = parsed.pathname.replace(/\\/+$/, '');\n return { origin: parsed.origin, pathPrefix: prefix === '' ? undefined : prefix };\n } catch {\n // A `url` the platform's own URL parser rejects cannot be matched against a\n // response URL either. Same treatment as absent: empty, and never a throw.\n return null;\n }\n};\n\n/** The raw `POST /auth/upgrade/start` body, snake_case exactly as the server sends it. */\nexport type UpgradeStartWire = {\n challenge_id: string;\n method: string;\n /** RFC 3339. The server enforces the TTL; the SDK carries this for display only. */\n expires_at: string;\n /** WebAuthn assertion options, present only for a passkey upgrade. */\n webauthn?: PublicKeyCredentialRequestOptionsJSON;\n /** Masked delivery target (`j***@x.com`), present only for email / SMS OTP. */\n code_sent_to?: string;\n};\n\n/**\n * The `upgrade` field of `POST /auth/refresh` — the commit round.\n *\n * The server types `assertion` as `json.RawMessage`, so a new factor can extend\n * the payload without a wire-format change.\n */\nexport type UpgradeRefreshField = {\n type: string;\n assertion: UpgradeAssertion;\n};\n\nexport type PassflowAuthorizationResponse = Tokens & {\n /**\n * The authorization code a PKCE app answers with in place of tokens. The SDK\n * redeems it, so a response handed back to a caller never carries one.\n */\n code?: string;\n /** Where the server sends the user next; a password reset answers with its link's redirect. */\n redirect_url?: string;\n requires_2fa?: boolean;\n challenge_id?: string;\n tfa_token?: string;\n token_delivery?: 'json_body' | 'cookie' | 'mobile';\n cookies?: string[];\n csrf_token?: string;\n};\n\nexport type PassflowValidationResponse = Tokens & {\n redirect_url: string;\n requires_2fa?: boolean;\n challenge_id?: string;\n tfa_token?: string;\n};\n\nexport type PassflowSuccessResponse = {\n result: 'ok';\n};\n\nexport type PassflowLogoutResponse = {\n status: 'ok';\n};\n\nexport interface PassflowSessionValidationResponse {\n valid: boolean;\n user?: {\n id: string;\n email?: string;\n username?: string;\n [key: string]: unknown;\n };\n expires_at?: number;\n}\n\nexport type PassflowResponseError = {\n error: {\n id: string;\n message: string;\n status: number;\n location: string;\n time: string;\n };\n};\n\nexport class PassflowError extends Error {\n id: string;\n message: string;\n status: number;\n location: string;\n time: string;\n\n constructor(error: PassflowResponseError['error']) {\n super();\n this.id = error?.id ?? 'unknown';\n this.message = error?.message ?? error ?? 'Something went wrong';\n this.status = error?.status ?? 500;\n this.location = error?.location ?? 'unknown';\n this.time = error?.time ?? new Date().toISOString();\n }\n}\n\nexport type PassflowSignInPayload = {\n password: string;\n scopes?: string[];\n invite_token?: string;\n code_challenge?: string;\n} & (\n | { email: string; phone?: never; username?: never }\n | { phone: string; email?: never; username?: never }\n | { username: string; email?: never; phone?: never }\n);\n\nexport type PassflowSignInExtendedPayload = PassflowSignInPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowAddressPayload = {\n formatted?: string;\n street_address?: string;\n locality?: string;\n region?: string;\n postal_code?: string;\n country?: string;\n};\n\nexport type PassflowUserPayload = {\n password: string;\n username?: string;\n email?: string;\n given_name?: string;\n family_name?: string;\n middle_name?: string;\n nickname?: string;\n preferred_username?: string;\n phone_number?: string;\n profile?: string;\n picture?: string;\n website?: string;\n gender?: string;\n birthday?: Date;\n timezone?: string;\n locale?: string;\n addresses?: PassflowAddressPayload;\n} & ({ email: string } | { phone_number: string });\n\nexport type PassflowSignUpPayload = {\n user: PassflowUserPayload;\n scopes?: string[];\n create_tenant?: boolean;\n anonymous?: boolean;\n invite_token?: string;\n code_challenge?: string;\n};\n\nexport type PassflowPasswordlessSignInPayload = {\n challenge_type: InternalStrategyChallenge;\n redirect_url: string;\n scopes?: string[];\n create_tenant?: boolean;\n invite_token?: string;\n} & ({ email: string; phone?: never } | { phone: string; email?: never });\n\nexport type PassflowPasswordlessResponse = {\n challenge_id: string;\n expires_at: Date | string;\n};\n\nexport type PassflowPasswordlessSignInExtendedPayload = PassflowPasswordlessSignInPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowPasswordlessSignInCompletePayload = {\n challenge_id: string;\n otp: string;\n device?: string;\n scopes?: string[];\n challenge_type?: InternalStrategyChallenge;\n};\n\nexport enum Providers {\n google = 'google',\n facebook = 'facebook',\n}\n\nexport type FimStrategy = {\n fim_type: Providers;\n};\n\nexport type InternalStrategyIdentity = 'id' | 'email' | 'phone' | 'username' | 'anonymous' | 'none';\nexport type InternalStrategyChallenge = 'password' | 'otp' | 'magic_link' | 'recovery_codes' | 'guardian' | 'none';\nexport type InternalStrategyTransport = 'email' | 'sms' | 'push' | 'socket' | 'authenticator' | 'none';\n\nexport type InternalStrategy = {\n identity: InternalStrategyIdentity;\n challenge: InternalStrategyChallenge;\n transport: InternalStrategyTransport;\n};\n\nexport type OtherStrategy = Record<string, never>;\n\nexport type AuthTypeStrategy = 'internal' | 'passkey' | 'webauthn' | 'fim' | 'pkce' | 'anonymous';\n\nexport type AuthStrategies =\n | { type: Extract<AuthTypeStrategy, 'internal'>; strategy: InternalStrategy }\n | { type: Extract<AuthTypeStrategy, 'fim'>; strategy: FimStrategy }\n | {\n type: Exclude<AuthTypeStrategy, 'internal' | 'fim'>;\n strategy: OtherStrategy;\n };\n\nexport type AppType = 'web' | 'spa' | 'bff' | 'android' | 'ios' | 'desktop' | 'm2m' | 'other';\n\nexport type TokenDeliveryMethod = 'json_body' | 'cookie' | '';\n\nexport type AppSettings = {\n id: string;\n secret: string;\n active: boolean;\n name: string;\n description: string;\n offline: boolean;\n type: AppType;\n redirect_urls: string[] | null;\n origins: string[] | null;\n custom_email_templates: boolean;\n auth_strategies: AuthStrategies[];\n force_passwordless_login: boolean;\n pkce_enabled: boolean;\n custom_sms_messages: boolean;\n registration_allowed: boolean;\n invite_only_registration: boolean;\n passwordless_registration_allowed: boolean;\n anonymous_registration_allowed: boolean;\n create_tenant_on_registration: 'never' | 'always' | 'optional';\n fim_merge_by_email_allowed: boolean;\n debug_otp_code_allowed: boolean;\n debug_otp_code_for_registration: string;\n defaults: DefaultAppSettings;\n login_app_theme: LoginWebAppTheme;\n login_app_settings?: unknown;\n token_delivery_method?: TokenDeliveryMethod;\n};\n\nexport type DefaultAppSettings = {\n app_id: string;\n redirect: string;\n scopes: string[];\n create_tenant_for_new_user: boolean;\n};\n\nexport enum OS {\n web = 'web',\n ios = 'ios',\n android = 'android',\n}\n\n/**\n * Maps platform.getDeviceType() return values to the OS enum.\n * The OS enum values match the getDeviceType() strings exactly,\n * so this is a safe cast.\n */\nexport function getOSFromDeviceType(deviceType: 'web' | 'ios' | 'android'): OS {\n // OS enum values happen to match getDeviceType() string values exactly\n return deviceType as OS;\n}\n\nexport type PassflowPasskeyRegisterStartPayload = {\n passkey_display_name?: string;\n passkey_username?: string;\n invite_token?: string;\n\n scopes: string[];\n create_tenant?: boolean;\n\n relying_party_id: string;\n redirect_url: string;\n code_challenge?: string;\n};\n\nexport type PassflowPasskeyRegisterStartExtendedPayload = PassflowPasskeyRegisterStartPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowPasskeyStart = {\n challenge_id: string;\n publicKey: PublicKeyCredentialCreationOptionsJSON;\n};\n\nexport type PassflowPasskeyCompleteMessageWithTokens = Tokens;\n\nexport type PassflowPasskeyPayload = {\n device: string;\n challenge_id: string;\n};\n\nexport type PassflowPasskeyRegisterPayload = PassflowPasskeyPayload & {\n passkey_data: RegistrationResponseJSON;\n};\n\nexport type PassflowPasskeyAuthenticatePayload = PassflowPasskeyPayload & {\n passkey_data: AuthenticationResponseJSON;\n};\n\nexport type PassflowPasskeyAuthenticateStartPayload = {\n relying_party_id: string;\n scopes?: string[];\n user_id?: string;\n invite_token?: string;\n code_challenge?: string;\n};\n\nexport type PassflowPasskeyAuthenticateStartExtendedPayload = PassflowPasskeyAuthenticateStartPayload & {\n device: string;\n os: OS;\n};\n\nexport type PassflowFederatedAuthPayload = {\n provider: Providers;\n redirect_url: string;\n scopes?: string[];\n invite_token?: string;\n create_tenant?: boolean;\n};\n\nexport type PassflowFederatedAuthExtendedPayload = PassflowFederatedAuthPayload & {\n device?: string;\n};\n\nexport type PassflowValidatePayload = {\n otp: string;\n device: string;\n challenge_id: string;\n};\n\n// SETTINGS\nexport type PassflowPasskeyProviderOption = 'none' | 'required' | 'preferred' | 'discouraged';\n\nexport type PassflowSettingsAll = {\n password_policy: PassflowPasswordPolicySettings;\n passkey_provider: PassflowPasskeySettings;\n};\n\nexport type PassflowPasswordPolicySettings = {\n restrict_min_password_length: boolean;\n min_password_length: number;\n reject_compromised: boolean;\n enforce_password_strength: 'none' | 'weak' | 'average' | 'strong';\n require_lowercase: boolean;\n require_uppercase: boolean;\n require_number: boolean;\n require_symbol: boolean;\n};\n\nexport type PassflowPasskeySettings = {\n // name: string;\n id: string;\n display_name: string;\n // id_field: 'email' | 'phone' | 'username';\n // validation: InternalStrategyChallenge;\n registration?: {\n user_verification: PassflowPasskeyProviderOption;\n authenticator_attachment: 'platform' | 'cross-platform' | 'any';\n discoverable_key: PassflowPasskeyProviderOption;\n attestation_metadata: PassflowPasskeyProviderOption;\n extensions: unknown;\n };\n authentication?: {\n user_verification: PassflowPasskeyProviderOption;\n attestation_metadata: PassflowPasskeyProviderOption;\n extensions: unknown;\n };\n};\n\ntype PassflowCredentialFlags = {\n user_present: boolean;\n user_verified: boolean;\n backup_eligible: boolean;\n backup_state: boolean;\n};\n\ntype PassflowEnrolmentAuthenticator = {\n aaguid: string;\n sign_count: number;\n clone_warning: boolean;\n attachment: 'platform' | 'cross-platform';\n};\n\nexport type PassflowUserPasskey = {\n id: string;\n user_id: string;\n name: string;\n strategy: InternalStrategy;\n challenge_type: InternalStrategyChallenge;\n strategy_hash: string;\n enrolled_at: Date | string;\n enrollment_challenge_id: string;\n confirmed_at: Date | string;\n last_auth_at: Date | string;\n public_key: string;\n attestation_type: string;\n transport: string[];\n flags: PassflowCredentialFlags;\n authenticator: PassflowEnrolmentAuthenticator;\n archived: boolean;\n archived_at: Date | string;\n count?: number;\n enrolled_with_app_id?: string;\n};\n\nexport type PassflowSendPasswordResetEmailPayload = {\n reset_page_url?: string;\n redirect_url?: string;\n} & (\n | { email: string; phone?: never; username?: never }\n | { phone: string; email?: never; username?: never }\n | { username: string; email?: never; phone?: never }\n);\n\nexport type PassflowInviteResponse = {\n link: string;\n};\n\nexport type PassflowInvitePayload = {\n invite_token: string;\n scopes: string[];\n};\n\nexport type PassflowUserWithRoles = {\n user_id: string;\n username: string;\n email: string;\n phone_number: string;\n tenant_id: string;\n group_id: string;\n role_id: string;\n preferred_username?: string;\n given_name?: string;\n family_name?: string;\n nickname?: string;\n picture?: string;\n roles: {\n [role_id: string]: string; // Maps role_id to role_name\n };\n};\n\nexport type PassflowGroup = {\n id: string;\n name: string;\n default: boolean;\n updated_at: string;\n created_at: string;\n};\n\nexport type PassflowRole = {\n id: string;\n tenant_id: string;\n name: string;\n};\n\nexport type PassflowTenantResponse = {\n tenant_id: string;\n tenant_name: string;\n users_in_groups?: PassflowUserInGroup[];\n groups?: PassflowGroup[];\n roles?: PassflowRole[];\n};\n\n/**\n * Represents a user's membership in a group with their assigned roles\n */\nexport type PassflowUserInGroup = {\n user: {\n id: string;\n name?: string | null;\n email?: string | null;\n phone?: string | null;\n };\n group_id: string;\n roles?: {\n id: string;\n }[];\n};\n\nexport type PassflowCreateTenantPayload = {\n name: string;\n};\n\nexport type LoginWebAppTemplateType = 'default' | 'simple' | 'extendable';\n\nexport type LoginWebAppTemplateColorScheme = 'system' | 'light' | 'dark';\n\nexport type LoginWebAppStyle = {\n primary_color: string;\n text_color: string;\n secondary_text_color: string;\n background_color: string;\n card_color: string;\n input_background_color: string;\n input_border_color: string;\n button_text_color: string;\n divider_color: string;\n federated_button_background_color: string;\n federated_button_text_color: string;\n logo_url: string;\n passkey_button_background_color: string;\n passkey_button_text_color: string;\n background_image: string;\n custom_css: string;\n};\n\nexport type LoginWebAppTheme = {\n template_type: LoginWebAppTemplateType;\n application_name: string;\n remove_passflow_logo: boolean;\n description: string;\n color_scheme: LoginWebAppTemplateColorScheme;\n light_style: LoginWebAppStyle;\n dark_style: LoginWebAppStyle;\n};\n\nexport type PassflowCreateTokenResponse = PassflowTenantResponse;\n\n// Helper function to create paths with parameters\nexport function pathWithParams(template: string, params: Record<string, string>): string {\n let result = template;\n Object.entries(params).forEach(([key, value]) => {\n result = result.replace(`:${key}`, value);\n });\n return result;\n}\n\n// Usage example:\n// const invitationsUrl = pathWithParams(PassflowEndpointPaths.invitationsPath, { tenantID: '123' });\n\n// ============================================\n// Two-Factor Authentication Types\n// ============================================\n\n/**\n * Two-Factor authentication policy\n */\nexport enum TwoFactorPolicy {\n Disabled = 'disabled',\n Optional = 'optional',\n Required = 'required',\n}\n\n/**\n * Two-Factor error codes\n */\nexport type TwoFactorErrorCode =\n | 'INVALID_CODE'\n | 'CODE_EXPIRED'\n | 'TOO_MANY_ATTEMPTS'\n | 'SETUP_NOT_STARTED'\n | 'ALREADY_ENABLED'\n | 'NOT_ENABLED'\n | 'INVALID_RECOVERY_CODE'\n | 'NO_RECOVERY_CODES_REMAINING'\n | 'INVALID_CHALLENGE'\n | 'TIME_DRIFT_DETECTED';\n\n// Request Types\nexport type TwoFactorConfirmRequest = {\n code: string;\n};\n\nexport type TwoFactorVerifyRequest = {\n code: string;\n tfa_token: string;\n trust_device?: boolean;\n};\n\nexport type TwoFactorRecoveryRequest = {\n recovery_code: string;\n tfa_token: string;\n trust_device?: boolean;\n};\n\nexport type TwoFactorDisableRequest = {\n code: string;\n};\n\nexport type TwoFactorRegenerateRequest = {\n code: string;\n};\n\n// Response Types\nexport type TwoFactorStatusResponse = {\n enabled: boolean;\n policy: TwoFactorPolicy;\n recovery_codes_remaining: number;\n totp_digits?: 6 | 8; // Optional for backward compatibility, defaults to 6 if not provided\n};\n\nexport type TwoFactorSetupResponse = {\n secret: string;\n qr_code: string;\n totp_digits?: 6 | 8; // Optional for backward compatibility, defaults to 6 if not provided\n};\n\nexport type TwoFactorConfirmResponse = {\n success: true;\n recovery_codes: string[];\n};\n\nexport type TwoFactorVerifyResponse = Tokens & {\n success: true;\n /** On a PKCE app; `TwoFactorService` redeems it, so its callers never see one. */\n code?: string;\n};\n\n/**\n * Options for the call that completes a sign-in with its second factor.\n */\nexport type TwoFactorCompletionOptions = {\n /**\n * Ask the server to trust this device, so later sign-ins from it skip the\n * second factor where the app allows that. The server keeps the trust in\n * an HttpOnly cookie; nothing is stored by the SDK.\n */\n trustDevice?: boolean;\n};\n\n/**\n * A device the user trusted to stand in for their second factor on this app.\n */\nexport type TwoFactorTrustedDevice = {\n id: string;\n user_agent?: string;\n created_at: string;\n last_used_at?: string;\n expires_at: string;\n /** True for the device the request came from. */\n current: boolean;\n};\n\n/** GET /v2/user/2fa/trusted-devices */\nexport type TwoFactorTrustedDevicesResponse = {\n devices: TwoFactorTrustedDevice[];\n};\n\nexport type TwoFactorRecoveryResponse = Tokens & {\n success: true;\n remaining_recovery_codes: number;\n /** On a PKCE app; `TwoFactorService` redeems it, so its callers never see one. */\n code?: string;\n};\n\nexport type TwoFactorDisableResponse = {\n success: true;\n};\n\nexport type TwoFactorRegenerateResponse = {\n success: true;\n recovery_codes: string[];\n};\n\n// ============================================\n// Two-Factor Magic Link Setup Types\n// ============================================\n\n/**\n * Two-Factor magic link error codes\n */\nexport type TwoFactorSetupMagicLinkErrorCode =\n | 'INVALID_TOKEN'\n | 'EXPIRED_TOKEN'\n | 'REVOKED_TOKEN'\n | 'RATE_LIMITED'\n | 'SERVER_ERROR';\n\n/**\n * Two-Factor magic link error details\n */\nexport type TwoFactorSetupMagicLinkError = {\n code: TwoFactorSetupMagicLinkErrorCode;\n message: string;\n retryAfter?: number; // Seconds until retry allowed (for RATE_LIMITED)\n};\n\n/**\n * Response from magic link validation endpoint\n * Backend returns scoped session token for 2FA setup operations\n */\nexport type TwoFactorSetupMagicLinkValidationResponse = {\n success: boolean;\n sessionToken?: string; // JWT with scope \"2fa_setup\"\n userId?: string; // Target user ID\n expiresIn?: number; // Session expiration in seconds (3600 = 1 hour)\n appId?: string | null; // Optional associated app ID\n error?: TwoFactorSetupMagicLinkError;\n};\n\n/**\n * Internal session state for magic link 2FA setup\n * Stored in memory only, not persisted across page reloads\n */\nexport type TwoFactorSetupMagicLinkSession = {\n sessionToken: string;\n userId: string;\n appId?: string | null;\n scope: '2fa_setup'; // Immutable scope\n timestamp: number;\n expiresAt: number;\n};\n\n// ============================================\n// Two-Factor v2 Multi-Method Types\n// ============================================\n\n/**\n * Two-Factor method types\n */\nexport type TwoFactorMethod = 'totp' | 'email_otp' | 'sms_otp' | 'passkey' | 'push_fcm' | 'push_webpush' | 'recovery_codes';\n\n/**\n * Challenge Request for v2 2FA flow\n */\nexport interface TwoFactorChallengeRequest {\n first_factor_method?: string;\n trust_device?: boolean;\n}\n\n/**\n * Challenge Response from v2 2FA flow\n */\nexport interface TwoFactorChallengeResponse {\n challenge_id: string;\n method: TwoFactorMethod;\n alternative_methods: TwoFactorMethod[];\n expires_at: string;\n code_sent_to?: string; // Masked email/phone for OTP\n webauthn_options?: unknown; // For passkey\n}\n\n/**\n * Verify Request for v2 2FA flow\n */\nexport interface TwoFactorVerifyRequestV2 {\n challenge_id: string;\n method: TwoFactorMethod;\n response: string; // OTP code or WebAuthn response\n trust_device?: boolean;\n}\n\n/**\n * Verify Response from v2 2FA flow\n */\nexport interface TwoFactorVerifyResponseV2 {\n success: boolean;\n access_token?: string;\n refresh_token?: string;\n /** On a PKCE app; `TwoFactorService` redeems it, so its callers never see one. */\n code?: string;\n device_trusted?: boolean;\n}\n\n/**\n * Registered Two-Factor Method\n */\nexport interface RegisteredTwoFactorMethod {\n id: string;\n method: TwoFactorMethod;\n name: string;\n created_at: string;\n last_used_at?: string;\n}\n\n/**\n * Alternative Method Request\n */\nexport interface TwoFactorAlternativeRequest {\n challenge_id: string;\n method: TwoFactorMethod;\n}\n\n// ============================================\n// CLI Authentication Types\n// ============================================\n\nexport interface CLIAuthStatusResponse {\n status: 'pending' | 'completed' | 'expired' | 'failed';\n expires_at?: string;\n challenge?: Record<string, unknown>; // WebAuthn challenge - raw PublicKeyCredentialRequestOptions\n access_token?: string;\n refresh_token?: string;\n user_id?: string;\n expires_in?: number;\n error?: string;\n}\n\nexport interface CLIAuthCompleteRequest {\n session_id: string;\n access_token: string;\n refresh_token: string;\n user_id: string;\n expires_in: number;\n}\n","import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';\nimport {\n APP_ID_HEADER_KEY,\n AUTHORIZATION_HEADER_KEY,\n DEVICE_ID_HEADER_KEY,\n DEVICE_TYPE_HEADER_KEY,\n PASSFLOW_CLOUD_URL,\n TOKEN_EXPIRY_BUFFER_SECONDS,\n} from '../constants';\n\nimport { DeviceService } from '../device';\nimport type { PlatformAdapter } from '../platform';\nimport { webAdapter } from '../platform';\nimport { StorageManager } from '../storage';\nimport { TokenService, isTokenExpired, parseToken } from '../token';\nimport { TokenDeliveryManager } from '../token/delivery-manager';\nimport type { UpgradeChallenge } from '../upgrade/challenge';\nimport { parseUpgradeChallenge } from '../upgrade/challenge';\nimport { hasUpgradeTransactionMarker } from '../upgrade/markers';\n\nimport {\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowError,\n PassflowResponseError,\n RequestMethod,\n RequestOptions,\n type UpgradeDomain,\n resolveUpgradeDomain,\n} from './model';\n\nexport enum HttpStatuses {\n badRequest = 400,\n unauthorized = 401,\n tooManyRequests = 429,\n internalServerError = 500,\n success = 200,\n created = 201,\n}\n\n// Rate limiting retry configuration\nconst MAX_RETRIES = 3;\nconst INITIAL_RETRY_DELAY_MS = 1000;\n\n/**\n * Reports whether a response is an RFC 9470 step-up challenge rather than a\n * plain authentication failure. Adapts the axios response shape onto the\n * transport-agnostic parser in `../upgrade/challenge`.\n */\nconst axiosHeaderReader =\n (response: AxiosResponse) =>\n (name: string): string | undefined => {\n const value = response.headers?.[name] ?? response.headers?.[name.toLowerCase()];\n return Array.isArray(value) ? value.join(', ') : (value as string | undefined);\n };\n\nconst readUpgradeChallengeFrom = (response: AxiosResponse): UpgradeChallenge | undefined =>\n parseUpgradeChallenge(response.status, axiosHeaderReader(response), response.data);\n\nconst isUpgradeChallengeResponse = (response: AxiosResponse): boolean => readUpgradeChallengeFrom(response) !== undefined;\n\n/**\n * Handles a challenge the Passflow instance itself received, resolving with a\n * replayed response once the session has been elevated.\n *\n * Consulted INSIDE the existing error interceptor rather than registered as a\n * later one: axios runs response interceptors FIFO and the handler below\n * converts the `AxiosError` into a `PassflowError`, which has no headers and no\n * config, so anything registered afterwards is structurally too late.\n */\nexport type AxiosUpgradeHandler = (error: AxiosError, challenge: UpgradeChallenge) => Promise<AxiosResponse>;\n\nexport class AxiosClient {\n private instance: AxiosInstance;\n protected storageManager: StorageManager;\n protected deviceService: DeviceService;\n protected tokenDeliveryManager: TokenDeliveryManager;\n private refreshPromise: Promise<AxiosResponse<PassflowAuthorizationResponse>> | null = null;\n private isRefreshing = false;\n private _platform: PlatformAdapter;\n\n tokenService: TokenService;\n\n get origin(): string {\n return this._platform.getCurrentUrl()?.origin ?? '';\n }\n url: string;\n appId?: string;\n\n protected defaultHeaders: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n };\n\n private readonly nonAccessTokenEndpoints = ['/auth/', '/settings', '/settings/'];\n /**\n * Endpoints under `/auth/` that DO need credentials.\n *\n * `'upgrade'` is here because `/auth/upgrade/start` is access-token-gated.\n * Without it the path matches `isNonAuthEndpoint()` and the request\n * interceptor returns before either the cookie-mode branch or the JSON-mode\n * token branch runs: no `Authorization`, no `withCredentials`, no CSRF token,\n * and a prepare round that 401s in every delivery mode.\n */\n private readonly protectedEndpoints = ['logout', 'refresh', 'upgrade'];\n\n /** Set when a consumer wires the upgrade feature. Absent by default. */\n private upgradeHandler?: AxiosUpgradeHandler;\n\n /**\n * Passflow's own API as an interception scope, resolved ONCE here from\n * `PassflowConfig.url` so no wrapper or adapter ever derives it. `null` when\n * no `url` was configured — see `resolveUpgradeDomain`.\n */\n readonly upgradeDomain: UpgradeDomain | null;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n const { url, appId, keyStoragePrefix } = config;\n this._platform = config.platform ?? webAdapter;\n\n this.url = url || PASSFLOW_CLOUD_URL;\n this.upgradeDomain = resolveUpgradeDomain(url);\n\n // Use provided instances or create new ones (backward compatibility)\n this.storageManager =\n storageManager ??\n new StorageManager({\n prefix: keyStoragePrefix ?? '',\n });\n this.deviceService = deviceService ?? new DeviceService(this.storageManager);\n this.tokenService = new TokenService(this.storageManager);\n this.tokenDeliveryManager = new TokenDeliveryManager(this.storageManager);\n\n if (appId) {\n this.appId = appId;\n\n this.defaultHeaders = {\n ...this.defaultHeaders,\n [APP_ID_HEADER_KEY]: appId,\n };\n }\n\n // Add device headers\n const deviceId = this.deviceService.getDeviceId();\n this.defaultHeaders = {\n ...this.defaultHeaders,\n [DEVICE_ID_HEADER_KEY]: deviceId,\n [DEVICE_TYPE_HEADER_KEY]: this._platform.getDeviceType(),\n };\n\n // Detect cookie capability\n this.detectCookieSupport();\n\n this.instance = axios.create({\n baseURL: this.url,\n headers: { ...this.defaultHeaders },\n });\n\n this.instance.interceptors.request.use(async (axiosConfig: InternalAxiosRequestConfig) => {\n // Request to non-access token endpoints\n if (this.isNonAuthEndpoint(axiosConfig.url)) {\n return axiosConfig;\n }\n\n // Cookie mode handling\n if (this.tokenDeliveryManager.isCookieMode()) {\n // Browser automatically sends HttpOnly cookies\n // Do NOT add Authorization header\n axiosConfig.withCredentials = true;\n\n // Add CSRF token if available\n const csrfToken = this.storageManager.getCsrfToken();\n if (csrfToken) {\n axiosConfig.headers['X-CSRF-Token'] = csrfToken;\n }\n\n return axiosConfig;\n }\n\n // JSON mode: existing token handling\n // Request to refresh token endpoint\n if (axiosConfig.url?.includes('refresh')) {\n if (this.isRefreshing) {\n // Abort duplicate refresh requests\n const controller = new AbortController();\n controller.abort();\n axiosConfig.signal = controller.signal;\n return axiosConfig;\n }\n return axiosConfig;\n }\n\n // Request to access token endpoints\n const tokens = this.storageManager.getTokens();\n\n if (tokens?.access_token) {\n const access = parseToken(tokens.access_token);\n\n // Check if token is expired with buffer\n if (isTokenExpired(access, TOKEN_EXPIRY_BUFFER_SECONDS) && tokens.refresh_token) {\n try {\n // Single-flight pattern: reuse in-flight refresh\n if (this.refreshPromise) {\n const response = await this.refreshPromise;\n // After refresh completes, get new token\n if (response?.data?.access_token) {\n axiosConfig.headers[AUTHORIZATION_HEADER_KEY] = `Bearer ${response.data.access_token}`;\n }\n return axiosConfig;\n }\n\n // Start new refresh using single-flight pattern\n this.refreshPromise = this.refreshTokens();\n\n try {\n const response = await this.refreshPromise;\n // After refresh completes, get new token\n if (response?.data?.access_token) {\n axiosConfig.headers[AUTHORIZATION_HEADER_KEY] = `Bearer ${response.data.access_token}`;\n }\n return axiosConfig;\n } finally {\n this.refreshPromise = null;\n }\n } catch (error) {\n // On failure, clear refresh state immediately so future requests can retry\n this.refreshPromise = null;\n this.isRefreshing = false;\n // Clear tokens on auth failure\n this.storageManager.deleteTokens();\n return Promise.reject(error);\n }\n }\n\n axiosConfig.headers[AUTHORIZATION_HEADER_KEY] = `Bearer ${tokens.access_token}`;\n\n return axiosConfig;\n }\n return axiosConfig;\n });\n\n this.instance.interceptors.response.use(\n (response: AxiosResponse) => response,\n async (e: AxiosError) => {\n // Mark the session invalid on a 401 — unless this particular request\n // carries positive evidence that the 401 is part of an elevation.\n //\n // Evidence, both per-request and both necessary:\n //\n // 1. the failed request is one of the upgrade transaction's two rounds\n // (`/auth/upgrade/start`, or the committing `/auth/refresh`). Those\n // 401s mean \"wrong code\" or \"spent challenge\" and carry NO RFC 9470\n // vocabulary at all, so the response cannot be used to recognise\n // them. Without this limb a mistyped TOTP digit logs the user out\n // mid-prompt, which is the defect this feature exists to fix.\n // 2. the response itself carried a parseable challenge, which covers a\n // challenge arriving outside any elevation.\n //\n // On neither, the 401 invalidates exactly as it always has. That matters\n // as much as the suppression: keying on the ABSENCE of challenge\n // vocabulary would mean an expired or revoked session never invalidates,\n // leaving the user in a broken \"authenticated\" UI — the mirror image of\n // the same bug. Scoping to the one request that failed also keeps a\n // genuine expiry on an unrelated background request invalidating\n // normally while a prompt is open.\n if (e.response?.status === HttpStatuses.unauthorized && !this.isUpgradeTransactionError(e)) {\n this.tokenDeliveryManager.setSessionInvalid();\n }\n\n // An elevation seam, consulted here because a later-registered\n // interceptor would only ever see the `PassflowError` below.\n if (e.response && this.upgradeHandler) {\n const challenge = readUpgradeChallengeFrom(e.response);\n if (challenge) {\n try {\n return await this.upgradeHandler(e, challenge);\n } catch (upgradeError) {\n // The core DECLINED to act — `not-a-challenge`, `out-of-scope` or\n // `replay-unsupported` all settle by re-rejecting the error we\n // handed in. Returning that rejection would hand an SDK caller a\n // raw `AxiosError` where every existing consumer pattern-matches\n // on `PassflowError.id`, and it would skip the rate-limit path\n // below as well. The identity comparison is the whole test: the\n // same object back means \"not mine\", anything else is a genuine\n // step-up outcome and belongs to the caller untouched.\n //\n // Reachable in a configuration, not just in theory: with no `url`\n // configured, domain 1 is `null`, so Passflow's own challenges are\n // all out of scope and every one of them took this path.\n if (upgradeError !== e) {\n throw upgradeError;\n }\n }\n }\n }\n\n // Handle rate limiting with retry logic\n if (e.response?.status === HttpStatuses.tooManyRequests) {\n return await this.handleRateLimitError(e);\n }\n return this.handleAxiosError(e);\n },\n );\n }\n\n /**\n * The `setSessionInvalid()` suppression predicate, keyed on the request that\n * failed and on the response that came back — never on \"this response does not\n * speak upgrade vocabulary\", which is unsound in both directions.\n */\n private isUpgradeTransactionError(e: AxiosError): boolean {\n return hasUpgradeTransactionMarker(e.config) || (e.response !== undefined && isUpgradeChallengeResponse(e.response));\n }\n\n /**\n * Installs (or clears) the handler consulted when a response carries an\n * upgrade challenge. Passing `undefined` detaches it.\n */\n setUpgradeInterceptor(handler: AxiosUpgradeHandler | undefined): void {\n this.upgradeHandler = handler;\n }\n\n /**\n * Re-issues a request after an elevation. Exposed for the upgrade handler,\n * which owns the replay-once decision; the marker preventing a second\n * elevation travels on the config the caller passes in.\n */\n replay<T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {\n return this.instance.request<T>(config);\n }\n\n private isProtectedEndpoint(url?: string): boolean {\n return this.protectedEndpoints.some((endpoint) => url?.includes(endpoint));\n }\n\n private isNonAuthEndpoint(url?: string): boolean {\n return this.nonAccessTokenEndpoints.some((endpoint) => url?.includes(endpoint)) && !this.isProtectedEndpoint(url);\n }\n\n /**\n * Detect if cookies are supported/enabled in the browser.\n * If cookie mode is already active (restored from storage) and cookies are not\n * supported, throws immediately to prevent silent misconfiguration.\n *\n * Note: If cookie mode is set later (e.g., server responds with token_delivery=cookie\n * on a non-cookie platform), this check will not catch it. The server should not\n * configure cookie mode for platforms where cookiesSupported() returns false.\n */\n private detectCookieSupport(): void {\n const supported = this._platform.cookiesSupported();\n\n // Enforce immediately if cookie mode was persisted from a previous session.\n if (!supported && this.tokenDeliveryManager.isCookieMode()) {\n throw new Error(\n 'Passflow SDK: Cookie delivery mode is configured but cookies are not ' +\n 'supported in this environment. Use a platform adapter that supports ' +\n 'cookies or configure the server to use JSON token delivery.',\n );\n }\n }\n\n /**\n * Refresh tokens using single-flight pattern to prevent race conditions\n * Supports both cookie mode and JSON mode\n */\n private async refreshTokens(): Promise<AxiosResponse<PassflowAuthorizationResponse>> {\n if (this.tokenDeliveryManager.isCookieMode()) {\n // Cookie mode: call /auth/refresh with credentials:'include'\n // Server reads refresh token from HttpOnly cookie\n const response = await this.instance.post<PassflowAuthorizationResponse>(\n PassflowEndpointPaths.refresh,\n {}, // Empty body\n { withCredentials: true },\n );\n\n // Mark session as valid after successful refresh\n this.tokenDeliveryManager.setSessionValid();\n\n // Extract CSRF token if present\n if (response.data.csrf_token) {\n this.storageManager.setCsrfToken(response.data.csrf_token);\n }\n\n // Save ID token if present (other tokens are in HttpOnly cookies)\n if (response.data.id_token) {\n this.storageManager.setIdToken(response.data.id_token);\n }\n\n return response;\n } else {\n // JSON mode: existing refresh logic\n const tokens = this.storageManager.getTokens();\n const scopes = this.storageManager.getScopes();\n\n if (!tokens?.refresh_token) {\n throw new Error('No refresh token available');\n }\n\n this.isRefreshing = true;\n const payload = {\n refresh_token: tokens.refresh_token,\n scopes,\n };\n\n const response = await this.instance.post<PassflowAuthorizationResponse>(PassflowEndpointPaths.refresh, payload, {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${tokens.refresh_token}`,\n },\n });\n\n if (response.data) {\n // Update storage BEFORE processing queued requests\n this.storageManager.saveTokens(response.data);\n }\n\n this.isRefreshing = false;\n\n return response;\n }\n }\n\n private async handleRateLimitError(e: AxiosError): Promise<AxiosResponse> {\n const config = e.config;\n if (!config) {\n return Promise.reject(e);\n }\n\n // Only retry idempotent requests to avoid duplicate operations\n const method = config.method?.toUpperCase();\n const isIdempotent = ['GET', 'HEAD', 'OPTIONS'].includes(method || '');\n\n if (!isIdempotent) {\n // Don't retry non-idempotent requests - could cause duplicates\n return Promise.reject(e);\n }\n\n // Track retry attempts on the config object\n const retryCount = (config as AxiosRequestConfig & { _retryCount?: number })._retryCount || 0;\n\n if (retryCount >= MAX_RETRIES) {\n // Max retries exceeded, reject with original error\n return Promise.reject(e);\n }\n\n // Calculate delay with exponential backoff\n let delayMs = INITIAL_RETRY_DELAY_MS * Math.pow(2, retryCount);\n\n // Check for Retry-After header (can be in seconds or HTTP date)\n const retryAfter = e.response?.headers?.['retry-after'];\n if (retryAfter) {\n const retryAfterNum = Number.parseInt(retryAfter, 10);\n if (!Number.isNaN(retryAfterNum)) {\n // Retry-After is in seconds\n delayMs = retryAfterNum * 1000;\n } else {\n // Retry-After is an HTTP date\n const retryDate = new Date(retryAfter);\n if (!Number.isNaN(retryDate.getTime())) {\n delayMs = Math.max(0, retryDate.getTime() - Date.now());\n }\n }\n }\n\n // Wait for the calculated delay\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n\n // Increment retry count and retry the request\n (config as AxiosRequestConfig & { _retryCount?: number })._retryCount = retryCount + 1;\n return this.instance.request(config);\n }\n\n // eslint-disable-next-line complexity\n // biome-ignore lint/suspicious/useAwait: <explanation>\n private async handleAxiosError(e: AxiosError): Promise<unknown> {\n // Handle network\n if (!e.response) {\n return Promise.reject(e);\n }\n\n const status = e.response.status as HttpStatuses;\n const errorData = e.response.data as Record<string, unknown>;\n\n // If we have a response with error data in Passflow format\n if ('error' in errorData && typeof errorData.error === 'object' && errorData.error !== null) {\n const { error } = errorData as PassflowResponseError;\n\n return Promise.reject(new PassflowError(error));\n }\n\n // For non-Passflow format errors, create a generic PassflowError\n return Promise.reject(\n new PassflowError({\n id: `error.http.${status}`,\n message: e.message || 'An error occurred',\n status: status,\n location: e.config?.url || 'unknown',\n time: new Date().toISOString(),\n }),\n );\n }\n\n private async send<T, D>(method: RequestMethod, path: string, options?: RequestOptions<D>): Promise<T> {\n const response = await this.instance.request<T>({\n method,\n url: path,\n ...options,\n });\n return response.data;\n }\n\n get<T>(path: string, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.GET, path, config);\n }\n\n post<T, D>(path: string, data?: D, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.POST, path, { data, ...config });\n }\n\n put<T, D>(path: string, data?: D, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.PUT, path, { data, ...config });\n }\n\n patch<T, D>(path: string, data?: D, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.PATCH, path, { data, ...config });\n }\n\n delete<T>(path: string, config?: AxiosRequestConfig): Promise<T> {\n return this.send(RequestMethod.DELETE, path, config);\n }\n\n /**\n * Update the appId and propagate it to axios headers.\n * This ensures that the APP_ID_HEADER_KEY is updated in all future requests.\n *\n * @param appId - The new application ID to set\n */\n setAppId(appId: string): void {\n this.appId = appId;\n\n // Update default headers\n this.defaultHeaders = {\n ...this.defaultHeaders,\n [APP_ID_HEADER_KEY]: appId,\n };\n\n // Update axios instance headers\n this.instance.defaults.headers.common[APP_ID_HEADER_KEY] = appId;\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport { type AppSettings, type PassflowConfig, PassflowEndpointPaths } from './model';\n\nexport class AppAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getAppSettings(): Promise<AppSettings> {\n return this.axiosClient.get<AppSettings>(PassflowEndpointPaths.appSettings);\n }\n}\n","import { AuthenticationResponseJSON, RegistrationResponseJSON } from '@simplewebauthn/types';\nimport type { AxiosRequestConfig } from 'axios';\nimport { APP_ID_HEADER_KEY, AUTHORIZATION_HEADER_KEY } from '../constants';\n\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\nimport { markUpgradeTransaction } from '../upgrade/markers';\n\nimport { AxiosClient } from './axios-client';\nimport {\n OS,\n PassflowAdminEndpointPaths,\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowLogoutResponse,\n PassflowPasskeyAuthenticatePayload,\n PassflowPasskeyAuthenticateStartExtendedPayload,\n PassflowPasskeyAuthenticateStartPayload,\n PassflowPasskeyRegisterPayload,\n PassflowPasskeyRegisterStartExtendedPayload,\n PassflowPasskeyRegisterStartPayload,\n PassflowPasskeyStart,\n PassflowPasswordlessResponse,\n PassflowPasswordlessSignInCompletePayload,\n PassflowPasswordlessSignInExtendedPayload,\n PassflowPasswordlessSignInPayload,\n PassflowSendPasswordResetEmailPayload,\n PassflowSessionValidationResponse,\n PassflowSignInExtendedPayload,\n PassflowSignInPayload,\n PassflowSignUpPayload,\n PassflowSuccessResponse,\n PassflowValidatePayload,\n PassflowValidationResponse,\n UpgradeRefreshField,\n} from './model';\n\n/**\n * Request config for the calls that end a sign-in.\n *\n * They go with credentials so the browser sends the trusted-device cookie\n * (`passflow_trusted_device_<appID>`) the server reads to skip the second\n * factor, and so a cross-origin response can set it. Only the per-request\n * flag: the Authorization and CSRF handling stay as the interceptor has them.\n */\nconst SIGN_IN_REQUEST_CONFIG: AxiosRequestConfig = { withCredentials: true };\n\nexport class AuthAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Refreshes the session, optionally committing an assurance upgrade.\n *\n * `upgrade` carries `{type, assertion}` — the proof the user just produced.\n * The server scores the new `acr` from the durable session record plus the\n * verified factor and rotates the tokens, so this one endpoint is both the\n * refresh and the elevation.\n *\n * When `upgrade` is present the request is MARKED as part of the upgrade\n * transaction. The server answers a wrong code, a spent challenge, a foreign\n * session and an exhausted budget with a bare 401 carrying no RFC 9470\n * vocabulary, which is indistinguishable from an expired session on the\n * response alone; the marker is what tells the response interceptor not to\n * invalidate the session over a mistyped digit.\n */\n refreshToken(\n refreshToken: string,\n scopes: string[],\n accessToken?: string,\n upgrade?: UpgradeRefreshField,\n ): Promise<PassflowAuthorizationResponse> {\n const payload = {\n access: accessToken,\n scopes,\n // Omitted entirely rather than sent as `null` when absent: the server\n // decodes non-strictly and an explicit null would be a value, not a gap.\n ...(upgrade ? { upgrade } : {}),\n };\n\n const config: AxiosRequestConfig = {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${refreshToken}`,\n },\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, typeof payload>(\n PassflowEndpointPaths.refresh,\n payload,\n upgrade ? markUpgradeTransaction(config) : config,\n );\n }\n\n signIn(payload: PassflowSignInPayload, deviceId: string, os: OS): Promise<PassflowAuthorizationResponse> {\n const defaultPayload: PassflowSignInExtendedPayload = {\n ...payload,\n device: deviceId,\n os,\n };\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowSignInExtendedPayload>(\n PassflowEndpointPaths.signin,\n defaultPayload,\n SIGN_IN_REQUEST_CONFIG,\n );\n }\n\n signUp(payload: PassflowSignUpPayload): Promise<PassflowAuthorizationResponse> {\n const { create_tenant, anonymous } = payload;\n const defaultPayload: PassflowSignUpPayload = {\n ...payload,\n create_tenant: create_tenant ?? false,\n anonymous: anonymous ?? false,\n };\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowSignUpPayload>(\n PassflowEndpointPaths.signup,\n defaultPayload,\n SIGN_IN_REQUEST_CONFIG,\n );\n }\n\n /** Redeems the code a PKCE sign-in answered with for the tokens it stands for. */\n pkceExchange(code: string, verifier: string): Promise<PassflowAuthorizationResponse> {\n return this.axiosClient.post<PassflowAuthorizationResponse, { authz_code: string; verifier: string }>(\n PassflowEndpointPaths.pkceExchange,\n { authz_code: code, verifier },\n SIGN_IN_REQUEST_CONFIG,\n );\n }\n\n passwordlessSignIn(\n payload: PassflowPasswordlessSignInPayload,\n deviceId: string,\n os: OS,\n ): Promise<PassflowPasswordlessResponse> {\n const { create_tenant } = payload;\n const defaultPayload: PassflowPasswordlessSignInExtendedPayload = {\n ...payload,\n create_tenant: create_tenant ?? false,\n device: deviceId,\n os,\n };\n return this.axiosClient.post<PassflowPasswordlessResponse, PassflowPasswordlessSignInExtendedPayload>(\n PassflowEndpointPaths.passwordless,\n defaultPayload,\n );\n }\n\n passwordlessSignInComplete(payload: PassflowPasswordlessSignInCompletePayload): Promise<PassflowValidationResponse> {\n return this.axiosClient.post<PassflowValidationResponse, PassflowPasswordlessSignInCompletePayload>(\n PassflowEndpointPaths.passwordlessComplete,\n payload,\n SIGN_IN_REQUEST_CONFIG,\n );\n }\n\n logOut(deviceId?: string, refreshToken?: string, isAdmin = false): Promise<PassflowLogoutResponse> {\n const payload = !isAdmin ? { refresh_token: refreshToken, device: deviceId } : undefined;\n const endpoint = isAdmin ? PassflowAdminEndpointPaths.logout : PassflowEndpointPaths.logout;\n\n return this.axiosClient.post<PassflowLogoutResponse, typeof payload>(endpoint, payload);\n }\n\n validateSession(): Promise<PassflowSessionValidationResponse> {\n return this.axiosClient.get<PassflowSessionValidationResponse>(PassflowEndpointPaths.validateSession);\n }\n\n sendPasswordResetEmail(payload: PassflowSendPasswordResetEmailPayload): Promise<PassflowSuccessResponse> {\n return this.axiosClient.post<PassflowSuccessResponse, typeof payload>(\n PassflowEndpointPaths.sendPasswordResetEmail,\n payload,\n );\n }\n\n resetPassword(\n newPassword: string,\n scopes: string[],\n resetToken?: string,\n codeChallenge?: string,\n ): Promise<PassflowAuthorizationResponse> {\n const payload = {\n password: newPassword,\n scopes,\n ...(codeChallenge ? { code_challenge: codeChallenge } : {}),\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, typeof payload>(PassflowEndpointPaths.resetPassword, payload, {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${resetToken}`,\n [APP_ID_HEADER_KEY]: undefined,\n },\n });\n }\n\n passkeyRegisterStart(\n payload: PassflowPasskeyRegisterStartPayload,\n deviceId: string,\n os: OS,\n isAdmin = false,\n ): Promise<PassflowPasskeyStart> {\n const { create_tenant } = payload;\n const defaultPayload: PassflowPasskeyRegisterStartExtendedPayload = {\n ...payload,\n create_tenant: create_tenant ?? false,\n device: deviceId,\n os,\n };\n\n const endpoint = isAdmin ? PassflowAdminEndpointPaths.passkeyRegisterStart : PassflowEndpointPaths.passkeyRegisterStart;\n\n return this.axiosClient.post<PassflowPasskeyStart, PassflowPasskeyRegisterStartExtendedPayload>(endpoint, defaultPayload);\n }\n\n passkeyRegisterComplete(\n passkeyData: RegistrationResponseJSON,\n deviceId: string,\n challengeId: string,\n isAdmin = false,\n ): Promise<PassflowAuthorizationResponse> {\n const payload: PassflowPasskeyRegisterPayload = {\n challenge_id: challengeId,\n device: deviceId,\n passkey_data: passkeyData,\n };\n\n const endpoint = isAdmin\n ? PassflowAdminEndpointPaths.passkeyRegisterComplete\n : PassflowEndpointPaths.passkeyRegisterComplete;\n\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowPasskeyRegisterPayload>(\n endpoint,\n payload,\n SIGN_IN_REQUEST_CONFIG,\n );\n }\n\n passkeyAuthenticateStart(\n payload: PassflowPasskeyAuthenticateStartPayload,\n deviceId: string,\n os: OS,\n isAdmin = false,\n ): Promise<PassflowPasskeyStart> {\n const defaultPayload: PassflowPasskeyAuthenticateStartExtendedPayload = {\n ...payload,\n user_id: payload.user_id ?? '',\n device: deviceId,\n os,\n };\n\n const endpoint = isAdmin\n ? PassflowAdminEndpointPaths.passkeyAuthenticateStart\n : PassflowEndpointPaths.passkeyAuthenticateStart;\n\n return this.axiosClient.post<PassflowPasskeyStart, PassflowPasskeyAuthenticateStartExtendedPayload>(\n endpoint,\n defaultPayload,\n );\n }\n\n passkeyAuthenticateComplete(\n passkeyData: AuthenticationResponseJSON,\n deviceId: string,\n challengeId: string,\n isAdmin = false,\n ): Promise<PassflowAuthorizationResponse> {\n const payload: PassflowPasskeyAuthenticatePayload = {\n challenge_id: challengeId,\n device: deviceId,\n passkey_data: passkeyData,\n };\n\n const endpoint = isAdmin\n ? PassflowAdminEndpointPaths.passkeyAuthenticateComplete\n : PassflowEndpointPaths.passkeyAuthenticateComplete;\n\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowPasskeyAuthenticatePayload>(\n endpoint,\n payload,\n SIGN_IN_REQUEST_CONFIG,\n );\n }\n\n passkeyValidate(\n otp: string,\n deviceId: string,\n challengeId: string,\n isAdmin = false,\n appId?: string,\n ): Promise<PassflowValidationResponse> {\n const payload: PassflowValidatePayload = {\n otp,\n device: deviceId,\n challenge_id: challengeId,\n };\n\n let endpoint: PassflowEndpointPaths.passkeyValidate | PassflowAdminEndpointPaths.passkeyValidate =\n PassflowEndpointPaths.passkeyValidate;\n if (!appId && isAdmin) {\n endpoint = PassflowAdminEndpointPaths.passkeyValidate;\n }\n\n const headers = appId ? { [APP_ID_HEADER_KEY]: appId } : {};\n\n return this.axiosClient.post<PassflowValidationResponse, PassflowValidatePayload>(endpoint, payload, { headers });\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport { CLIAuthCompleteRequest, CLIAuthStatusResponse, PassflowConfig, PassflowEndpointPaths, pathWithParams } from './model';\n\nexport class CLIAuthAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getCLIAuthStatus(sessionId: string): Promise<CLIAuthStatusResponse> {\n const url = pathWithParams(PassflowEndpointPaths.cliAuthStatus, { sessionId });\n return this.axiosClient.get<CLIAuthStatusResponse>(url);\n }\n\n completeCLIAuth(request: CLIAuthCompleteRequest): Promise<void> {\n return this.axiosClient.post<void, CLIAuthCompleteRequest>(PassflowEndpointPaths.cliAuthComplete, request);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport { type PassflowConfig, PassflowEndpointPaths, type PassflowSuccessResponse, pathWithParams } from './model';\n\nexport interface RequestInviteLinkPayload {\n email?: string;\n tenant?: string;\n group?: string;\n role?: string;\n callback?: string;\n send_to_email?: boolean;\n data?: Record<string, unknown>;\n}\n\nexport interface InviteLinkResponse {\n link: string;\n token?: string;\n}\n\nexport interface Invitation {\n id: string;\n archived?: boolean;\n app_id: string;\n inviter_id: string;\n inviter_name: string;\n token: string;\n email?: string;\n role?: string;\n tenant?: string;\n tenant_name?: string;\n group?: string;\n created_by?: string;\n created_at: string;\n expires_at: string;\n callback?: string;\n data?: Record<string, unknown>;\n}\n\n// Internal API response type\ninterface InvitationsAPIResponse {\n invites: Invitation[];\n next_page_skip?: string;\n}\n\n// Public interface for external use\nexport interface InvitationsPaginatedList {\n invites: Invitation[];\n nextPageSkip?: string;\n}\n\nexport class InvitationAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Requests an invitation link that can be used to invite users\n * @param payload Request invitation payload\n * @returns Promise with invitation link and token\n */\n requestInviteLink(payload: RequestInviteLinkPayload): Promise<InviteLinkResponse> {\n return this.axiosClient.post<InviteLinkResponse, RequestInviteLinkPayload>(\n PassflowEndpointPaths.requestInvitation,\n payload,\n );\n }\n\n /**\n * Gets a list of active invitations\n * @param options Optional parameters for filtering and pagination\n * @returns Promise with paginated list of invitations\n */\n getInvitations(options: {\n tenantID: string;\n groupID?: string;\n skip?: number | string;\n limit?: number | string;\n }): Promise<InvitationsPaginatedList> {\n const params: Record<string, string> = {};\n\n if (options.groupID) params.group_id = options.groupID.toString();\n if (options.skip !== undefined) params.skip = options.skip.toString();\n if (options.limit !== undefined) params.limit = options.limit.toString();\n\n const path = pathWithParams(PassflowEndpointPaths.invitationsPath, {\n tenantID: options.tenantID,\n });\n\n return this.axiosClient.get<InvitationsAPIResponse>(path, { params }).then((response) => ({\n invites: response.invites,\n nextPageSkip: response.next_page_skip,\n }));\n }\n\n /**\n * Deletes an invitation by token\n * @param invitationID The invitation ID to delete\n * @returns Promise with success response\n */\n deleteInvitation(invitationID: string): Promise<PassflowSuccessResponse> {\n const path = pathWithParams(PassflowEndpointPaths.invitationDelete, {\n invitationID,\n });\n return this.axiosClient.delete<PassflowSuccessResponse>(path);\n }\n\n /**\n * Resend an invitation by token\n * @param invitationID The invitation ID to resend\n * @returns Promise with success response\n */\n resendInvitation(invitationID: string): Promise<PassflowSuccessResponse> {\n const path = pathWithParams(PassflowEndpointPaths.invitationResend, {\n invitationID,\n });\n return this.axiosClient.post<PassflowSuccessResponse, unknown>(path, {});\n }\n\n /**\n * Get a link to an invitation by id\n * @param invitationID The invitation ID to get link\n * @returns Promise with the link\n */\n getInvitationLink(invitationID: string): Promise<InviteLinkResponse> {\n const path = pathWithParams(PassflowEndpointPaths.invitationGetLink, {\n invitationID,\n });\n return this.axiosClient.get<InviteLinkResponse>(path);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n type PassflowConfig,\n PassflowEndpointPaths,\n type PassflowPasskeySettings,\n type PassflowPasswordPolicySettings,\n type PassflowSettingsAll,\n} from './model';\n\nexport class SettingAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getSettingsAll(): Promise<PassflowSettingsAll> {\n return this.axiosClient.get<PassflowSettingsAll>(PassflowEndpointPaths.settingsAll);\n }\n\n getPasswordPolicySettings(): Promise<PassflowPasswordPolicySettings> {\n return this.axiosClient.get<PassflowPasswordPolicySettings>(PassflowEndpointPaths.settingsPasswordPolicy);\n }\n\n getPasskeySettings(): Promise<PassflowPasskeySettings> {\n return this.axiosClient.get<PassflowPasskeySettings>(PassflowEndpointPaths.settingsPasskey);\n }\n}\n","import { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowCreateTenantPayload,\n PassflowEndpointPaths,\n PassflowInvitePayload,\n PassflowTenantResponse,\n} from './model';\n\n// Response types\nexport type PassflowStatusResponse = {\n status: string;\n};\n\nexport type PassflowGroupResponse = {\n id: string;\n name: string;\n default?: boolean;\n updated_at: string;\n created_at: string;\n};\n\nexport type PassflowRoleResponse = {\n id: string;\n tenant_id: string;\n name: string;\n};\n\nexport type PassflowUserTenantMembershipResponse = Record<\n string, // tenant_id\n {\n tenant_id: string;\n tenant_name: string;\n groups: Record<string, string[]>; // group_id -> role_ids[]\n group_names: Record<string, string>; // group_id -> group_name\n }\n>;\n\nexport type PassflowInvitationItem = {\n id: string;\n archived: boolean;\n app_id: string;\n inviter_id: string;\n inviter_name: string;\n token: string;\n email: string;\n role: string;\n tenant: string;\n tenant_name: string;\n group: string;\n created_by: string;\n created_at: string;\n expires_at: string;\n};\n\nexport type PassflowInvitationsResponse = {\n invites: PassflowInvitationItem[];\n next_page_skip: string;\n};\n\n// Request payload types\nexport type PassflowUpdateTenantPayload = {\n name: string;\n};\n\nexport type PassflowCreateGroupPayload = {\n name: string;\n};\n\nexport type PassflowUpdateGroupPayload = {\n name: string;\n};\n\nexport type PassflowAddUserToGroupPayload = {\n user_id: string;\n role: string;\n};\n\nexport type PassflowRemoveUserRolesPayload = {\n user_id: string;\n roles: string[];\n};\n\nexport type PassflowChangeUserRolesPayload = {\n user_id: string;\n roles: string[];\n};\n\nexport type PassflowCreateRolePayload = {\n name: string;\n};\n\nexport type PassflowUpdateRolePayload = {\n name: string;\n};\n\nexport class TenantAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n joinInvitation(token: string, scopes: string[]): Promise<PassflowAuthorizationResponse> {\n const payload = {\n invite_token: token,\n scopes,\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, PassflowInvitePayload>(\n PassflowEndpointPaths.joinInvitation,\n payload,\n );\n }\n\n createTenant(name: string): Promise<PassflowTenantResponse> {\n const payload = {\n name,\n };\n return this.axiosClient.post<PassflowTenantResponse, PassflowCreateTenantPayload>(\n PassflowEndpointPaths.tenantPath,\n payload,\n );\n }\n\n // 1. Tenant Management\n\n /**\n * Get tenant details\n * @param tenantId Tenant ID\n */\n getTenantDetails(tenantId: string): Promise<PassflowTenantResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}`;\n return this.axiosClient.get<PassflowTenantResponse>(path);\n }\n\n /**\n * Update tenant name\n * @param tenantId Tenant ID\n * @param name New tenant name\n */\n updateTenant(tenantId: string, name: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}`;\n const payload: PassflowUpdateTenantPayload = { name };\n return this.axiosClient.put<PassflowStatusResponse, PassflowUpdateTenantPayload>(path, payload);\n }\n\n /**\n * Delete a tenant\n * @param tenantId Tenant ID\n */\n deleteTenant(tenantId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n /**\n * Get user's tenant memberships\n */\n getUserTenantMembership(): Promise<PassflowUserTenantMembershipResponse> {\n return this.axiosClient.get<PassflowUserTenantMembershipResponse>(PassflowEndpointPaths.tenantPath);\n }\n\n // 2. Group Management\n\n /**\n * Create a group in a tenant\n * @param tenantId Tenant ID\n * @param name Group name\n */\n createGroup(tenantId: string, name: string): Promise<PassflowGroupResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group`;\n const payload: PassflowCreateGroupPayload = { name };\n return this.axiosClient.post<PassflowGroupResponse, PassflowCreateGroupPayload>(path, payload);\n }\n\n /**\n * Get group information\n * @param tenantId Tenant ID\n * @param groupId Group ID\n */\n getGroupInfo(tenantId: string, groupId: string): Promise<PassflowGroupResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}`;\n return this.axiosClient.get<PassflowGroupResponse>(path);\n }\n\n /**\n * Update a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param name New group name\n */\n updateGroup(tenantId: string, groupId: string, name: string): Promise<PassflowGroupResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}`;\n const payload: PassflowUpdateGroupPayload = { name };\n return this.axiosClient.put<PassflowGroupResponse, PassflowUpdateGroupPayload>(path, payload);\n }\n\n /**\n * Delete a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n */\n deleteGroup(tenantId: string, groupId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n /**\n * Add a user to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param role Role to assign\n */\n addUserToGroup(tenantId: string, groupId: string, userId: string, role: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/add`;\n const payload: PassflowAddUserToGroupPayload = { user_id: userId, role };\n return this.axiosClient.post<PassflowStatusResponse, PassflowAddUserToGroupPayload>(path, payload);\n }\n\n /**\n * Remove user roles from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles Roles to remove\n */\n removeUserRolesFromGroup(\n tenantId: string,\n groupId: string,\n userId: string,\n roles: string[],\n ): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/remove_roles`;\n const payload: PassflowRemoveUserRolesPayload = { user_id: userId, roles };\n return this.axiosClient.post<PassflowStatusResponse, PassflowRemoveUserRolesPayload>(path, payload);\n }\n\n /**\n * Change user roles in a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles New roles to assign\n */\n changeUserRoles(tenantId: string, groupId: string, userId: string, roles: string[]): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/change`;\n const payload: PassflowChangeUserRolesPayload = { user_id: userId, roles };\n return this.axiosClient.post<PassflowStatusResponse, PassflowChangeUserRolesPayload>(path, payload);\n }\n\n /**\n * Delete a user from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n */\n deleteUserFromGroup(tenantId: string, groupId: string, userId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/${userId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n // 3. Role Management\n\n /**\n * Get roles for a tenant\n * @param tenantId Tenant ID\n */\n getRolesForTenant(tenantId: string): Promise<PassflowRoleResponse[]> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role`;\n return this.axiosClient.get<PassflowRoleResponse[]>(path);\n }\n\n /**\n * Create a role for a tenant\n * @param tenantId Tenant ID\n * @param name Role name\n */\n createRoleForTenant(tenantId: string, name: string): Promise<PassflowRoleResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role`;\n const payload: PassflowCreateRolePayload = { name };\n return this.axiosClient.post<PassflowRoleResponse, PassflowCreateRolePayload>(path, payload);\n }\n\n /**\n * Update a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n * @param name New role name\n */\n updateRole(tenantId: string, roleId: string, name: string): Promise<PassflowRoleResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role/${roleId}`;\n const payload: PassflowUpdateRolePayload = { name };\n return this.axiosClient.put<PassflowRoleResponse, PassflowUpdateRolePayload>(path, payload);\n }\n\n /**\n * Delete a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n */\n deleteRole(tenantId: string, roleId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/role/${roleId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n // 4. User Management in Tenants\n\n /**\n * Delete a user from a tenant\n * @param tenantId Tenant ID\n * @param userId User ID\n */\n deleteUserFromTenant(tenantId: string, userId: string): Promise<PassflowStatusResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/user/${userId}`;\n return this.axiosClient.delete<PassflowStatusResponse>(path);\n }\n\n // 5. Invitation Management\n\n /**\n * Get invitations to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n */\n getGroupInvitations(tenantId: string, groupId: string, limit: number, skip: number): Promise<PassflowInvitationsResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/invitations`;\n return this.axiosClient.get<PassflowInvitationsResponse>(path, {\n params: { limit, skip },\n });\n }\n\n /**\n * Get invitations to a tenant\n * @param tenantId Tenant ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n */\n getTenantInvitations(tenantId: string, limit: number, skip: number): Promise<PassflowInvitationsResponse> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/invitations`;\n return this.axiosClient.get<PassflowInvitationsResponse>(path, {\n params: { limit, skip },\n });\n }\n\n /**\n * Invalidate an invitation by ID\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param inviteId Invitation ID\n */\n invalidateInviteById(tenantId: string, groupId: string, inviteId: string): Promise<Record<string, never>> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/invite/${inviteId}`;\n return this.axiosClient.delete<Record<string, never>>(path);\n }\n\n /**\n * Invalidate an invitation by email\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param email Email address\n */\n invalidateInviteByEmail(tenantId: string, groupId: string, email: string): Promise<Record<string, never>> {\n const path = `${PassflowEndpointPaths.tenantPath}/${tenantId}/group/${groupId}/invite/email/${email}`;\n return this.axiosClient.delete<Record<string, never>>(path);\n }\n}\n","import type { AxiosRequestConfig } from 'axios';\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n PassflowConfig,\n PassflowEndpointPaths,\n RegisteredTwoFactorMethod,\n TwoFactorAlternativeRequest,\n TwoFactorChallengeRequest,\n TwoFactorChallengeResponse,\n TwoFactorConfirmRequest,\n TwoFactorConfirmResponse,\n TwoFactorDisableRequest,\n TwoFactorDisableResponse,\n TwoFactorMethod,\n TwoFactorRecoveryRequest,\n TwoFactorRecoveryResponse,\n TwoFactorRegenerateRequest,\n TwoFactorRegenerateResponse,\n TwoFactorSetupMagicLinkErrorCode,\n TwoFactorSetupMagicLinkValidationResponse,\n TwoFactorSetupResponse,\n TwoFactorStatusResponse,\n TwoFactorTrustedDevice,\n TwoFactorTrustedDevicesResponse,\n TwoFactorVerifyRequest,\n TwoFactorVerifyRequestV2,\n TwoFactorVerifyResponse,\n TwoFactorVerifyResponseV2,\n pathWithParams,\n} from './model';\n\n/** The 2FA token as the Bearer credential, or no config when there is none. */\nconst bearer = (tfaToken?: string): AxiosRequestConfig =>\n tfaToken ? { headers: { Authorization: `Bearer ${tfaToken}` } } : {};\n\n/**\n * Request config for the calls that end a sign-in with its second factor.\n *\n * They go with credentials so that a `trust_device` answer can set the\n * HttpOnly trusted-device cookie on a cross-origin response, and so the\n * browser sends the one it already holds.\n */\nconst credentialed = (tfaToken?: string): AxiosRequestConfig => ({\n withCredentials: true,\n ...bearer(tfaToken),\n});\n\n/** Backend response format (snake_case) for magic link validation */\ninterface TwoFactorSetupMagicLinkBackendResponse {\n session_token?: string;\n user_id?: string;\n expires_in?: number;\n app_id?: string | null;\n}\n\n/**\n * API client for Two-Factor Authentication operations\n */\nexport class TwoFactorApiClient {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Get current 2FA enrollment status\n * GET /user/2fa/status\n */\n getStatus(): Promise<TwoFactorStatusResponse> {\n return this.axiosClient.get<TwoFactorStatusResponse>(PassflowEndpointPaths.twoFactorStatus);\n }\n\n /**\n * Begin 2FA setup process\n * POST /user/2fa/setup/begin\n * Returns secret and QR code for authenticator app\n */\n beginSetup(): Promise<TwoFactorSetupResponse> {\n return this.axiosClient.post<TwoFactorSetupResponse, {}>(PassflowEndpointPaths.twoFactorSetupBegin, {});\n }\n\n /**\n * Confirm 2FA setup with TOTP code\n * POST /user/2fa/setup/confirm\n * Returns recovery codes on success\n */\n confirmSetup(payload: TwoFactorConfirmRequest): Promise<TwoFactorConfirmResponse> {\n return this.axiosClient.post<TwoFactorConfirmResponse, TwoFactorConfirmRequest>(\n PassflowEndpointPaths.twoFactorSetupConfirm,\n payload,\n );\n }\n\n /**\n * Verify TOTP code during login\n * POST /auth/2fa/verify\n * Uses tfa_token as Bearer token for authentication\n */\n verify(payload: TwoFactorVerifyRequest): Promise<TwoFactorVerifyResponse> {\n const { tfa_token, code, trust_device } = payload;\n // `trust_device` is sent only when asked for, so a plain verify stays the\n // body it always was.\n return this.axiosClient.post<TwoFactorVerifyResponse, { code: string; trust_device?: true }>(\n PassflowEndpointPaths.twoFactorVerify,\n { code, ...(trust_device ? { trust_device: true as const } : {}) },\n credentialed(tfa_token),\n );\n }\n\n /**\n * Use recovery code for authentication\n * POST /auth/2fa/recovery\n * Uses tfa_token as Bearer token for authentication\n */\n useRecoveryCode(payload: TwoFactorRecoveryRequest): Promise<TwoFactorRecoveryResponse> {\n const { tfa_token, recovery_code, trust_device } = payload;\n return this.axiosClient.post<TwoFactorRecoveryResponse, { recovery_code: string; trust_device?: true }>(\n PassflowEndpointPaths.twoFactorRecovery,\n { recovery_code, ...(trust_device ? { trust_device: true as const } : {}) },\n credentialed(tfa_token),\n );\n }\n\n /**\n * Disable 2FA (requires TOTP verification)\n * DELETE /user/2fa\n */\n disable(payload: TwoFactorDisableRequest): Promise<TwoFactorDisableResponse> {\n return this.axiosClient.delete<TwoFactorDisableResponse>(PassflowEndpointPaths.twoFactor, { data: payload });\n }\n\n /**\n * Regenerate recovery codes\n * POST /user/2fa/recovery-codes/regenerate\n */\n regenerateRecoveryCodes(payload: TwoFactorRegenerateRequest): Promise<TwoFactorRegenerateResponse> {\n return this.axiosClient.post<TwoFactorRegenerateResponse, TwoFactorRegenerateRequest>(\n PassflowEndpointPaths.twoFactorRegenerateCodes,\n payload,\n );\n }\n\n /**\n * Validate magic link token for 2FA setup\n * GET /auth/2fa-setup/:token\n *\n * This endpoint validates an admin-generated magic link token\n * and returns a scoped session (scope: \"2fa_setup\") that can ONLY\n * be used for completing 2FA setup operations.\n *\n * This method never throws - it always returns a TwoFactorSetupMagicLinkValidationResponse\n * with either success=true and session data, or success=false and error details.\n *\n * @param token - Magic link token from URL parameter\n * @returns Validation response with scoped session token or error\n */\n validateTwoFactorSetupMagicLink(token: string): Promise<TwoFactorSetupMagicLinkValidationResponse> {\n // Construct endpoint with token as path parameter\n const endpoint = `${PassflowEndpointPaths.twoFactorSetupMagicLink}/${token}`;\n\n // No authentication required - token validation IS the authentication\n return this.axiosClient\n .get<TwoFactorSetupMagicLinkValidationResponse>(endpoint, {\n // Override default auth headers (this is a public endpoint)\n transformRequest: [\n (data, headers) => {\n if (headers) {\n delete headers.Authorization;\n }\n return data;\n },\n ],\n })\n .then((response) => {\n // Transform snake_case backend response to camelCase\n const backendResponse = response as unknown as TwoFactorSetupMagicLinkBackendResponse;\n return {\n success: true,\n sessionToken: backendResponse.session_token,\n userId: backendResponse.user_id,\n expiresIn: backendResponse.expires_in,\n appId: backendResponse.app_id,\n };\n })\n .catch((error) => {\n // Map all errors to structured TwoFactorSetupMagicLinkValidationResponse\n // This ensures consumers always get consistent error format\n if (error.response) {\n const status = error.response.status;\n const data = error.response.data || {};\n\n // Parse Retry-After header for rate limiting\n const retryAfter = error.response.headers?.['retry-after']\n ? parseInt(error.response.headers['retry-after'], 10)\n : undefined;\n\n return {\n success: false,\n error: {\n code: data.error || this.mapStatusToErrorCode(status),\n message: data.message || this.getDefaultErrorMessage(status),\n retryAfter,\n },\n };\n }\n\n // Network error or other unexpected error\n return {\n success: false,\n error: {\n code: 'SERVER_ERROR' as const,\n message: error instanceof Error ? error.message : 'Unable to connect to the server. Please check your connection.',\n },\n };\n });\n }\n\n /**\n * Map HTTP status code to magic link error code\n */\n private mapStatusToErrorCode(status: number): TwoFactorSetupMagicLinkErrorCode {\n switch (status) {\n case 400:\n return 'INVALID_TOKEN';\n case 404:\n return 'REVOKED_TOKEN';\n case 410:\n return 'EXPIRED_TOKEN';\n case 429:\n return 'RATE_LIMITED';\n default:\n return 'SERVER_ERROR';\n }\n }\n\n /**\n * Get default error message for HTTP status code\n */\n private getDefaultErrorMessage(status: number): string {\n switch (status) {\n case 400:\n return 'The provided magic link is invalid or malformed.';\n case 404:\n return 'This magic link has been revoked or does not exist.';\n case 410:\n return 'This magic link has expired. Please request a new one from your administrator.';\n case 429:\n return 'Too many validation attempts. Please try again later.';\n default:\n return 'An error occurred while validating the magic link.';\n }\n }\n\n // ============================================\n // v2 Multi-Method 2FA API Methods\n // ============================================\n\n /**\n * Get available 2FA methods for current user\n * GET /v2/user/2fa/methods/available\n */\n getAvailableMethods(): Promise<TwoFactorMethod[]> {\n return this.axiosClient.get<TwoFactorMethod[]>(PassflowEndpointPaths.TwoFactorMethodsAvailable);\n }\n\n /**\n * Get registered 2FA methods for current user\n * GET /v2/user/2fa/methods\n */\n getRegisteredMethods(): Promise<RegisteredTwoFactorMethod[]> {\n return this.axiosClient.get<RegisteredTwoFactorMethod[]>(PassflowEndpointPaths.TwoFactorMethodsRegistered);\n }\n\n /**\n * Start 2FA method setup.\n * POST /v2/user/2fa/setup/start\n *\n * The method travels in the body: at this point it is a parameter of the\n * request, not the identity of an enrolled resource.\n */\n startMethodSetup(method: TwoFactorMethod, payload?: Record<string, unknown>): Promise<unknown> {\n return this.axiosClient.post<unknown, Record<string, unknown>>(PassflowEndpointPaths.TwoFactorSetupStart, {\n ...payload,\n method,\n });\n }\n\n /**\n * Complete 2FA method setup.\n * POST /v2/user/2fa/setup/complete\n */\n completeMethodSetup(method: TwoFactorMethod, payload: Record<string, unknown>): Promise<unknown> {\n return this.axiosClient.post<unknown, Record<string, unknown>>(PassflowEndpointPaths.TwoFactorSetupComplete, {\n ...payload,\n method,\n });\n }\n\n /**\n * Remove registered 2FA method\n * DELETE /v2/user/2fa/methods/:id\n */\n removeMethod(methodId: string): Promise<void> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorMethodRemove, { id: methodId });\n return this.axiosClient.delete<void>(endpoint);\n }\n\n /**\n * Request 2FA challenge during login\n * POST /v2/auth/2fa/challenge\n * `tfaToken`, when given, goes as the Bearer token the route is gated on.\n */\n requestChallenge(payload: TwoFactorChallengeRequest, tfaToken?: string): Promise<TwoFactorChallengeResponse> {\n return this.axiosClient.post<TwoFactorChallengeResponse, TwoFactorChallengeRequest>(\n PassflowEndpointPaths.TwoFactorChallenge,\n payload,\n bearer(tfaToken),\n );\n }\n\n /**\n * Verify 2FA challenge (v2)\n * POST /v2/auth/2fa/verify (the server's verify-challenge)\n *\n * `tfaToken` is the 2FA token the first factor returned; the route is gated\n * on it, so it goes as the Bearer token when given.\n */\n verifyV2(payload: TwoFactorVerifyRequestV2, tfaToken?: string): Promise<TwoFactorVerifyResponseV2> {\n const { trust_device, ...rest } = payload;\n return this.axiosClient.post<TwoFactorVerifyResponseV2, TwoFactorVerifyRequestV2>(\n PassflowEndpointPaths.TwoFactorVerifyV2,\n { ...rest, ...(trust_device ? { trust_device: true } : {}) },\n credentialed(tfaToken),\n );\n }\n\n /**\n * Switch to alternative 2FA method during challenge\n * POST /v2/auth/2fa/alternative\n * `tfaToken`, when given, goes as the Bearer token the route is gated on.\n */\n switchToAlternative(payload: TwoFactorAlternativeRequest, tfaToken?: string): Promise<TwoFactorChallengeResponse> {\n return this.axiosClient.post<TwoFactorChallengeResponse, TwoFactorAlternativeRequest>(\n PassflowEndpointPaths.TwoFactorAlternative,\n payload,\n bearer(tfaToken),\n );\n }\n\n /**\n * List the devices the user trusted on this app\n * GET /v2/user/2fa/trusted-devices\n *\n * Sent with credentials so the server sees this device's trust cookie and\n * can mark it `current`.\n */\n async getTrustedDevices(): Promise<TwoFactorTrustedDevice[]> {\n const response = await this.axiosClient.get<TwoFactorTrustedDevicesResponse>(\n PassflowEndpointPaths.TwoFactorTrustedDevices,\n { withCredentials: true },\n );\n return response?.devices ?? [];\n }\n\n /**\n * Revoke trusted device\n * DELETE /v2/user/2fa/trusted-devices/:id\n * The server answers 404 when the user has no such device.\n */\n revokeTrustedDevice(deviceId: string): Promise<void> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorTrustedDeviceRevoke, { id: deviceId });\n return this.axiosClient.delete<void>(endpoint);\n }\n}\n","/**\n * The assurance-upgrade API — the prepare round of a step-up.\n *\n * One endpoint, deliberately. The commit round is `POST /auth/refresh`, which\n * belongs to `AuthAPI` because that is the only path that stores the rotated\n * tokens; splitting it off here would elevate the session on the server and\n * leave the client holding stale ones.\n *\n * Constructed exactly like `AuthAPI`, sharing `StorageManager` and\n * `DeviceService`, which is what makes cookie mode, CSRF and the device headers\n * work without duplicating any of it.\n */\n\nimport type { AxiosRequestConfig } from 'axios';\n\nimport type { DeviceService } from '../device';\nimport type { StorageManager } from '../storage';\nimport { markUpgradeTransaction } from '../upgrade/markers';\n\nimport { AxiosClient } from './axios-client';\nimport { type PassflowConfig, PassflowEndpointPaths, type UpgradeStartWire } from './model';\n\n/** The `POST /auth/upgrade/start` request body. `relying_party_id` is passkey-only. */\nexport type UpgradeStartPayload = {\n method: string;\n relying_party_id?: string;\n};\n\nexport class UpgradeAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n /**\n * Issues a single-use, session-bound upgrade challenge.\n *\n * Returns the raw snake_case body: normalisation is the transport's job, and\n * keeping it out of here means the wire shape has exactly one definition.\n *\n * The request carries the upgrade-transaction marker, so a 401 from this round\n * — a stale access token, a foreign session — is recognised as part of an\n * elevation by the response interceptor and does not invalidate the session\n * behind the user's back.\n */\n startUpgrade(payload: UpgradeStartPayload): Promise<UpgradeStartWire> {\n return this.axiosClient.post<UpgradeStartWire, UpgradeStartPayload>(\n PassflowEndpointPaths.upgradeStart,\n payload,\n markUpgradeTransaction<AxiosRequestConfig>({}),\n );\n }\n}\n","import { RegistrationResponseJSON } from '@simplewebauthn/types';\n\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\n\nimport { AxiosClient } from './axios-client';\nimport {\n OS,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowPasskeyRegisterPayload,\n PassflowPasskeyStart,\n PassflowSuccessResponse,\n PassflowUserPasskey,\n} from './model';\n\nexport class UserAPI {\n protected axiosClient: AxiosClient;\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n this.axiosClient = new AxiosClient(config, storageManager, deviceService);\n }\n\n setAppId(appId: string): void {\n this.axiosClient.setAppId(appId);\n }\n\n getUserPasskeys() {\n return this.axiosClient.get<PassflowUserPasskey[]>(PassflowEndpointPaths.userPasskey);\n }\n\n renameUserPasskey(name: string, passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.axiosClient.patch<PassflowSuccessResponse, { name: string }>(\n `${PassflowEndpointPaths.userPasskey}/${passkeyId}`,\n {\n name,\n },\n );\n }\n\n deleteUserPasskey(passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.axiosClient.delete<PassflowSuccessResponse>(`${PassflowEndpointPaths.userPasskey}/${passkeyId}`);\n }\n\n addUserPasskeyStart({\n relyingPartyId,\n deviceId,\n os,\n passkeyDisplayName,\n passkeyUsername,\n }: {\n relyingPartyId: string;\n deviceId: string;\n os: OS;\n passkeyDisplayName?: string;\n passkeyUsername?: string;\n }): Promise<PassflowPasskeyStart> {\n const payload = {\n passkey_display_name: passkeyDisplayName,\n passkey_username: passkeyUsername,\n relying_party_id: relyingPartyId,\n deviceId,\n os,\n };\n\n return this.axiosClient.post<PassflowPasskeyStart, typeof payload>(PassflowEndpointPaths.addUserPasskey, payload);\n }\n\n addUserPasskeyComplete(passkeyData: RegistrationResponseJSON, deviceId: string, challengeId: string): Promise<void> {\n return this.axiosClient.post<void, PassflowPasskeyRegisterPayload>(PassflowEndpointPaths.completeAddUserPasskey, {\n challenge_id: challengeId,\n device: deviceId,\n passkey_data: passkeyData,\n });\n }\n}\n","/**\n * RFC 7636 proof key: the verifier stays on this client, its S256 hash goes to\n * the server with the sign-in, and the server answers with a code that only the\n * verifier redeems.\n */\nexport type PkcePair = {\n verifier: string;\n challenge: string;\n};\n\nconst BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\n// 32 random bytes encode to 43 characters, the shortest verifier RFC 7636 §4.1 allows.\nconst VERIFIER_BYTES = 32;\n\n// Written out because `btoa` is not available on every platform this SDK runs on.\nconst base64UrlEncode = (bytes: Uint8Array): string => {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const a = bytes[i] as number;\n const b = bytes[i + 1];\n const c = bytes[i + 2];\n out += BASE64URL_ALPHABET.charAt(a >> 2);\n out += BASE64URL_ALPHABET.charAt(((a & 0x03) << 4) | ((b ?? 0) >> 4));\n if (b !== undefined) out += BASE64URL_ALPHABET.charAt(((b & 0x0f) << 2) | ((c ?? 0) >> 6));\n if (c !== undefined) out += BASE64URL_ALPHABET.charAt(c & 0x3f);\n }\n return out;\n};\n\n// `crypto.subtle` exists only in a secure context, so a plain-http page lands here.\nconst webCrypto = (): Crypto => {\n const crypto = globalThis.crypto;\n if (!crypto?.subtle) {\n throw new Error('PKCE needs the Web Crypto API, which is available only in a secure context (https or localhost)');\n }\n return crypto;\n};\n\n/** BASE64URL(SHA256(ASCII(verifier))), RFC 7636 §4.2. */\nexport const s256Challenge = async (verifier: string): Promise<string> => {\n const digest = await webCrypto().subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return base64UrlEncode(new Uint8Array(digest));\n};\n\nexport const createPkcePair = async (): Promise<PkcePair> => {\n const verifier = base64UrlEncode(webCrypto().getRandomValues(new Uint8Array(VERIFIER_BYTES)));\n return { verifier, challenge: await s256Challenge(verifier) };\n};\n","/**\n * Passflow Store\n *\n * Event subscription system using the Observer pattern.\n * Manages authentication event notifications to subscribers.\n * Supports filtered subscriptions for specific event types.\n *\n * @module store\n */\n\nimport type { TwoFactorMethod } from './api/model';\nimport { ParsedTokens, Tokens } from './types';\n\n/**\n * Passflow event types\n */\nexport enum PassflowEvent {\n SignIn = 'signin',\n SignInStart = 'signin:start',\n Register = 'register',\n RegisterStart = 'register:start',\n SignOut = 'signout',\n SessionRestored = 'session:restored',\n SessionExpired = 'session:expired',\n Error = 'error',\n Refresh = 'refresh',\n RefreshStart = 'refresh:start',\n TokenCacheExpired = 'token-cache-expired',\n TwoFactorRequired = '2fa:required',\n TwoFactorSetupStarted = '2fa:setup_started',\n TwoFactorEnabled = '2fa:enabled',\n TwoFactorDisabled = '2fa:disabled',\n TwoFactorVerified = '2fa:verified',\n TwoFactorRecoveryUsed = '2fa:recovery_used',\n TwoFactorRecoveryCodesLow = '2fa:recovery_low',\n TwoFactorRecoveryCodesExhausted = '2fa:recovery_exhausted',\n TwoFactorSetupMagicLinkValidated = '2fa:magic_link_validated',\n TwoFactorSetupMagicLinkFailed = '2fa:magic_link_failed',\n TwoFactorChallengeReceived = 'two_factor_challenge_received',\n TwoFactorMethodSwitched = 'two_factor_method_switched',\n TwoFactorDeviceTrusted = 'two_factor_device_trusted',\n}\n\n/**\n * Error payload interface for structured error information\n */\nexport interface ErrorPayload {\n message: string;\n code?: string | number;\n details?: unknown;\n originalError?: unknown;\n}\n\n/**\n * Event-specific payload types\n */\nexport type PassflowEventPayload = {\n [PassflowEvent.SignIn]: { tokens?: Tokens; parsedTokens?: ParsedTokens };\n [PassflowEvent.SignInStart]: { email?: string; provider?: string };\n [PassflowEvent.Register]: { tokens?: Tokens; parsedTokens?: ParsedTokens };\n [PassflowEvent.RegisterStart]: { email?: string };\n [PassflowEvent.SignOut]: { userId?: string };\n [PassflowEvent.SessionRestored]: { id: string; email?: string; username?: string; [key: string]: unknown };\n [PassflowEvent.SessionExpired]: { reason: 'refresh_failed' | 'token_invalid' | 'logged_out' };\n [PassflowEvent.Error]: ErrorPayload;\n [PassflowEvent.Refresh]: { tokens?: Tokens; parsedTokens?: ParsedTokens };\n [PassflowEvent.RefreshStart]: { tokenId?: string };\n [PassflowEvent.TokenCacheExpired]: { isExpired: boolean };\n [PassflowEvent.TwoFactorRequired]: {\n email: string;\n challengeId: string;\n tfaToken: string;\n };\n [PassflowEvent.TwoFactorSetupStarted]: { secret: string; method?: TwoFactorMethod };\n [PassflowEvent.TwoFactorEnabled]: { recoveryCodes: string[]; clearRecoveryCodes: () => void };\n [PassflowEvent.TwoFactorDisabled]: Record<string, never>;\n [PassflowEvent.TwoFactorVerified]: { tokens?: Tokens };\n [PassflowEvent.TwoFactorRecoveryUsed]: { tokens?: Tokens; remainingCodes: number };\n [PassflowEvent.TwoFactorRecoveryCodesLow]: { tokens?: Tokens; remainingCodes: number };\n [PassflowEvent.TwoFactorRecoveryCodesExhausted]: { tokens?: Tokens };\n [PassflowEvent.TwoFactorSetupMagicLinkValidated]: {\n userId: string;\n appId?: string | null;\n expiresIn: number;\n sessionToken: string;\n };\n [PassflowEvent.TwoFactorSetupMagicLinkFailed]: {\n error: {\n code: 'INVALID_TOKEN' | 'EXPIRED_TOKEN' | 'REVOKED_TOKEN' | 'RATE_LIMITED' | 'SERVER_ERROR';\n message: string;\n retryAfter?: number;\n };\n };\n [PassflowEvent.TwoFactorChallengeReceived]: {\n challengeId: string;\n method: TwoFactorMethod;\n alternativeMethods: TwoFactorMethod[];\n };\n [PassflowEvent.TwoFactorMethodSwitched]: {\n challengeId: string;\n method: TwoFactorMethod;\n alternativeMethods: TwoFactorMethod[];\n };\n [PassflowEvent.TwoFactorDeviceTrusted]: Record<string, never>;\n};\n\n/**\n * Passflow subscriber interface\n */\nexport interface PassflowSubscriber {\n onAuthChange<E extends PassflowEvent>(eventType: E, payload?: PassflowEventPayload[E]): void;\n}\n\n/**\n * Store for managing Passflow event subscriptions\n */\nexport class PassflowStore {\n private subscribers: Map<PassflowSubscriber, Set<PassflowEvent> | null> = new Map();\n\n /**\n * Subscribe to authentication events\n * @param subscriber The subscriber to register\n * @param events Optional specific events to subscribe to\n */\n subscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]): void {\n if (events?.length) {\n const eventSet = new Set<PassflowEvent>(events);\n this.subscribers.set(subscriber, eventSet);\n } else {\n this.subscribers.set(subscriber, null);\n }\n }\n\n /**\n * Unsubscribe from authentication events\n * @param subscriber The subscriber to unregister\n * @param events Optional specific events to unsubscribe from\n */\n unsubscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]): void {\n if (!events?.length) {\n this.subscribers.delete(subscriber);\n return;\n }\n\n const subscribedEvents = this.subscribers.get(subscriber);\n if (!subscribedEvents) {\n return;\n }\n\n events.forEach((event) => subscribedEvents.delete(event));\n if (subscribedEvents.size === 0) {\n this.subscribers.delete(subscriber);\n }\n }\n\n /**\n * Notify subscribers of an event\n * @param eventType The type of event that occurred\n * @param payload Event-specific payload data\n */\n notify<E extends PassflowEvent>(eventType: E, payload?: PassflowEventPayload[E]): void {\n this.subscribers.forEach((events, subscriber) => {\n if (!events || events.has(eventType)) {\n subscriber.onAuthChange?.(eventType, payload);\n }\n });\n }\n}\n","/**\n * Upgrade transport error classification.\n *\n * The server speaks two languages about the same failure: an exact\n * `PassflowError.id` and an HTTP status. The id is read FIRST and the status only\n * as a fallback, because the id names the cause while the status is a bucket —\n * `refreshErrorStatus` maps four unrelated upgrade errors onto one bare 401, and\n * three of them want the prompt kept open while the fourth ends the elevation.\n *\n * This module is pure: it imports nothing and touches no transport. It\n * duck-types whatever it is handed — a `PassflowError`, a raw `AxiosError`, or a\n * bare object — so it can be tested with literals and reused from any surface.\n */\n\n/** What kind of failure the server reported, in remedy terms rather than status terms. */\nexport type UpgradeErrorKind =\n /** The user typed the wrong code. Retry with a FRESH prepare round; the modal stays open. */\n | 'wrong_code'\n /** Bad, expired, spent or foreign challenge. Same remedy as `wrong_code`. */\n | 'challenge_spent'\n /** The server's per-challenge attempt budget is burned. Stop; a fresh prepare will not help. */\n | 'exhausted'\n /**\n * The CONTROLLER's local attempt budget is burned: the user has answered\n * wrongly `maxAttempts` times and the elevation gives up.\n *\n * Terminal, and distinct from the four retryable kinds for one specific\n * reason: the loop's last failure is usually a `wrong_code`, which IS\n * retryable, and returning that error as the flight's outcome told every\n * surface above \"ask them again\" for a flight that will never ask again — so\n * the UI re-prompted forever against a budget the server had already closed.\n * The budget being spent is the fact that must travel, not the last answer\n * that spent it; the answer travels on `cause`.\n */\n | 'attempts_exhausted'\n /** This FACTOR is not enrolled for this user. Drop it for this flight, offer the others. */\n | 'not_registered'\n /** Our static `requiresPrepare` disagrees with the server. Recover by asserting directly. */\n | 'prepare_not_required'\n /** Unsupported factor, or no session at all. Not retryable; surface it, do not log the user out. */\n | 'fatal'\n /** The deployment cannot serve an upgrade at all (no challenge storage, or a BFF commit). */\n | 'unavailable'\n /** A malformed response from a server that claimed success. */\n | 'protocol'\n /** No response: the request never completed. */\n | 'network'\n /**\n * A genuine session failure DURING the elevation — a commit 401 that is none of\n * the four recoverable upgrade errors, so it is an expired or revoked refresh\n * token rather than a bad answer. Terminates the elevation instead of\n * re-prompting, and the transport invalidates the session itself.\n */\n | 'session_changed';\n\n/** Server error ids this module classifies. Verified against `src/l/messages_const.go`. */\nexport const UpgradeErrorIds = {\n twoFactorCodeInvalid: 'error.api.request.2fa_code.invalid',\n challengeInvalid: 'error.upgrade.challenge_invalid',\n challengeSessionMismatch: 'error.upgrade.challenge_session_mismatch',\n challengeTooManyAttempts: 'error.upgrade.challenge_too_many_attempts',\n unsupportedFactor: 'error.upgrade.unsupported_factor',\n noSession: 'error.upgrade.no_session',\n unavailable: 'error.upgrade.unavailable',\n methodNotRegistered: 'error.upgrade.method_not_registered',\n prepareNotRequired: 'error.upgrade.prepare_not_required',\n methodNotSupported: 'error.2fa.method_not_supported',\n twoFactorNotEnabled: 'error.2fa.not_enabled',\n notImplemented: 'error.not.implemented',\n tokenInvalid: 'error.api.request.token.invalid',\n} as const;\n\n/**\n * **Every** error `refreshErrorStatus` maps to a 401 on the commit path, as\n * enumerated by `src/web/api/refresh_token.go:157-178`.\n *\n * All five are \"recoverable\" in the session sense: every one of them is the\n * upgrade protocol talking, so none of them means the session died. Note that\n * `challenge_too_many_attempts` is in here yet is NOT retryable — it ends the\n * elevation while leaving the session intact. The two axes are independent and\n * are deliberately not collapsed.\n *\n * `error.2fa.not_enabled` was missing from this list, which signed out a user\n * who had no 2FA enrolled the moment an elevation reached the commit round —\n * the exact lock-out PFL-497 exists to prevent. The set is kept complete for\n * documentation and for callers, but the invalidation decision no longer rests\n * on it: see `invalidatesSessionOnCommit`, which is the inverse predicate.\n */\nexport const RECOVERABLE_COMMIT_ERROR_IDS: readonly string[] = Object.freeze([\n UpgradeErrorIds.twoFactorCodeInvalid,\n UpgradeErrorIds.twoFactorNotEnabled,\n UpgradeErrorIds.challengeInvalid,\n UpgradeErrorIds.challengeSessionMismatch,\n UpgradeErrorIds.challengeTooManyAttempts,\n]);\n\n/**\n * Error ids that positively mean the SESSION is gone, not merely the elevation.\n *\n * This is the predicate the transport invalidates on, and it is deliberately a\n * POSITIVE list rather than the complement of the recoverable one. The server's\n * 401 set on the commit path is enumerated and small, so a list of \"ids that\n * mean the session died\" can be complete in a way that \"everything else\" never\n * is; and when a future server adds a sixth 401 id, an allow-list of expiries\n * fails toward \"I don't know\" rather than toward \"log them out\", which is this\n * feature's governing rule. A client-side miss costs a stale session that the\n * next ordinary 401 clears; a false positive costs the user their session in\n * the middle of proving who they are.\n */\nexport const SESSION_INVALIDATING_COMMIT_ERROR_IDS: readonly string[] = Object.freeze([UpgradeErrorIds.tokenInvalid]);\n\n/** Kinds the user can recover from by answering again, possibly with another factor. */\nconst RETRYABLE_KINDS: readonly UpgradeErrorKind[] = Object.freeze([\n 'wrong_code',\n 'challenge_spent',\n 'not_registered',\n 'prepare_not_required',\n]);\n\n/** True when a kind leaves the elevation alive. Single source for `retryable`. */\nexport const isRetryableUpgradeKind = (kind: UpgradeErrorKind): boolean => RETRYABLE_KINDS.includes(kind);\n\nexport type UpgradeTransportErrorInit = {\n kind: UpgradeErrorKind;\n message?: string;\n status?: number;\n /** The `PassflowError.id`, verbatim, when the server supplied one. */\n errorId?: string;\n cause?: unknown;\n};\n\n/** Which round of the transaction failed. The same status means different things on each. */\nexport type UpgradeEndpoint = 'start' | 'refresh';\n\nconst DEFAULT_MESSAGES: Record<UpgradeErrorKind, string> = {\n wrong_code: 'That code was not accepted. Try again.',\n challenge_spent: 'That verification challenge is no longer valid. Start again.',\n exhausted: 'Too many attempts. Try again later.',\n attempts_exhausted: 'Too many incorrect attempts. Verification was not completed.',\n not_registered: 'That verification method is not set up for this account.',\n prepare_not_required: 'This verification method needs no preparation round.',\n fatal: 'This verification method cannot be used right now.',\n unavailable: 'Step-up verification is not available on this deployment.',\n protocol: 'The server returned an unexpected upgrade response.',\n network: 'The verification request did not reach the server.',\n session_changed: 'The session ended during verification.',\n};\n\n/**\n * A classified transport failure.\n *\n * `code` is the literal, bundling-proof discriminant every upgrade error class\n * carries; the server's own id lives in `errorId`. (The design's §3.3 called the\n * latter `code`, which §13.3 then claimed for the literal tag — `errorId` keeps\n * both without one shadowing the other.)\n */\nexport class UpgradeTransportError extends Error {\n readonly name = 'UpgradeTransportError';\n readonly code = 'upgrade_transport' as const;\n readonly kind: UpgradeErrorKind;\n readonly status?: number;\n readonly errorId?: string;\n readonly retryable: boolean;\n /** Declared here rather than inherited: `Error.cause` is ES2022 and this build targets ES2021 libs. */\n readonly cause?: unknown;\n\n constructor(init: UpgradeTransportErrorInit) {\n super(init.message || DEFAULT_MESSAGES[init.kind]);\n this.kind = init.kind;\n this.status = init.status;\n this.errorId = init.errorId;\n this.retryable = isRetryableUpgradeKind(init.kind);\n if (init.cause !== undefined) {\n this.cause = init.cause;\n }\n }\n}\n\n/**\n * Every literal `code` an upgrade error class carries, including the two that\n * live in `controller.ts` (`upgrade_cancelled`, `upgrade_unsatisfiable`).\n *\n * The codes are listed rather than imported because this module imports nothing\n * — `controller.ts` imports it, so naming those classes here would be a cycle —\n * and because `code` is the discriminant that survives bundling, a duplicated\n * package copy and a cross-realm boundary, which is exactly the property a\n * pass-through guard needs.\n */\nconst UPGRADE_ERROR_CODES: readonly string[] = Object.freeze([\n 'upgrade_transport',\n 'upgrade_insufficient',\n 'upgrade_unavailable',\n 'upgrade_session_changed',\n 'upgrade_cancelled',\n 'upgrade_unsatisfiable',\n]);\n\n/**\n * Reports whether a value is one of the SDK's own upgrade error classes — an\n * outcome that has already been decided, on any surface.\n *\n * Used where a lower layer must not re-bucket a settled outcome: classifying an\n * `UpgradeSessionChangedError` as a bare network failure (which is what\n * happened, because it carries no `response`) turned §13.3's `session changed\n * mid-flight` row into `upgrade_transport` on all four adapters.\n */\nexport const isUpgradeSdkError = (e: unknown): e is Error & { code: string } =>\n typeof e === 'object' &&\n e !== null &&\n typeof (e as { code?: unknown }).code === 'string' &&\n UPGRADE_ERROR_CODES.includes((e as { code: string }).code);\n\n/** Reports whether a value is an already-classified transport failure. */\nexport const isUpgradeTransportError = (e: unknown): e is UpgradeTransportError =>\n e instanceof UpgradeTransportError ||\n (typeof e === 'object' && e !== null && (e as { code?: unknown }).code === 'upgrade_transport');\n\ntype ErrorFacts = { status?: number; errorId?: string; message?: string; hasResponse: boolean };\n\n/**\n * Pulls status, error id and message out of whatever the API layer rejected with.\n *\n * Three shapes reach here: a `PassflowError` (what `AxiosClient` normally\n * produces), a raw `AxiosError` whose body carries the Passflow envelope, and a\n * plain `Error` from a non-HTTP failure.\n */\nconst readErrorFacts = (e: unknown): ErrorFacts => {\n if (typeof e !== 'object' || e === null) {\n return { hasResponse: false, message: typeof e === 'string' ? e : undefined };\n }\n\n const candidate = e as {\n id?: unknown;\n status?: unknown;\n message?: unknown;\n response?: { status?: unknown; data?: unknown };\n };\n\n const response = candidate.response;\n const body = response?.data as { error?: { id?: unknown } } | undefined;\n const envelopeId = typeof body?.error?.id === 'string' ? body.error.id : undefined;\n\n const status =\n typeof candidate.status === 'number'\n ? candidate.status\n : typeof response?.status === 'number'\n ? response.status\n : undefined;\n\n return {\n status,\n errorId: typeof candidate.id === 'string' ? candidate.id : envelopeId,\n message: typeof candidate.message === 'string' ? candidate.message : undefined,\n hasResponse: response !== undefined || typeof candidate.status === 'number',\n };\n};\n\n/**\n * The id → kind table. `undefined` means \"this id says nothing; fall back to status\".\n *\n * `endpoint` is a parameter because one id genuinely means different things on\n * the two rounds — see `tokenInvalid` below. Everything else is round-independent.\n */\nconst kindFromErrorId = (errorId: string | undefined, endpoint: UpgradeEndpoint): UpgradeErrorKind | undefined => {\n switch (errorId) {\n case UpgradeErrorIds.twoFactorCodeInvalid:\n return 'wrong_code';\n case UpgradeErrorIds.challengeInvalid:\n case UpgradeErrorIds.challengeSessionMismatch:\n return 'challenge_spent';\n case UpgradeErrorIds.challengeTooManyAttempts:\n return 'exhausted';\n case UpgradeErrorIds.methodNotRegistered:\n return 'not_registered';\n // `error.2fa.not_enabled` is the same statement from the account's side —\n // the factor is not set up — and it is ROUND-DEPENDENT.\n //\n // On the PREPARE round it is the server refusing one factor while the\n // flight is still choosing, which §13.4 makes flight-local pruning plus a\n // re-prompt: retryable with another factor, never an expiry.\n //\n // On the COMMIT round C26c governs, and it is explicit: a commit 401\n // carrying any other id, `error.2fa.not_enabled` included, terminates the\n // elevation as `session changed mid-flight` while `setSessionInvalid()` is\n // NOT called and `SessionState` is untouched. Mapping it to\n // `not_registered` there made it retryable, so the controller pruned TOTP\n // and re-prompted against a commit that can only refuse again — the user\n // sat in front of a modal that never resolved. Terminal settlement with the\n // session left alone is the lock-out-free outcome the ticket exists for;\n // invalidation stays off because this id is not on\n // `SESSION_INVALIDATING_COMMIT_ERROR_IDS`.\n case UpgradeErrorIds.twoFactorNotEnabled:\n return endpoint === 'refresh' ? 'session_changed' : 'not_registered';\n case UpgradeErrorIds.prepareNotRequired:\n return 'prepare_not_required';\n case UpgradeErrorIds.unavailable:\n case UpgradeErrorIds.notImplemented:\n return 'unavailable';\n case UpgradeErrorIds.unsupportedFactor:\n case UpgradeErrorIds.noSession:\n case UpgradeErrorIds.methodNotSupported:\n return 'fatal';\n // The one round-dependent id. §13.4's table maps a `start` 401 carrying it\n // to `fatal`; §13.4 revision 5 specifies the COMMIT round's occurrence as\n // *session changed mid-flight* — it is the single id on the positive\n // allow-list `invalidatesSessionOnCommit` reads, so classifying it `fatal`\n // on that round contradicted the predicate that acts on it.\n case UpgradeErrorIds.tokenInvalid:\n return endpoint === 'refresh' ? 'session_changed' : 'fatal';\n default:\n // An id we do not recognise — the normal case against a newer server.\n // `undefined` hands the decision to the status fallback.\n return undefined;\n }\n};\n\n/** The status → kind fallback, which differs per round. */\nconst kindFromStatus = (status: number | undefined, endpoint: UpgradeEndpoint): UpgradeErrorKind => {\n if (status === undefined) {\n return 'network';\n }\n if (status === 501) {\n return 'unavailable';\n }\n if (status === 400) {\n return 'fatal';\n }\n if (status === 401 || status === 403) {\n // On the prepare round an unrecognised 401 is the challenge going stale, and\n // a fresh prepare fixes it. On the commit round an unrecognised id is a\n // server this SDK does not know: the five ids `refreshErrorStatus` can emit\n // as a 401 are all mapped by `kindFromErrorId`, so nothing enumerated\n // reaches here. The elevation ends — there is no sane way to continue it —\n // but the SESSION does not: `invalidatesSessionOnCommit` is a positive list\n // and says nothing about an id it has never seen.\n return endpoint === 'start' ? 'challenge_spent' : 'session_changed';\n }\n if (status >= 500) {\n return 'network';\n }\n return 'fatal';\n};\n\n/**\n * Classifies a rejection from either upgrade round into an `UpgradeTransportError`.\n *\n * Already-classified errors pass through unchanged, so a nested call cannot\n * re-bucket a `prepare_not_required` into something terminal.\n */\nexport const classifyUpgradeError = (e: unknown, endpoint: UpgradeEndpoint): UpgradeTransportError => {\n if (e instanceof UpgradeTransportError) {\n return e;\n }\n\n const facts = readErrorFacts(e);\n if (!facts.hasResponse) {\n return new UpgradeTransportError({ kind: 'network', message: facts.message, cause: e });\n }\n\n const kind = kindFromErrorId(facts.errorId, endpoint) ?? kindFromStatus(facts.status, endpoint);\n\n return new UpgradeTransportError({\n kind,\n message: facts.message,\n status: facts.status,\n errorId: facts.errorId,\n cause: e,\n });\n};\n\n/**\n * Reports whether a classified commit failure is a KNOWN upgrade-protocol error\n * that leaves the session intact.\n *\n * Informational: it answers \"is this one of the server's enumerated commit\n * 401s\". It is NOT the invalidation decision — `invalidatesSessionOnCommit` is,\n * and an unrecognised id is not an expiry just because this list has not heard\n * of it.\n */\nexport const isRecoverableCommitFailure = (error: UpgradeTransportError): boolean =>\n error.errorId !== undefined && RECOVERABLE_COMMIT_ERROR_IDS.includes(error.errorId);\n\n/**\n * Reports whether a classified commit failure means the SESSION has ended.\n *\n * The one predicate the transport invalidates on, and the inverse of what this\n * module used to ask. `undefined` — no id at all — is explicitly NOT an expiry:\n * a 401 the SDK cannot explain is \"I don't know\", and \"I don't know\" must never\n * resolve to signing the user out mid-verification.\n */\nexport const invalidatesSessionOnCommit = (error: UpgradeTransportError): boolean =>\n error.errorId !== undefined && SESSION_INVALIDATING_COMMIT_ERROR_IDS.includes(error.errorId);\n\n/* ────────────────────────────────────────────────────────────────────────────\n * The remaining §13.3 error classes.\n *\n * Every upgrade error carries a literal `code` discriminant as well as being\n * `instanceof`-checkable, because `code` is what survives bundling, a duplicated\n * package copy and a cross-realm boundary — the three situations in which an\n * `instanceof` check quietly returns `false` on a genuine failure.\n *\n * They live beside `UpgradeTransportError` rather than in an adapter so that the\n * axios and imperative surfaces can be pure translation: the core decides which\n * class an outcome maps to (`coreOutcomeToError`), the adapter only throws it.\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** The bar a challenge demanded, or an elevation reached, on both axes. */\nexport type AssuranceBar = {\n /** The raw winning `acr_values` list, for display and diagnostics. */\n acrValues?: string;\n /** The parsed bar. `undefined` = unknown, or not constrained on this axis. */\n level?: number;\n /** The freshness constraint, when that is what was demanded. */\n maxAge?: number;\n};\n\n/**\n * The elevation completed, and the protected operation's replay was challenged\n * again: the proof was real but did not reach this operation's bar.\n *\n * This is the outcome that makes the AAL3-with-only-TOTP path honest. It is\n * per-operation, never per-proof — a requestless `passflow.upgrade()` with no\n * stated bar can never produce it, because it has nothing to replay.\n */\nexport class UpgradeInsufficientError extends Error {\n readonly name = 'UpgradeInsufficientError';\n readonly code = 'upgrade_insufficient' as const;\n readonly obtained: AssuranceBar;\n readonly required: AssuranceBar;\n\n constructor(obtained: AssuranceBar, required: AssuranceBar, message?: string) {\n super(message ?? 'The session was elevated, but this action requires a higher assurance level');\n this.obtained = obtained;\n this.required = required;\n }\n}\n\n/** The deployment cannot serve an upgrade at all — no challenge storage, or a BFF commit. */\nexport class UpgradeUnavailableError extends Error {\n readonly name = 'UpgradeUnavailableError';\n readonly code = 'upgrade_unavailable' as const;\n\n constructor(message = 'Step-up verification is not available on this deployment') {\n super(message);\n }\n}\n\n/** The session ended or changed while the elevation was running (§7.4). */\nexport class UpgradeSessionChangedError extends Error {\n readonly name = 'UpgradeSessionChangedError';\n readonly code = 'upgrade_session_changed' as const;\n\n constructor(message = 'The session changed during verification') {\n super(message);\n }\n}\n","/**\n * Session and user identity pinning for an upgrade flight (§13.4 revision 6).\n *\n * ## The rule\n *\n * An upgrade flight is bound to the **session and user identity it began\n * under**, and that identity is verified **at the moment tokens are applied** —\n * not only around `prepare` and `commit`. On mismatch the rotated tokens are\n * discarded, never applied, and the flight settles as `session changed\n * mid-flight`.\n *\n * ## Why the epoch counter was not enough\n *\n * The round-1 epoch check wraps the two rounds that talk to the server. It does\n * not wrap token APPLICATION, which happens inside `AuthService.refreshToken`\n * after the commit returns. A logout and re-login during an in-flight upgrade\n * therefore let user A's flight write its rotated tokens over user B's live\n * session: the request was authorized as A, the storage belongs to B, and\n * nothing in between compared the two principals.\n *\n * An epoch tells you something changed. An identity check tells you **whether\n * it is still the same principal**, which is the property actually required.\n * Both run, and they are complementary rather than redundant.\n *\n * ## Why this module is pure\n *\n * It imports nothing and reads no storage, so the token-applying path can be\n * given a pin by value and the comparison can be tested with literals. Reading\n * the identity is the host's job — it is the only code that knows where the\n * session lives in each delivery mode.\n */\n\n/**\n * The principal an upgrade flight is pinned to.\n *\n * `sub` is the user. `sid` is the session when the server issues one; it is\n * absent in tokens that carry no session claim, and the field is then simply\n * not part of the comparison — a missing claim cannot be made to prove\n * anything, and inventing a value would make two different sessions compare\n * equal or unequal arbitrarily.\n */\nexport type UpgradeSessionPin = {\n readonly sub: string;\n readonly sid?: string;\n};\n\n/** Claims the pin is read from. Deliberately `unknown`-typed: it is a parsed JWT body. */\nexport type UpgradeSessionClaims = { sub?: unknown; sid?: unknown } | null | undefined;\n\n/**\n * Reads a pin from token claims, or `undefined` when there is no identity to\n * pin to.\n *\n * `sub` is mandatory: without a user there is no principal, and a pin that\n * compares nothing would report \"unchanged\" for every change.\n */\nexport const readSessionPin = (claims: UpgradeSessionClaims): UpgradeSessionPin | undefined => {\n if (!claims || typeof claims.sub !== 'string' || claims.sub === '') {\n return undefined;\n }\n return typeof claims.sid === 'string' && claims.sid !== '' ? { sub: claims.sub, sid: claims.sid } : { sub: claims.sub };\n};\n\n/**\n * Is `current` the same principal the flight was pinned to?\n *\n * `false` when `current` is absent: a pin exists, so the identity WAS readable\n * when the flight began, and an identity that has since become unreadable is a\n * change — the session was torn down. That is fail-closed on token\n * APPLICATION (nothing is written) and still fail-open on session STATE\n * (nothing is invalidated, nobody is logged out). §13.4 revision 6 keeps those\n * two axes separate on purpose.\n *\n * When both sides carry a `sid` it must match as well, which is what catches a\n * logout and re-login as the SAME user.\n */\nexport const sessionPinMatches = (pin: UpgradeSessionPin, current: UpgradeSessionPin | undefined): boolean => {\n if (!current || current.sub !== pin.sub) {\n return false;\n }\n if (pin.sid !== undefined && current.sid !== undefined) {\n return pin.sid === current.sid;\n }\n return true;\n};\n","import { ERROR_MESSAGE_MAX_LENGTH, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH } from '../constants';\n\n/**\n * Validates JWT token format (not signature, just structure)\n * Checks that the token has 3 parts separated by dots and each part is valid base64url\n *\n * @param token - The token string to validate\n * @returns true if the token has valid JWT format, false otherwise\n */\nexport function isValidJWTFormat(token: string): boolean {\n if (!token || typeof token !== 'string') return false;\n\n const parts = token.split('.');\n if (parts.length !== 3) return false;\n\n // Check each part is valid base64url (alphanumeric, underscore, hyphen only)\n const base64UrlPattern = /^[A-Za-z0-9_-]+$/;\n return parts.every((part) => base64UrlPattern.test(part) && part.length > 0);\n}\n\n/**\n * Sanitizes error messages from URL parameters to prevent XSS\n * Removes HTML tags and limits length\n *\n * @param message - The error message to sanitize\n * @returns Sanitized error message\n */\nexport function sanitizeErrorMessage(message: string): string {\n // Remove any HTML tags\n const cleaned = message.replace(/<[^>]*>/g, '');\n // Limit length to prevent excessively long error messages\n return cleaned.substring(0, ERROR_MESSAGE_MAX_LENGTH);\n}\n\n/**\n * Validates email format using RFC 5322 simplified pattern\n *\n * @param email - The email string to validate\n * @returns true if valid email format, false otherwise\n */\nexport function isValidEmail(email: string): boolean {\n if (!email || typeof email !== 'string') return false;\n\n const trimmed = email.trim();\n if (trimmed.length === 0) return false;\n\n // RFC 5322 simplified pattern\n const emailPattern = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailPattern.test(trimmed);\n}\n\n/**\n * Validates phone number format (E.164 format)\n *\n * @param phone - The phone number string to validate\n * @returns true if valid phone format, false otherwise\n */\nexport function isValidPhoneNumber(phone: string): boolean {\n if (!phone || typeof phone !== 'string') return false;\n\n const trimmed = phone.trim();\n\n // E.164 format: +[country code][number] (1-15 digits after +)\n const phonePattern = /^\\+[1-9]\\d{1,14}$/;\n return phonePattern.test(trimmed);\n}\n\n/**\n * Validates username format\n *\n * @param username - The username string to validate\n * @returns true if valid username format, false otherwise\n */\nexport function isValidUsername(username: string): boolean {\n if (!username || typeof username !== 'string') return false;\n\n const trimmed = username.trim();\n if (trimmed.length < USERNAME_MIN_LENGTH || trimmed.length > USERNAME_MAX_LENGTH) return false;\n\n // Alphanumeric, underscore, hyphen only\n const usernamePattern = /^[a-zA-Z0-9_-]+$/;\n return usernamePattern.test(trimmed);\n}\n\n/**\n * Validates TOTP code format (6 or 8 numeric digits)\n *\n * @param code - The TOTP code to validate\n * @param digits - Expected number of digits (6 or 8, defaults to 6 for backward compatibility)\n * @returns true if valid TOTP code format, false otherwise\n */\nexport function isValidTOTPCode(code: string, digits: 6 | 8 = 6): boolean {\n if (!code || typeof code !== 'string') return false;\n\n // TOTP codes must be exactly 6 or 8 numeric digits\n const pattern = digits === 8 ? /^\\d{8}$/ : /^\\d{6}$/;\n return pattern.test(code);\n}\n\n/**\n * Normalizes and validates recovery code format\n * Converts to uppercase and removes whitespace\n *\n * @param code - The recovery code to normalize and validate\n * @returns Normalized recovery code if valid, null if invalid\n */\nexport function normalizeRecoveryCode(code: string): string | null {\n if (!code || typeof code !== 'string') return null;\n\n // Normalize: uppercase and remove all whitespace\n const normalized = code.toUpperCase().replace(/\\s+/g, '');\n\n // Basic validation: must be alphanumeric\n // Accept reasonable lengths (4-16 chars) to allow API to do final validation\n // This allows formats like \"ABCD1234\", \"ABCD-1234\", or even \"INVALID\" for testing\n const recoveryPattern = /^[A-Z0-9-]{4,16}$/;\n\n if (!recoveryPattern.test(normalized)) return null;\n\n return normalized;\n}\n","/**\n * Logger interface for Passflow services\n */\nexport interface Logger {\n error(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\n\n/**\n * Default console logger implementation\n */\nexport class ConsoleLogger implements Logger {\n error(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.error(message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.warn(message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.info(message, ...args);\n }\n\n debug(message: string, ...args: unknown[]): void {\n // biome-ignore lint/suspicious/noConsole: <explanation>\n console.debug(message, ...args);\n }\n}\n\n/**\n * Get the default logger\n * @returns Default logger instance\n */\nexport function getDefaultLogger(): Logger {\n return new ConsoleLogger();\n}\n","import axios from 'axios';\nimport {\n AuthAPI,\n PassflowAuthorizationResponse,\n PassflowError,\n PassflowFederatedAuthExtendedPayload,\n PassflowFederatedAuthPayload,\n PassflowPasskeyAuthenticateStartPayload,\n PassflowPasskeyRegisterStartPayload,\n PassflowPasswordlessResponse,\n PassflowPasswordlessSignInCompletePayload,\n PassflowPasswordlessSignInPayload,\n PassflowSendPasswordResetEmailPayload,\n PassflowSignInPayload,\n PassflowSignUpPayload,\n PassflowSuccessResponse,\n PassflowValidationResponse,\n TokenExchangeConfig,\n type UpgradeRefreshField,\n getOSFromDeviceType,\n} from '../api';\nimport { DeviceService } from '../device';\nimport { type PkcePair, createPkcePair } from '../pkce';\nimport { type PlatformAdapter, PopupClosedError } from '../platform';\nimport { webAdapter } from '../platform';\nimport { StorageManager } from '../storage';\nimport { ErrorPayload, PassflowEvent, PassflowStore } from '../store';\nimport { TokenType, isTokenExpired, parseToken } from '../token';\nimport { TokenDeliveryManager, TokenDeliveryMode } from '../token/delivery-manager';\nimport { ParsedTokens, Tokens } from '../types';\nimport { UpgradeSessionChangedError, UpgradeTransportError } from '../upgrade/errors';\nimport { type UpgradeSessionPin, readSessionPin, sessionPinMatches } from '../upgrade/session';\nimport { isValidEmail, isValidPhoneNumber, isValidUsername } from '../utils/validation';\nimport { getDefaultLogger } from './logger';\nimport { TokenCacheService } from './token-cache-service';\n\n/** The proof key of a PKCE sign-in that is waiting on its second factor. */\ntype PendingPkceSignIn = {\n verifier: string;\n scopes?: string[];\n expiresAt: number;\n};\n\nconst PENDING_PKCE_STORAGE_KEY = 'passflow_2fa_pkce';\n/** As long as the pending second factor it belongs to. */\nconst PENDING_PKCE_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Service for handling authentication related functionality\n */\nexport class AuthService {\n private tokenDeliveryManager: TokenDeliveryManager;\n private tokenExchangeConfig?: TokenExchangeConfig;\n private pendingPkce?: PendingPkceSignIn;\n\n constructor(\n private authApi: AuthAPI,\n private deviceService: DeviceService,\n private storageManager: StorageManager,\n private subscribeStore: PassflowStore,\n private tokenCacheService: TokenCacheService,\n private scopes: string[],\n private createTenantForNewUser: boolean,\n private url: string,\n private sessionCallbacks: {\n createSession?: ({ tokens, parsedTokens }: { tokens?: Tokens; parsedTokens?: ParsedTokens }) => Promise<void>;\n expiredSession?: () => Promise<void>;\n },\n private appId?: string,\n tokenExchangeConfig?: TokenExchangeConfig,\n private platform: PlatformAdapter = webAdapter,\n private readPkceEnabled?: () => Promise<boolean>,\n ) {\n this.tokenExchangeConfig = tokenExchangeConfig;\n this.tokenDeliveryManager = new TokenDeliveryManager(storageManager);\n\n // If token exchange is enabled, set BFF mode immediately\n if (tokenExchangeConfig?.enabled) {\n this.tokenDeliveryManager.setMode(TokenDeliveryMode.BFF);\n }\n\n // Initialize session state on page load (cookie/BFF mode only)\n this.initializeSession();\n }\n\n /**\n * Initialize session state on page load for cookie/BFF mode\n */\n private async initializeSession(): Promise<void> {\n if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {\n // Cookie/BFF mode: validate session with server\n await this.restoreSession();\n }\n }\n\n /**\n * Restore session for cookie/BFF mode on page load\n * Validates that HttpOnly cookies are still valid\n * @returns true if session is valid, false otherwise\n */\n async restoreSession(): Promise<boolean> {\n // BFF mode: validate session with BFF endpoint\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.statusUrl) {\n try {\n const response = await fetch(this.tokenExchangeConfig.statusUrl, {\n method: 'GET',\n credentials: 'include', // Include httpOnly cookies\n });\n\n if (response.ok) {\n const data = await response.json();\n if (data.authenticated) {\n this.tokenDeliveryManager.setSessionValid();\n return true;\n }\n }\n\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n } catch (_error) {\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n }\n }\n\n if (!this.tokenDeliveryManager.isCookieMode()) {\n return false; // Only applicable to cookie mode\n }\n\n try {\n // Call lightweight endpoint to validate session\n // This uses the HttpOnly cookies automatically\n const response = await this.authApi.validateSession();\n\n if (response.valid) {\n this.tokenDeliveryManager.setSessionValid();\n\n // If response includes user info, emit event\n if (response.user) {\n this.subscribeStore.notify(PassflowEvent.SessionRestored, response.user);\n }\n\n return true;\n } else {\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n }\n } catch (_error) {\n // Session invalid or network error\n this.tokenDeliveryManager.setSessionInvalid();\n return false;\n }\n }\n\n /**\n * Process successful authentication response\n * Handles token storage, session state, CSRF tokens\n * In BFF mode, forwards tokens to BFF server\n */\n private async processAuthResponse(response: PassflowAuthorizationResponse, scopes: string[]): Promise<void> {\n if (response.code && !response.access_token) {\n throw new Error('A PKCE authorization code was not exchanged, so there are no tokens to apply');\n }\n\n // BFF mode overrides server-returned delivery mode\n if (this.tokenExchangeConfig?.enabled) {\n // Don't change mode from BFF\n } else if ('token_delivery' in response && response.token_delivery) {\n // Detect and update delivery mode from server\n this.tokenDeliveryManager.setMode(response.token_delivery as TokenDeliveryMode);\n }\n\n // Mark session as valid after successful auth\n this.tokenDeliveryManager.setSessionValid();\n\n // BFF mode: forward tokens to BFF server\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.callbackUrl) {\n await this.forwardTokensToBFF(response);\n }\n\n // Save tokens (conditional based on delivery mode)\n // In BFF mode, only ID token is saved locally\n response.scopes = scopes;\n this.storageManager.saveTokens(response, this.tokenDeliveryManager.getMode());\n this.tokenCacheService.setTokensCache(response);\n\n // Store CSRF token if present (cookie mode)\n if (response.csrf_token) {\n this.storageManager.setCsrfToken(response.csrf_token);\n }\n }\n\n /**\n * Reports whether a sign-in response is still waiting on its second factor,\n * and when it is, hands the 2FA token on.\n *\n * Every sign-in path can end this way: the server answers with a\n * `tfa_token` (or `requires_2fa`) in place of tokens. That response is not\n * a session, so on `true` the caller returns it as it is — without\n * `processAuthResponse`, a `SignIn`/`Register` event or a session check.\n * The `TwoFactorRequired` event is what the TwoFactorService listens to, so\n * its verify and recovery calls can carry the token. On a PKCE sign-in the\n * server keeps the login's challenge, so the second factor's answer is a code:\n * the verifier stays here for `completePkceSignIn`, off the public event.\n */\n private handlePendingSecondFactor(\n response: Pick<PassflowAuthorizationResponse, 'requires_2fa' | 'challenge_id' | 'tfa_token'>,\n email?: string,\n pkce?: PkcePair,\n scopes?: string[],\n ): boolean {\n if (response.requires_2fa !== true && !response.tfa_token) {\n return false;\n }\n if (pkce) {\n this.rememberPkceSignIn(pkce.verifier, scopes);\n } else {\n this.forgetPkceSignIn();\n }\n this.subscribeStore.notify(PassflowEvent.TwoFactorRequired, {\n email: email || '',\n challengeId: response.challenge_id || '',\n tfaToken: response.tfa_token || '',\n });\n return true;\n }\n\n /**\n * Keeps the verifier in memory, which is where it is read from first. The copy\n * in sessionStorage is only for a redirect or reload during the second factor,\n * which would otherwise lose the sign-in.\n */\n private rememberPkceSignIn(verifier: string, scopes?: string[]): void {\n this.pendingPkce = { verifier, scopes, expiresAt: Date.now() + PENDING_PKCE_TIMEOUT_MS };\n if (typeof sessionStorage === 'undefined') {\n return;\n }\n try {\n sessionStorage.setItem(PENDING_PKCE_STORAGE_KEY, JSON.stringify(this.pendingPkce));\n } catch {\n // Disabled or full: the sign-in still completes unless the page goes away.\n }\n }\n\n private readPendingPkce(): PendingPkceSignIn | undefined {\n if (!this.pendingPkce && typeof sessionStorage !== 'undefined') {\n try {\n const stored = sessionStorage.getItem(PENDING_PKCE_STORAGE_KEY);\n this.pendingPkce = stored ? (JSON.parse(stored) as PendingPkceSignIn) : undefined;\n } catch {\n this.pendingPkce = undefined;\n }\n }\n if (this.pendingPkce && Date.now() >= this.pendingPkce.expiresAt) {\n this.forgetPkceSignIn();\n }\n return this.pendingPkce;\n }\n\n /** Whether a PKCE sign-in is waiting for the code its second factor answers with. */\n hasPendingPkceSignIn(): boolean {\n return this.readPendingPkce() !== undefined;\n }\n\n /** Drops the verifier of a pending sign-in. `TwoFactorService` calls this with its own state. */\n forgetPkceSignIn(): void {\n this.pendingPkce = undefined;\n if (typeof sessionStorage === 'undefined') {\n return;\n }\n try {\n sessionStorage.removeItem(PENDING_PKCE_STORAGE_KEY);\n } catch {\n // Nothing to do: it expires with the second factor.\n }\n }\n\n /**\n * The proof key for a sign-in on a PKCE app, undefined on any other app.\n *\n * It is sent whether or not the sign-in has a parent: the parent is added to\n * the request outside this SDK, so it is not visible here, and `pkce.Challenge`\n * on the server wants it under a device parent and ignores it under an OIDC one.\n * When the setting cannot be read the sign-in goes out without a challenge, and\n * a PKCE app refuses it with its own error.\n */\n private async startPkce(): Promise<PkcePair | undefined> {\n let enabled = false;\n try {\n enabled = (await this.readPkceEnabled?.()) ?? false;\n } catch (error) {\n getDefaultLogger().warn('Passflow: could not read the app settings, so this sign-in sends no PKCE challenge', error);\n return undefined;\n }\n return enabled ? createPkcePair() : undefined;\n }\n\n /**\n * Swaps a code in a sign-in's answer for what the code stands for; any other\n * answer passes through. That can still be a 2FA token: the server re-runs its\n * gate at the exchange unless the code records a passed second factor.\n */\n private async exchangeCode(\n response: PassflowAuthorizationResponse,\n pkce: PkcePair | undefined,\n ): Promise<PassflowAuthorizationResponse> {\n if (!response.code || !pkce) {\n return response;\n }\n return await this.authApi.pkceExchange(response.code, pkce.verifier);\n }\n\n /**\n * Finishes a PKCE sign-in whose code came from its second factor: redeems the\n * code with the first factor's verifier and applies the tokens. `TwoFactorService`\n * is the caller, and clears the pending state once this has succeeded.\n */\n async completePkceSignIn(code: string): Promise<PassflowAuthorizationResponse> {\n const pending = this.readPendingPkce();\n if (!pending) {\n throw new Error('The sign-in answered with a PKCE code, but its verifier is gone');\n }\n const tokens = await this.authApi.pkceExchange(code, pending.verifier);\n await this.processAuthResponse(tokens, pending.scopes ?? this.scopes);\n return tokens;\n }\n\n /**\n * Applies tokens that were issued outside this service, honouring the delivery\n * mode: this is the one place they reach the BFF and browser storage, so no\n * caller saves them itself. An answer that carries no tokens is left alone.\n */\n async applyTokens(tokens: Tokens): Promise<void> {\n const response = tokens as PassflowAuthorizationResponse;\n if (!response.access_token && !response.id_token && !response.token_delivery) {\n return;\n }\n await this.processAuthResponse(response, response.scopes ?? this.readPendingPkce()?.scopes ?? this.scopes);\n }\n\n /**\n * Forward tokens to BFF server for httpOnly cookie storage\n */\n private async forwardTokensToBFF(tokens: PassflowAuthorizationResponse): Promise<void> {\n if (!this.tokenExchangeConfig?.callbackUrl) {\n return;\n }\n const response = await fetch(this.tokenExchangeConfig.callbackUrl, {\n method: 'POST',\n credentials: 'include', // Include/set httpOnly cookies\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n access_token: tokens.access_token,\n refresh_token: tokens.refresh_token,\n id_token: tokens.id_token,\n // expires_in is returned by the server but not typed in the SDK\n expires_in: (tokens as Record<string, unknown>).expires_in,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`BFF token storage failed: ${response.status}`);\n }\n }\n\n async signIn(payload: PassflowSignInPayload): Promise<PassflowAuthorizationResponse> {\n // Validate input before API call\n if ('email' in payload && payload.email) {\n if (!isValidEmail(payload.email)) {\n const error = new Error('Invalid email format');\n const errorPayload: ErrorPayload = {\n message: 'Invalid email format',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n if ('username' in payload && payload.username) {\n if (!isValidUsername(payload.username)) {\n const error = new Error(\n 'Invalid username format. Username must be 3-30 characters and contain only letters, numbers, underscores, and hyphens',\n );\n const errorPayload: ErrorPayload = {\n message:\n 'Invalid username format. Username must be 3-30 characters and contain only letters, numbers, underscores, and hyphens',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n if ('phone' in payload && payload.phone) {\n if (!isValidPhoneNumber(payload.phone)) {\n const error = new Error('Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)');\n const errorPayload: ErrorPayload = {\n message: 'Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n this.subscribeStore.notify(PassflowEvent.SignInStart, { email: payload.email });\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n payload.scopes = payload.scopes ?? this.scopes;\n\n try {\n const pkce = await this.startPkce();\n const answer = await this.authApi.signIn(pkce ? { ...payload, code_challenge: pkce.challenge } : payload, deviceId, os);\n if (this.handlePendingSecondFactor(answer, payload.email, pkce, payload.scopes)) {\n return answer;\n }\n\n const response = await this.exchangeCode(answer, pkce);\n if (this.handlePendingSecondFactor(response, payload.email)) {\n return response;\n }\n\n await this.processAuthResponse(response, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Sign in failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async signUp(payload: PassflowSignUpPayload): Promise<PassflowAuthorizationResponse> {\n // Validate user input before API call\n if (payload.user.email && !isValidEmail(payload.user.email)) {\n const error = new Error('Invalid email format');\n const errorPayload: ErrorPayload = {\n message: 'Invalid email format',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n if (payload.user.phone_number && !isValidPhoneNumber(payload.user.phone_number)) {\n const error = new Error('Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)');\n const errorPayload: ErrorPayload = {\n message: 'Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n this.subscribeStore.notify(PassflowEvent.RegisterStart, { email: payload.user.email });\n payload.scopes = payload.scopes ?? this.scopes;\n payload.create_tenant = this.createTenantForNewUser;\n\n try {\n const pkce = await this.startPkce();\n const answer = await this.authApi.signUp(pkce ? { ...payload, code_challenge: pkce.challenge } : payload);\n if (this.handlePendingSecondFactor(answer, payload.user.email, pkce, payload.scopes)) {\n return answer;\n }\n\n const response = await this.exchangeCode(answer, pkce);\n if (this.handlePendingSecondFactor(response, payload.user.email)) {\n return response;\n }\n\n await this.processAuthResponse(response, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.Register, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Sign up failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passwordlessSignIn(payload: PassflowPasswordlessSignInPayload): Promise<PassflowPasswordlessResponse> {\n // Validate input before API call\n if (payload.email && !isValidEmail(payload.email)) {\n const error = new Error('Invalid email format');\n const errorPayload: ErrorPayload = {\n message: 'Invalid email format',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n if (payload.phone && !isValidPhoneNumber(payload.phone)) {\n const error = new Error('Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)');\n const errorPayload: ErrorPayload = {\n message: 'Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)',\n originalError: error,\n code: 'VALIDATION_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n this.subscribeStore.notify(PassflowEvent.SignInStart, { email: payload.email });\n payload.scopes = payload.scopes ?? this.scopes;\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n\n try {\n const response = await this.authApi.passwordlessSignIn(payload, deviceId, os);\n // Don't emit SignIn event yet since this is just the first step (magic link sent)\n // We'll emit SignIn when passwordlessSignInComplete is called\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to send passwordless sign-in link',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passwordlessSignInComplete(payload: PassflowPasswordlessSignInCompletePayload): Promise<PassflowValidationResponse> {\n this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n payload.scopes = payload.scopes ?? this.scopes;\n payload.device = this.deviceService.getDeviceId();\n\n try {\n const response = await this.authApi.passwordlessSignInComplete(payload);\n if (this.handlePendingSecondFactor(response)) {\n return response;\n }\n\n await this.processAuthResponse(response, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Passwordless sign in failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async logOut() {\n // BFF mode: call BFF logout endpoint to clear httpOnly cookies\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.logoutUrl) {\n try {\n const response = await fetch(this.tokenExchangeConfig.logoutUrl, {\n method: 'POST',\n credentials: 'include', // Include httpOnly cookies\n });\n\n if (!response.ok) {\n // Logout endpoint returned error - ignore and continue with local cleanup\n // Server-side session cleanup is best-effort, local cleanup is critical\n }\n } catch (_error) {\n // Network error during logout - ignore and continue with local cleanup\n // User experience is better if we silently handle logout failures\n }\n } else {\n // Regular mode: call passflow logout API\n const refreshToken = this.storageManager.getToken(TokenType.refresh_token);\n const deviceId = this.storageManager.getDeviceId();\n\n try {\n // Call logout API (works in both cookie and JSON modes)\n // Cookie mode: server reads refresh token from HttpOnly cookie\n // JSON mode: uses refresh_token from storage\n const response = await this.authApi.logOut(deviceId, refreshToken, !this.appId);\n if (response.status !== 'ok') {\n throw new Error('Logout failed');\n }\n } catch (_error) {\n // Logout API call failed - ignore and continue with local cleanup\n // User experience is better if we silently handle logout failures\n }\n }\n\n // Clear all local state (both modes)\n this.forgetPkceSignIn();\n this.storageManager.deleteTokens();\n this.storageManager.clearIdToken();\n this.storageManager.clearCsrfToken();\n this.tokenDeliveryManager.reset();\n this.subscribeStore.notify(PassflowEvent.SignOut, {});\n }\n\n /**\n * The session and user identity this client currently holds, or `undefined`\n * when none is readable.\n *\n * Read from the stored access token's claims, which is the same source every\n * other client-side session fact comes from. In a delivery mode that keeps\n * the access token out of reach — a cookie-mode or BFF session — there is\n * nothing to read and the answer is `undefined`.\n */\n readSessionPin(): UpgradeSessionPin | undefined {\n try {\n const token = this.storageManager.getToken(TokenType.access_token);\n if (!token) {\n return undefined;\n }\n return readSessionPin(parseToken(token) as { sub?: unknown; sid?: unknown });\n } catch {\n return undefined;\n }\n }\n\n /**\n * Refuses to APPLY tokens that belong to a different principal (§13.4\n * revision 6 / H2).\n *\n * Called immediately before the rotated tokens are written, which is the only\n * place the check cannot be bypassed: the epoch guard around `prepare` and\n * `commit` runs before the server call, and token application happens after\n * it returns, so a logout and re-login in between let user A's flight write\n * over user B's session.\n *\n * It throws `UpgradeSessionChangedError`, so the elevation terminates as\n * §13.3's `session changed mid-flight` on every surface — the outcome class\n * itself, not a transport failure that happens to carry the kind: this is the\n * only entry point into token application, so a caller holding the promise\n * directly (`AuthService.refreshToken`) must see the same `code` a waiter\n * behind the controller does. It does NOT invalidate\n * anything: `setSessionInvalid()` runs off a positive allow-list elsewhere,\n * and the live user is never logged out as a side effect of someone else's\n * abandoned flight. Fail-closed on token application, fail-open on session\n * state — two different axes.\n */\n private assertSessionPin(pin: UpgradeSessionPin | undefined): void {\n if (!pin) {\n // No pin was captured when the flight started, which means this delivery\n // mode exposes no readable identity at all. There is nothing to compare,\n // so the epoch guard is the only protection in force — stated, not\n // silently assumed.\n return;\n }\n if (sessionPinMatches(pin, this.readSessionPin())) {\n return;\n }\n // biome-ignore lint/suspicious/noConsole: §13.4 revision 6 / C41 requires one visible `warn` on this path. A discarded elevation that left no trace is indistinguishable from a feature that silently never fired, and this is the fail-open diagnostic rule the whole module is built on.\n console.warn(\n '[passflow] the session or user changed while a step-up upgrade was in flight; the elevated tokens were discarded and the live session was left untouched.',\n );\n throw new UpgradeSessionChangedError('The session changed during verification, so the elevated tokens were discarded');\n }\n\n /**\n * Refreshes the session, optionally committing an assurance upgrade.\n *\n * This is the ONLY token-storing path for all four delivery modes, which is\n * why the upgrade commit goes through here rather than posting to\n * `/auth/refresh` directly: it saves the rotated tokens, updates the cache and\n * emits `Refresh`. Bypassing it would elevate the session on the server and\n * leave the client holding stale tokens — everything looks fine until the next\n * request 401s.\n *\n * An upgrade commit changes the error handling in one specific way: a 4xx is\n * NOT treated as a dead session. The server answers a wrong code with a 401,\n * and tearing the session down there is the logout-at-step-up defect. The\n * upgrade transport classifies those failures and invalidates the session\n * itself when the 401 turns out to be a genuine expiry.\n */\n async refreshToken(upgrade?: UpgradeRefreshField, pin?: UpgradeSessionPin): Promise<PassflowAuthorizationResponse> {\n this.subscribeStore.notify(PassflowEvent.RefreshStart, {});\n\n // BFF mode cannot carry an upgrade, and the refusal has to come BEFORE the\n // network call: the BFF refresh endpoint is the customer's own, posted to\n // with an empty body, so there is nowhere to put `{upgrade:{...}}` without a\n // customer-backend change. Refusing early is what lets the UI show\n // \"unavailable\" instead of asking for a code that can never be submitted.\n if (upgrade && this.tokenDeliveryManager.isBFFMode()) {\n throw new UpgradeTransportError({\n kind: 'unavailable',\n message: 'Step-up verification cannot be committed through a BFF refresh endpoint',\n });\n }\n\n // BFF mode: call BFF refresh endpoint\n if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.refreshUrl) {\n try {\n const response = await fetch(this.tokenExchangeConfig.refreshUrl, {\n method: 'POST',\n credentials: 'include', // Include httpOnly cookies\n });\n\n if (!response.ok) {\n this.tokenDeliveryManager.setSessionInvalid();\n throw new Error('BFF token refresh failed');\n }\n\n const data = await response.json();\n\n // Update session state\n this.tokenDeliveryManager.setSessionValid();\n\n // If BFF returns id_token, update local storage\n if (data.id_token) {\n this.storageManager.setIdToken(data.id_token);\n }\n\n this.subscribeStore.notify(PassflowEvent.Refresh, {\n tokens: data,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenCacheService.isRefreshing = false;\n this.tokenCacheService.tokenExpiredFlag = false;\n return data;\n } catch (error) {\n this.tokenDeliveryManager.setSessionInvalid();\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Token refresh failed',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n // Cookie mode: Server reads refresh token from HttpOnly cookie\n if (this.tokenDeliveryManager.isCookieMode()) {\n try {\n // Call refresh endpoint - browser sends HttpOnly cookies automatically\n const response = upgrade\n ? await this.authApi.refreshToken('', this.scopes, undefined, upgrade)\n : await this.authApi.refreshToken('', this.scopes);\n\n // The pin is verified BEFORE anything is applied — see `assertSessionPin`.\n this.assertSessionPin(pin);\n\n // Update session state\n this.tokenDeliveryManager.setSessionValid();\n\n // Process response (stores ID token, CSRF token)\n await this.processAuthResponse(response, this.scopes);\n\n this.subscribeStore.notify(PassflowEvent.Refresh, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenCacheService.isRefreshing = false;\n this.tokenCacheService.tokenExpiredFlag = false;\n return response;\n } catch (error) {\n // A failed upgrade commit is not a failed session: the four recoverable\n // upgrade errors all arrive as a bare 401. Classification — and the\n // invalidation a genuine expiry still deserves — belongs to the upgrade\n // transport, which knows it issued the request.\n if (!upgrade) {\n this.tokenDeliveryManager.setSessionInvalid();\n }\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Token refresh failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n // JSON mode: Existing behavior\n const tokens = this.storageManager.getTokens();\n if (!tokens) {\n const error = new Error('No tokens found');\n const errorPayload: ErrorPayload = {\n message: 'No tokens found',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n } else if (!tokens?.refresh_token) {\n const error = new Error('No refresh token found');\n const errorPayload: ErrorPayload = {\n message: 'No refresh token found',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n const oldScopes = tokens?.scopes ?? this.scopes;\n try {\n // The `upgrade` argument is passed only when there is one, rather than as\n // an explicit `undefined`: the ordinary refresh call stays byte-identical\n // to what it was before step-up existed.\n const refresh = tokens?.refresh_token ?? '';\n const access = tokens?.access_token;\n const response = upgrade\n ? await this.authApi.refreshToken(refresh, oldScopes, access, upgrade)\n : await this.authApi.refreshToken(refresh, oldScopes, access);\n // The pin is verified BEFORE the tokens are written — see\n // `assertSessionPin`. This is the line H2 exists for: `saveTokens` is the\n // moment user A's rotated tokens would land in user B's storage.\n this.assertSessionPin(pin);\n\n response.scopes = oldScopes;\n this.storageManager.saveTokens(response);\n this.tokenCacheService.setTokensCache(response);\n this.subscribeStore.notify(PassflowEvent.Refresh, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenCacheService.isRefreshing = false;\n this.tokenCacheService.tokenExpiredFlag = false;\n this.tokenCacheService.startTokenCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Token refresh failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n details:\n axios.isAxiosError(error) && error.response\n ? {\n status: error.response.status,\n data: error.response.data,\n }\n : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n\n // Check if this is an auth error (4xx) indicating session is invalid\n const is4xxError =\n axios.isAxiosError(error) && error.response?.status && error.response.status >= 400 && error.response.status < 500;\n\n // On an upgrade commit a 4xx is the server rejecting the PROOF, not the\n // session. Deleting the tokens here would log the user out for mistyping\n // six digits — the defect this flow exists to fix.\n if (is4xxError && !upgrade) {\n // Session is invalid - mark as expired and clean up\n this.tokenCacheService.tokenExpiredFlag = true;\n this.tokenCacheService.setTokensCache(undefined);\n this.storageManager.deleteTokens();\n this.subscribeStore.notify(PassflowEvent.SessionExpired, { reason: 'refresh_failed' });\n }\n\n if (error instanceof PassflowError) {\n throw error;\n } else if (is4xxError && !upgrade) {\n // Rethrown verbatim on the upgrade path instead: the status is the only\n // thing left to classify by once the server sent no error envelope, and\n // flattening it into a generic Error destroys that.\n throw new Error(`Getting unknown error message from server with code:${error.response?.status}`);\n } else {\n throw error;\n }\n }\n }\n\n async sendPasswordResetEmail(payload: PassflowSendPasswordResetEmailPayload): Promise<PassflowSuccessResponse> {\n try {\n return await this.authApi.sendPasswordResetEmail(payload);\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to send password reset email',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async resetPassword(newPassword: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n const urlParams = new URLSearchParams(this.platform.getCurrentUrl()?.search ?? '');\n const resetToken = urlParams.get('token') ?? undefined;\n const sscopes = scopes ?? this.scopes;\n\n try {\n const pkce = await this.startPkce();\n const answer = pkce\n ? await this.authApi.resetPassword(newPassword, sscopes, resetToken, pkce.challenge)\n : await this.authApi.resetPassword(newPassword, sscopes, resetToken);\n if (this.handlePendingSecondFactor(answer, undefined, pkce, sscopes)) {\n return answer;\n }\n\n const exchanged = await this.exchangeCode(answer, pkce);\n if (this.handlePendingSecondFactor(exchanged)) {\n return exchanged;\n }\n\n // The reset link's redirect is on the code's answer: the exchange returns only tokens.\n const response = answer.redirect_url ? { ...exchanged, redirect_url: answer.redirect_url } : exchanged;\n await this.processAuthResponse(response, sscopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Password reset failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passkeyRegister(payload: PassflowPasskeyRegisterStartPayload): Promise<PassflowAuthorizationResponse> {\n if (!this.platform.passkeys) throw new Error('Passkeys are not supported on this platform');\n this.subscribeStore.notify(PassflowEvent.RegisterStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n payload.scopes = payload.scopes ?? this.scopes;\n payload.create_tenant = this.createTenantForNewUser;\n\n try {\n const pkce = await this.startPkce();\n const { challenge_id, publicKey } = await this.authApi.passkeyRegisterStart(\n pkce ? { ...payload, code_challenge: pkce.challenge } : payload,\n deviceId,\n os,\n !this.appId,\n );\n // user handle should be base64 encoded for simplewebauthn lib we are using\n publicKey.user.id = this.platform.btoa(publicKey.user.id);\n const webauthn = await this.platform.passkeys.startRegistration(publicKey);\n if (!webauthn) throw new Error('Passkey registration was not completed');\n\n const answer = await this.authApi.passkeyRegisterComplete(webauthn, deviceId, challenge_id, !this.appId);\n if (this.handlePendingSecondFactor(answer, undefined, pkce, payload.scopes)) {\n return answer;\n }\n\n const responseRegisterComplete = await this.exchangeCode(answer, pkce);\n if (this.handlePendingSecondFactor(responseRegisterComplete)) {\n return responseRegisterComplete;\n }\n\n await this.processAuthResponse(responseRegisterComplete, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.Register, {\n tokens: responseRegisterComplete,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return responseRegisterComplete;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Passkey registration failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n async passkeyAuthenticate(payload: PassflowPasskeyAuthenticateStartPayload): Promise<PassflowAuthorizationResponse> {\n if (!this.platform.passkeys) throw new Error('Passkeys are not supported on this platform');\n this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n payload.scopes = payload.scopes ?? this.scopes;\n\n try {\n const pkce = await this.startPkce();\n const { challenge_id, publicKey } = await this.authApi.passkeyAuthenticateStart(\n pkce ? { ...payload, code_challenge: pkce.challenge } : payload,\n deviceId,\n os,\n !this.appId,\n );\n const webauthn = await this.platform.passkeys.startAuthentication(publicKey);\n if (!webauthn) throw new Error('Passkey authentication was not completed');\n\n const answer = await this.authApi.passkeyAuthenticateComplete(webauthn, deviceId, challenge_id, !this.appId);\n if (this.handlePendingSecondFactor(answer, undefined, pkce, payload.scopes)) {\n return answer;\n }\n\n const responseAuthenticateComplete = await this.exchangeCode(answer, pkce);\n if (this.handlePendingSecondFactor(responseAuthenticateComplete)) {\n return responseAuthenticateComplete;\n }\n\n if ('access_token' in responseAuthenticateComplete) {\n await this.processAuthResponse(responseAuthenticateComplete, payload.scopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: responseAuthenticateComplete,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n }\n\n return responseAuthenticateComplete;\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Passkey authentication failed',\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n createFederatedAuthUrl(payload: PassflowFederatedAuthExtendedPayload): string {\n const passflowPathWithProvider = `/auth/federated/start/${payload.provider}`;\n\n if (!this.appId) throw new Error('AppId is required for federated auth');\n const sscopes = payload.scopes ?? this.scopes;\n\n const params: Record<string, string> = {\n scopes: sscopes.join(' '),\n redirect_url: payload.redirect_url ?? this.platform.getCurrentUrl()?.origin ?? '',\n appId: this.appId,\n ...(payload.invite_token ? { invite_token: payload.invite_token } : {}),\n ...(payload.create_tenant ? { create_tenant: payload.create_tenant.toString() } : {}),\n ...(payload.device ? { device: payload.device } : {}),\n };\n\n const url = new URL(passflowPathWithProvider, this.url);\n const queryParams = new URLSearchParams(params);\n url.search = queryParams.toString();\n\n return url.toString();\n }\n\n async federatedAuthWithPopup(payload: PassflowFederatedAuthPayload): Promise<void> {\n this.subscribeStore.notify(PassflowEvent.SignInStart, { provider: payload.provider });\n const sscopes = payload.scopes ?? this.scopes;\n const deviceId = this.deviceService.getDeviceId();\n const passflowURL = this.createFederatedAuthUrl({ ...payload, scopes: sscopes, device: deviceId });\n\n try {\n const result = await this.platform.openAuthUrl(passflowURL, {\n mode: 'popup',\n expectedReturnOrigin: this.platform.getCurrentUrl()?.origin ?? '',\n });\n\n // A sign-in that owes its second factor comes back with the 2FA token\n // in the fragment, never the query, and with no tokens. Nothing is saved\n // and the page is not navigated: it stays to ask for the factor.\n const hashParams = new URLSearchParams(result.callbackHash.replace(/^#/, ''));\n if (this.handlePendingSecondFactor({ tfa_token: hashParams.get('tfa_token') ?? undefined })) {\n return;\n }\n\n const urlParams = new URLSearchParams(result.callbackSearch);\n const access_token = urlParams.get('access_token') || '';\n const refresh_token = urlParams.get('refresh_token') || '';\n const id_token = urlParams.get('id_token') || '';\n\n const tokensData = {\n access_token,\n refresh_token: refresh_token || undefined,\n id_token: id_token || undefined,\n scopes: sscopes,\n };\n\n await this.processAuthResponse(tokensData, sscopes);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: tokensData,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n this.platform.navigateTo(this.platform.getCurrentUrl()?.origin ?? '');\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Authentication failed',\n code: error instanceof PopupClosedError ? 'POPUP_CLOSED' : 'AUTH_FAILED',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n }\n }\n\n federatedAuthWithRedirect(payload: PassflowFederatedAuthPayload): void {\n this.subscribeStore.notify(PassflowEvent.SignInStart, { provider: payload.provider });\n const sscopes = payload.scopes ?? this.scopes;\n const deviceId = this.deviceService.getDeviceId();\n const passflowURL = this.createFederatedAuthUrl({ ...payload, scopes: sscopes, device: deviceId });\n this.platform.navigateTo(passflowURL);\n }\n\n // Helper methods for authentication UI redirect\n authRedirectUrl(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): string {\n try {\n const { url, redirectUrl, scopes, appId } = options ?? {};\n const externalUrl = new URL(url ?? this.url);\n // add web to the pathname if it's not there\n externalUrl.pathname = (externalUrl.pathname.endsWith('/') ? externalUrl.pathname : externalUrl.pathname + '/') + 'web';\n const sscopes = scopes ?? this.scopes;\n const params: Record<string, string> = {\n appId: appId ?? this.appId ?? '',\n redirectto: redirectUrl ?? this.platform.getCurrentUrl()?.href ?? '',\n scopes: sscopes.join(','),\n };\n\n const queryParams = new URLSearchParams(params);\n externalUrl.search = queryParams.toString();\n return externalUrl.toString();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to create auth redirect URL',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n authRedirect(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): void {\n try {\n this.platform.navigateTo(this.authRedirectUrl(options));\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to redirect to auth page',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n }\n\n /**\n * Check if user is authenticated\n * CRITICAL: Cookie/BFF mode checks ID token + session state, NOT access_token\n */\n isAuthenticated(parsedTokens: ParsedTokens): boolean {\n try {\n if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {\n // Cookie/BFF mode: Check for ID token presence + session validity\n const hasIdToken = !!parsedTokens?.id_token || !!this.storageManager.getIdToken();\n const sessionValid = this.tokenDeliveryManager.isSessionValid();\n const sessionUnknown = this.tokenDeliveryManager.isSessionUnknown();\n\n // Trust session if:\n // 1. We have ID token (proves we authenticated at some point)\n // 2. Session is valid (haven't received 401) OR\n // 3. Session is unknown (haven't tried yet, optimistic)\n return hasIdToken && (sessionValid || sessionUnknown);\n }\n\n // JSON mode: existing logic (check access/refresh token expiry)\n if (!parsedTokens || !parsedTokens.access_token) return false;\n\n return (\n !isTokenExpired(parsedTokens.access_token) ||\n (parsedTokens.refresh_token !== undefined && !isTokenExpired(parsedTokens.refresh_token))\n );\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to check authentication status',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return false;\n }\n }\n\n /**\n * Handle session check and callbacks\n */\n async submitSessionCheck(doRefresh = false): Promise<Tokens | undefined> {\n let tokens;\n let parsedTokens;\n try {\n tokens = await this.getTokens(doRefresh);\n parsedTokens = this.tokenCacheService.getParsedTokens();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error || error instanceof PassflowError ? error.message : 'Session check failed',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n tokens = undefined;\n }\n\n if (tokens && this.sessionCallbacks.createSession) {\n await this.sessionCallbacks.createSession({ tokens, parsedTokens });\n }\n\n if (!tokens && this.sessionCallbacks.expiredSession) {\n await this.sessionCallbacks.expiredSession();\n }\n\n return tokens;\n }\n\n /**\n * Get tokens and refresh if needed\n * Cookie/BFF mode: Returns ID token only (access/refresh in HttpOnly cookies)\n * JSON mode: Returns all tokens from localStorage\n */\n async getTokens(doRefresh: boolean): Promise<Tokens | undefined> {\n try {\n // Cookie/BFF mode: Server manages access/refresh tokens in HttpOnly cookies\n if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {\n const tokens = this.storageManager.getTokens();\n // In cookie/BFF mode, we only have ID token in localStorage\n if (!tokens?.id_token) return undefined;\n\n // If session is invalid and refresh requested, try refresh\n if (this.tokenDeliveryManager.isSessionInvalid() && doRefresh) {\n return await this.refreshToken();\n }\n\n // Return tokens (ID token only, access/refresh are in cookies)\n return tokens;\n }\n\n // JSON mode: Existing behavior\n const tokens = this.storageManager.getTokens();\n // we have no token in storage\n if (!tokens || !tokens.access_token) return undefined;\n\n const access = parseToken(tokens.access_token);\n\n if (isTokenExpired(access)) {\n // we have expired token and we need to refresh it or throw error if it's not possible\n if (doRefresh) return await this.refreshToken();\n\n // we need return undefined here, because we have expired token and we no need to refresh it\n return undefined;\n } else {\n return tokens;\n }\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return undefined;\n }\n }\n}\n","import type {\n InvitationAPI,\n InvitationsPaginatedList,\n InviteLinkResponse,\n PassflowSuccessResponse,\n RequestInviteLinkPayload,\n} from '../api';\n\n/**\n * Service for managing invitations\n */\nexport class InvitationService {\n constructor(private invitationApi: InvitationAPI) {}\n\n /**\n * Requests an invitation link that can be used to invite users\n * @param payload Request invitation payload\n * @returns Promise with invitation link and token\n */\n requestInviteLink(payload: RequestInviteLinkPayload): Promise<InviteLinkResponse> {\n return this.invitationApi.requestInviteLink(payload);\n }\n\n /**\n * Gets a list of active invitations\n * @param options Optional parameters for filtering and pagination\n * @returns Promise with paginated list of invitations\n */\n getInvitations(options: {\n tenantID: string;\n groupID?: string;\n skip?: number | string;\n limit?: number | string;\n }): Promise<InvitationsPaginatedList> {\n return this.invitationApi.getInvitations(options);\n }\n\n /**\n * Deletes an invitation by token\n * @param token The invitation token to delete\n * @returns Promise with success response\n */\n deleteInvitation(token: string): Promise<PassflowSuccessResponse> {\n return this.invitationApi.deleteInvitation(token);\n }\n\n /**\n * Resends an invitation by token\n * @param token The invitation token to resend\n * @returns Promise with success response\n */\n resendInvitation(token: string): Promise<PassflowSuccessResponse> {\n return this.invitationApi.resendInvitation(token);\n }\n\n /**\n * Gets a link to an invitation by id\n * @param invitationID The invitation ID to get link\n * @returns Promise with the link\n */\n getInvitationLink(invitationID: string): Promise<InviteLinkResponse> {\n return this.invitationApi.getInvitationLink(invitationID);\n }\n}\n","// lib/services/tenant-user-membership.ts\n\nimport type { PassflowGroup, PassflowRole, PassflowTenantResponse, PassflowUserInGroup } from '../api/model';\n\n/**\n * Flat user representation\n */\nexport interface User {\n id: string;\n name: string | null;\n email: string | null;\n phone: string | null;\n}\n\n/**\n * Flat group representation\n */\nexport interface Group {\n id: string;\n name: string;\n default: boolean;\n updated_at: string;\n created_at: string;\n}\n\n/**\n * Flat role representation\n */\nexport interface Role {\n id: string;\n tenant_id: string;\n name: string;\n}\n\n/**\n * Maps a user to a group with specific roles\n */\nexport interface Membership {\n userId: string;\n groupId: string;\n roleIds: string[];\n}\n\n/**\n * Full tenant view with lookup maps\n */\nexport interface TenantData {\n tenant_id: string;\n tenant_name: string;\n users: User[];\n groups: Group[];\n roles: Role[];\n memberships: Membership[];\n usersById: Map<string, User>;\n groupsById: Map<string, Group>;\n rolesById: Map<string, Role>;\n}\n\n/**\n * Utility for transforming raw PassflowTenantResponse\n * into a flattened TenantData model with quick lookup methods.\n */\nexport class TenantUserMembership {\n private data: TenantData;\n\n constructor(raw: PassflowTenantResponse) {\n this.data = this.normalize(raw);\n }\n\n private normalize(raw: PassflowTenantResponse): TenantData {\n const users = new Map<string, User>();\n const groups = new Map<string, Group>();\n const roles = new Map<string, Role>();\n const memberships: Membership[] = [];\n\n // process groups\n raw.groups?.forEach((g: PassflowGroup) => {\n groups.set(g.id, {\n id: g.id,\n name: g.name,\n default: g.default ?? false,\n updated_at: g.updated_at,\n created_at: g.created_at,\n });\n });\n\n // process roles\n raw.roles?.forEach((r: PassflowRole) => {\n roles.set(r.id, {\n id: r.id,\n tenant_id: r.tenant_id,\n name: r.name,\n });\n });\n\n // process users and memberships\n raw.users_in_groups?.forEach((uig: PassflowUserInGroup) => {\n const u = uig.user;\n if (u && !users.has(u.id)) {\n users.set(u.id, {\n id: u.id,\n name: u.name ?? null,\n email: u.email ?? null,\n phone: u.phone ?? null,\n });\n }\n if (u && uig.group_id && groups.has(uig.group_id)) {\n memberships.push({\n userId: u.id,\n groupId: uig.group_id,\n roleIds: uig.roles?.map((r) => r.id) ?? [],\n });\n }\n });\n\n return {\n tenant_id: raw.tenant_id,\n tenant_name: raw.tenant_name,\n users: Array.from(users.values()),\n groups: Array.from(groups.values()),\n roles: Array.from(roles.values()),\n memberships,\n usersById: users,\n groupsById: groups,\n rolesById: roles,\n };\n }\n\n /**\n * Returns all users in the specified group.\n */\n getUsersInGroup(groupId: string): User[] {\n return this.data.memberships\n .filter((m) => m.groupId === groupId)\n .map((m) => this.data.usersById.get(m.userId))\n .filter((u): u is User => u !== undefined);\n }\n\n /**\n * Returns all groups to which the specified user belongs.\n */\n getGroupsForUser(userId: string): Group[] {\n return this.data.memberships\n .filter((m) => m.userId === userId)\n .map((m) => this.data.groupsById.get(m.groupId))\n .filter((g): g is Group => g !== undefined);\n }\n\n /**\n * Returns all roles that the specified user has in the specified group.\n */\n getUserRolesInGroup(userId: string, groupId: string): Role[] {\n const membership = this.data.memberships.find((m) => m.userId === userId && m.groupId === groupId);\n if (!membership) {\n return [];\n }\n return membership.roleIds.map((roleId) => this.data.rolesById.get(roleId)).filter((r): r is Role => r !== undefined);\n }\n\n /**\n * Returns the full TenantData object.\n */\n getData(): TenantData {\n return this.data;\n }\n}\n","import axios from 'axios';\nimport {\n PassflowAuthorizationResponse,\n PassflowGroupResponse,\n PassflowInvitationsResponse,\n PassflowRoleResponse,\n PassflowStatusResponse,\n PassflowTenantResponse,\n PassflowUserTenantMembershipResponse,\n TenantAPI,\n} from '../api';\nimport { Logger, getDefaultLogger } from './logger';\nimport { TenantUserMembership } from './tenant-user-membership';\n\n/**\n * Service for managing tenants\n */\nexport class TenantService {\n private logger: Logger;\n\n constructor(\n private tenantApi: TenantAPI,\n private scopes: string[],\n logger?: Logger,\n ) {\n this.logger = logger || getDefaultLogger();\n }\n\n /**\n * Handle Passflow API errors\n * @param error The error object\n * @param context Context information for logging\n * @throws Formatted error with Passflow API error details\n */\n private handlePassflowError(error: unknown, context: string): never {\n // Check if it's an Axios error with a response\n if (axios.isAxiosError(error) && error.response?.data) {\n const responseData = error.response.data;\n\n // Check if it's a Passflow API error format\n if (\n typeof responseData === 'object' &&\n responseData !== null &&\n 'error' in responseData &&\n typeof responseData.error === 'object' &&\n responseData.error !== null\n ) {\n const passflowError = responseData.error as {\n id: string;\n message: string;\n status: number;\n location: string;\n time: string;\n };\n\n // Log the formatted error\n this.logger.error(`${context}: ${passflowError.id} - ${passflowError.message} (Status: ${passflowError.status})`);\n\n // Throw a new error with the formatted message\n throw new Error(`Passflow API Error: ${passflowError.id} - ${passflowError.message} (Status: ${passflowError.status})`);\n }\n }\n\n // If it's not a Passflow API error, log and rethrow\n this.logger.error(`${context}:`, error);\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(String(error));\n }\n\n /**\n * Join a tenant invitation\n * @param token The invitation token\n * @param scopes Optional scopes to request\n * @returns Promise with invite response\n */\n async joinInvitation(token: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n try {\n const sscopes = scopes ?? this.scopes;\n return await this.tenantApi.joinInvitation(token, sscopes);\n } catch (error) {\n this.handlePassflowError(error, 'Join invitation failed');\n }\n }\n\n /**\n * Create a new tenant\n * @param name The name of the tenant\n * @returns Promise with tenant response\n */\n async createTenant(name: string): Promise<PassflowTenantResponse> {\n try {\n return await this.tenantApi.createTenant(name);\n } catch (error) {\n this.handlePassflowError(error, 'Tenant creation failed');\n }\n }\n\n // 1. Tenant Management\n\n /**\n * Get tenant details\n * @param tenantId Tenant ID\n * @returns Promise with tenant response\n */\n /**\n * Get tenant details\n * @param tenantId Tenant ID\n * @returns Promise with tenant response\n */\n async getTenantDetails(tenantId: string): Promise<PassflowTenantResponse> {\n try {\n const response = await this.tenantApi.getTenantDetails(tenantId);\n return response;\n } catch (error) {\n this.handlePassflowError(error, `Get tenant details failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Get tenant details and transform into TenantUserMembership\n * @param tenantId Tenant ID\n * @returns Promise with TenantUserMembership instance\n */\n async getTenantUserMembership(tenantId: string): Promise<TenantUserMembership> {\n try {\n const response = await this.tenantApi.getTenantDetails(tenantId);\n return new TenantUserMembership(response);\n } catch (error) {\n this.handlePassflowError(error, `Get tenant user membership failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Update tenant name\n * @param tenantId Tenant ID\n * @param name New tenant name\n * @returns Promise with status response\n */\n async updateTenant(tenantId: string, name: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.updateTenant(tenantId, name);\n } catch (error) {\n this.handlePassflowError(error, `Update tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Delete a tenant\n * @param tenantId Tenant ID\n * @returns Promise with status response\n */\n async deleteTenant(tenantId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteTenant(tenantId);\n } catch (error) {\n this.handlePassflowError(error, `Delete tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Get user's tenant memberships\n * @returns Promise with user tenant membership response\n */\n async getUserTenantMembership(): Promise<PassflowUserTenantMembershipResponse> {\n try {\n return await this.tenantApi.getUserTenantMembership();\n } catch (error) {\n this.handlePassflowError(error, 'Get user tenant memberships failed');\n }\n }\n\n // 2. Group Management\n\n /**\n * Create a group in a tenant\n * @param tenantId Tenant ID\n * @param name Group name\n * @returns Promise with group response\n */\n async createGroup(tenantId: string, name: string): Promise<PassflowGroupResponse> {\n try {\n return await this.tenantApi.createGroup(tenantId, name);\n } catch (error) {\n this.handlePassflowError(error, `Group creation failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Get group information\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @returns Promise with group response\n */\n async getGroupInfo(tenantId: string, groupId: string): Promise<PassflowGroupResponse> {\n try {\n return await this.tenantApi.getGroupInfo(tenantId, groupId);\n } catch (error) {\n this.handlePassflowError(error, `Get group info failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Update a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param name New group name\n * @returns Promise with group response\n */\n async updateGroup(tenantId: string, groupId: string, name: string): Promise<PassflowGroupResponse> {\n try {\n return await this.tenantApi.updateGroup(tenantId, groupId, name);\n } catch (error) {\n this.handlePassflowError(error, `Update group failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Delete a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @returns Promise with status response\n */\n async deleteGroup(tenantId: string, groupId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteGroup(tenantId, groupId);\n } catch (error) {\n this.handlePassflowError(error, `Delete group failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Add a user to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param role Role to assign\n * @returns Promise with status response\n */\n async addUserToGroup(tenantId: string, groupId: string, userId: string, role: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.addUserToGroup(tenantId, groupId, userId, role);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Add user to group failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n /**\n * Remove user roles from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles Roles to remove\n * @returns Promise with status response\n */\n async removeUserRolesFromGroup(\n tenantId: string,\n groupId: string,\n userId: string,\n roles: string[],\n ): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.removeUserRolesFromGroup(tenantId, groupId, userId, roles);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Remove user roles from group failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n /**\n * Change user roles in a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @param roles New roles to assign\n * @returns Promise with status response\n */\n async changeUserRoles(tenantId: string, groupId: string, userId: string, roles: string[]): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.changeUserRoles(tenantId, groupId, userId, roles);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Change user roles failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n /**\n * Delete a user from a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param userId User ID\n * @returns Promise with status response\n */\n async deleteUserFromGroup(tenantId: string, groupId: string, userId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteUserFromGroup(tenantId, groupId, userId);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Delete user from group failed for tenant ID ${tenantId}, group ID ${groupId}, user ID ${userId}`,\n );\n }\n }\n\n // 3. Role Management\n\n /**\n * Get roles for a tenant\n * @param tenantId Tenant ID\n * @returns Promise with array of role responses\n */\n async getRolesForTenant(tenantId: string): Promise<PassflowRoleResponse[]> {\n try {\n return await this.tenantApi.getRolesForTenant(tenantId);\n } catch (error) {\n this.handlePassflowError(error, `Get roles for tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Create a role for a tenant\n * @param tenantId Tenant ID\n * @param name Role name\n * @returns Promise with role response\n */\n async createRoleForTenant(tenantId: string, name: string): Promise<PassflowRoleResponse> {\n try {\n return await this.tenantApi.createRoleForTenant(tenantId, name);\n } catch (error) {\n this.handlePassflowError(error, `Create role for tenant failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Update a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n * @param name New role name\n * @returns Promise with role response\n */\n async updateRole(tenantId: string, roleId: string, name: string): Promise<PassflowRoleResponse> {\n try {\n return await this.tenantApi.updateRole(tenantId, roleId, name);\n } catch (error) {\n this.handlePassflowError(error, `Update role failed for tenant ID ${tenantId}, role ID ${roleId}`);\n }\n }\n\n /**\n * Delete a role\n * @param tenantId Tenant ID\n * @param roleId Role ID\n * @returns Promise with status response\n */\n async deleteRole(tenantId: string, roleId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteRole(tenantId, roleId);\n } catch (error) {\n this.handlePassflowError(error, `Delete role failed for tenant ID ${tenantId}, role ID ${roleId}`);\n }\n }\n\n // 4. User Management in Tenants\n\n /**\n * Delete a user from a tenant\n * @param tenantId Tenant ID\n * @param userId User ID\n * @returns Promise with status response\n */\n async deleteUserFromTenant(tenantId: string, userId: string): Promise<PassflowStatusResponse> {\n try {\n return await this.tenantApi.deleteUserFromTenant(tenantId, userId);\n } catch (error) {\n this.handlePassflowError(error, `Delete user from tenant failed for tenant ID ${tenantId}, user ID ${userId}`);\n }\n }\n\n // 5. Invitation Management\n\n /**\n * Get invitations to a group\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n * @returns Promise with invitations response\n */\n async getGroupInvitations(\n tenantId: string,\n groupId: string,\n limit: number,\n skip: number,\n ): Promise<PassflowInvitationsResponse> {\n try {\n return await this.tenantApi.getGroupInvitations(tenantId, groupId, limit, skip);\n } catch (error) {\n this.handlePassflowError(error, `Get group invitations failed for tenant ID ${tenantId}, group ID ${groupId}`);\n }\n }\n\n /**\n * Get invitations to a tenant\n * @param tenantId Tenant ID\n * @param limit Maximum number of invitations to return\n * @param skip Number of invitations to skip\n * @returns Promise with invitations response\n */\n async getTenantInvitations(tenantId: string, limit: number, skip: number): Promise<PassflowInvitationsResponse> {\n try {\n return await this.tenantApi.getTenantInvitations(tenantId, limit, skip);\n } catch (error) {\n this.handlePassflowError(error, `Get tenant invitations failed for tenant ID ${tenantId}`);\n }\n }\n\n /**\n * Invalidate an invitation by ID\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param inviteId Invitation ID\n * @returns Promise with empty record\n */\n async invalidateInviteById(tenantId: string, groupId: string, inviteId: string): Promise<Record<string, never>> {\n try {\n return await this.tenantApi.invalidateInviteById(tenantId, groupId, inviteId);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Invalidate invite by ID failed for tenant ID ${tenantId}, group ID ${groupId}, invite ID ${inviteId}`,\n );\n }\n }\n\n /**\n * Invalidate an invitation by email\n * @param tenantId Tenant ID\n * @param groupId Group ID\n * @param email Email address\n * @returns Promise with empty record\n */\n async invalidateInviteByEmail(tenantId: string, groupId: string, email: string): Promise<Record<string, never>> {\n try {\n return await this.tenantApi.invalidateInviteByEmail(tenantId, groupId, email);\n } catch (error) {\n this.handlePassflowError(\n error,\n `Invalidate invite by email failed for tenant ID ${tenantId}, group ID ${groupId}, email ${email}`,\n );\n }\n }\n}\n","import { AuthAPI } from '../api';\nimport type { PlatformAdapter } from '../platform';\nimport { webAdapter } from '../platform';\nimport { StorageManager } from '../storage';\nimport { ErrorPayload, PassflowEvent, PassflowStore } from '../store';\nimport { isTokenExpired, parseToken } from '../token';\nimport type { ParsedTokens, Tokens } from '../types';\n\nexport class TokenCacheService {\n tokensCache: Tokens | undefined;\n parsedTokensCache: ParsedTokens | undefined;\n\n private checkInterval: NodeJS.Timeout | null = null;\n private readonly CHECK_INTERVAL = 60000; // 1 minute (was 10ms)\n private unsubscribeForeground: (() => void) | null = null;\n private unsubscribeUnload: (() => void) | null = null;\n isRefreshing = false;\n tokenExpiredFlag = false;\n\n constructor(\n private storageManager: StorageManager,\n private authApi: AuthAPI,\n private subscribeStore: PassflowStore,\n private platform: PlatformAdapter = webAdapter,\n ) {\n this.storageManager = storageManager;\n this.authApi = authApi;\n this.setupLifecycleListeners();\n }\n\n initialize() {\n try {\n const tokens = this.storageManager.getTokens();\n if (!tokens) {\n // No tokens to watch. Polling would emit spurious TokenCacheExpired\n // events for logged-out users; wait for a sign-in to (re-)initialize.\n return;\n }\n\n // Cookie mode: access_token may not be in storage (only ID token)\n // In cookie mode, we cache whatever tokens we have (likely just ID token)\n if (!tokens.access_token) {\n this.setTokensCache(tokens);\n this.startTokenCheck();\n return;\n }\n\n // JSON mode: check access_token expiry\n const access = parseToken(tokens.access_token);\n\n if (isTokenExpired(access)) {\n this.tokenExpiredFlag = true;\n this.stopTokenCheck();\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: true });\n } else {\n this.setTokensCache(tokens);\n this.startTokenCheck();\n }\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.setTokensCache(undefined);\n }\n }\n\n private async refreshTokensCache(tokens: Tokens) {\n if (this.isRefreshing) return;\n\n try {\n this.isRefreshing = true;\n this.subscribeStore.notify(PassflowEvent.RefreshStart, {});\n\n const response = await this.authApi.refreshToken(tokens?.refresh_token ?? '', tokens.scopes ?? [], tokens.access_token);\n this.setTokensCache(response);\n\n this.subscribeStore.notify(PassflowEvent.Refresh, { tokens: response, parsedTokens: this.getParsedTokens() });\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: false });\n this.tokenExpiredFlag = false;\n this.startTokenCheck();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n\n // Mark session as expired to prevent infinite refresh loops\n this.tokenExpiredFlag = true;\n this.setTokensCache(undefined);\n this.stopTokenCheck();\n\n // Clear stored tokens since refresh failed\n this.storageManager.deleteTokens();\n\n // Notify that session has expired - app should redirect to login\n this.subscribeStore.notify(PassflowEvent.SessionExpired, { reason: 'refresh_failed' });\n } finally {\n this.isRefreshing = false;\n }\n }\n\n startTokenCheck() {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n }\n\n if (this.tokenExpiredFlag) return;\n\n // Setup lifecycle listeners for foreground/visibility checks\n this.setupLifecycleListeners();\n\n this.checkInterval = setInterval(() => {\n // Skip check if page is not in foreground\n if (!this.platform.isInForeground()) {\n return;\n }\n\n if (this.isRefreshing || this.tokenExpiredFlag) return;\n\n if (this.isExpired() && !this.tokenExpiredFlag) {\n this.tokenExpiredFlag = true;\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: true });\n this.stopTokenCheck();\n }\n }, this.CHECK_INTERVAL);\n }\n\n private setupLifecycleListeners(): void {\n // Clean up existing listeners before setting up new ones\n if (this.unsubscribeForeground) {\n this.unsubscribeForeground();\n this.unsubscribeForeground = null;\n }\n\n this.unsubscribeForeground = this.platform.onForeground(() => {\n if (this.checkInterval && !this.isRefreshing && !this.tokenExpiredFlag && this.isExpired()) {\n this.tokenExpiredFlag = true;\n this.subscribeStore.notify(PassflowEvent.TokenCacheExpired, { isExpired: true });\n this.stopTokenCheck();\n }\n });\n\n if (!this.unsubscribeUnload) {\n this.unsubscribeUnload = this.platform.onBeforeUnload(() => {\n this.destroy();\n });\n }\n }\n\n private stopTokenCheck() {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n\n if (this.unsubscribeForeground) {\n this.unsubscribeForeground();\n this.unsubscribeForeground = null;\n }\n }\n\n /**\n * Cleanup method to stop all intervals and remove event listeners.\n * Should be called when the service is no longer needed.\n */\n destroy() {\n this.stopTokenCheck();\n if (this.unsubscribeUnload) {\n this.unsubscribeUnload();\n this.unsubscribeUnload = null;\n }\n }\n\n setTokensCache(tokens: Tokens | undefined): void {\n this.tokensCache = tokens;\n\n // Cookie mode: Only ID token may be available (access/refresh tokens in HttpOnly cookies)\n // JSON mode: All tokens expected\n // Handle partial data gracefully for cookie mode\n if (tokens) {\n // Parse available tokens (some may be undefined in cookie mode)\n this.parsedTokensCache = {\n access_token: tokens.access_token ? parseToken(tokens.access_token) : undefined,\n id_token: tokens.id_token ? parseToken(tokens.id_token) : undefined,\n refresh_token: tokens.refresh_token ? parseToken(tokens.refresh_token) : undefined,\n scopes: tokens.scopes,\n };\n } else {\n this.parsedTokensCache = undefined;\n }\n }\n\n getTokens() {\n return this.tokensCache;\n }\n\n async getTokensWithRefresh() {\n try {\n if (!this.tokensCache) return this.tokensCache;\n\n // Cookie mode: access_token may not be available (in HttpOnly cookie)\n // In cookie mode, we cannot check expiry client-side, so just return tokens\n // Server will handle refresh via 401 response and axios interceptor\n if (!this.tokensCache.access_token) {\n return this.tokensCache;\n }\n\n // JSON mode: check and refresh if needed\n const access = parseToken(this.tokensCache.access_token);\n\n if (isTokenExpired(access) && !this.tokenExpiredFlag) {\n await this.refreshTokensCache(this.tokensCache);\n return this.tokensCache;\n } else {\n return this.tokensCache;\n }\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : 'Failed to get tokens',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return undefined;\n }\n }\n\n getParsedTokens() {\n return this.parsedTokensCache;\n }\n\n isExpired() {\n if (!this.tokensCache) return true;\n\n // Cookie mode: access_token may not be available (in HttpOnly cookie)\n // In cookie mode, we can't check expiry client-side, so return false\n // and let the server respond with 401 when the cookie expires\n if (!this.tokensCache.access_token) {\n return false; // Assume valid in cookie mode, server will validate\n }\n\n const access = parseToken(this.tokensCache.access_token);\n return isTokenExpired(access);\n }\n}\n","import {\n RegisteredTwoFactorMethod,\n TwoFactorAlternativeRequest,\n TwoFactorChallengeRequest,\n TwoFactorChallengeResponse,\n TwoFactorCompletionOptions,\n TwoFactorConfirmResponse,\n TwoFactorDisableResponse,\n TwoFactorMethod,\n TwoFactorRecoveryResponse,\n TwoFactorRegenerateResponse,\n TwoFactorSetupMagicLinkSession,\n TwoFactorSetupMagicLinkValidationResponse,\n TwoFactorSetupResponse,\n TwoFactorStatusResponse,\n TwoFactorTrustedDevice,\n TwoFactorVerifyRequestV2,\n TwoFactorVerifyResponse,\n TwoFactorVerifyResponseV2,\n} from '../api/model';\nimport { TwoFactorApiClient } from '../api/two-factor';\nimport { PassflowEvent, PassflowStore } from '../store';\nimport type { Tokens } from '../types';\nimport { isValidTOTPCode, normalizeRecoveryCode } from '../utils/validation';\n\n/**\n * Partial auth state for 2FA verification flow\n */\ninterface PartialAuthState {\n email?: string;\n challengeId?: string;\n tfaToken?: string;\n timestamp: number;\n expiresAt: number;\n}\n\n/** Payload for TwoFactorRequired event */\ninterface TwoFactorRequiredPayload {\n email: string;\n challengeId: string;\n tfaToken: string;\n}\n\n/**\n * Owns what a second factor's answer turns into: this service never stores tokens\n * or holds the PKCE verifier. `AuthService` implements it.\n */\nexport interface SignInCompleter {\n /** Redeems the code a second factor answered with on a PKCE sign-in, and applies the tokens. */\n completePkceSignIn(code: string): Promise<Tokens>;\n /** Applies the tokens a second factor answered with directly. */\n applyTokens(tokens: Tokens): Promise<void>;\n hasPendingPkceSignIn(): boolean;\n forgetPkceSignIn(): void;\n}\n\n/** Error with optional id field */\ninterface ErrorWithId extends Error {\n id?: string;\n}\n\n/**\n * Service for managing Two-Factor Authentication\n */\nexport class TwoFactorService {\n private partialAuthState?: PartialAuthState;\n private readonly PARTIAL_AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes timeout\n private readonly SESSION_STORAGE_KEY = 'passflow_2fa_challenge';\n\n // TOTP digit configuration (6 or 8 digits)\n // This field is used throughout the service for TOTP validation and API responses\n private totpDigits: 6 | 8 = 6; // Default to 6 for backward compatibility\n\n // Magic link session storage (in-memory only for security - no persistence)\n private magicLinkSession?: TwoFactorSetupMagicLinkSession;\n\n constructor(\n private twoFactorApi: TwoFactorApiClient,\n private subscribeStore: PassflowStore,\n private signInCompleter?: SignInCompleter,\n ) {\n // Listen for TwoFactorRequired event from AuthService\n // Create a subscriber that handles the TwoFactorRequired event\n const eventSubscriber = {\n onAuthChange: (event: PassflowEvent, payload?: unknown) => {\n if (event === PassflowEvent.TwoFactorRequired) {\n const tfPayload = payload as TwoFactorRequiredPayload;\n this.setPartialAuthState(tfPayload.email, tfPayload.challengeId, tfPayload.tfaToken);\n }\n // A completed sign-in supersedes any pending second factor: one left\n // from an abandoned attempt, or planted with a `#tfa_token=` link,\n // must not send the signed-in user to the 2FA screen.\n if (event === PassflowEvent.SignIn || event === PassflowEvent.Register) {\n this.clearPartialAuthState();\n }\n },\n };\n\n this.subscribeStore.subscribe(eventSubscriber, [\n PassflowEvent.TwoFactorRequired,\n PassflowEvent.SignIn,\n PassflowEvent.Register,\n ]);\n }\n\n /**\n * Emit error event and throw the error\n * Helper method to ensure errors are properly emitted to subscribers\n */\n private emitErrorAndThrow(error: unknown, context: string): never {\n const errorWithId = error as ErrorWithId;\n const errorPayload = {\n message: error instanceof Error ? error.message : `${context} failed`,\n originalError: error,\n code: errorWithId?.id || undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n /**\n * Get 2FA enrollment status for current user\n */\n async getStatus(): Promise<TwoFactorStatusResponse> {\n try {\n const response = await this.twoFactorApi.getStatus();\n // Store totp_digits from backend response\n if (response.totp_digits) {\n this.totpDigits = response.totp_digits;\n }\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get 2FA status');\n }\n }\n\n /**\n * Begin 2FA setup process\n * Returns secret and QR code for authenticator app\n */\n async beginSetup(): Promise<TwoFactorSetupResponse> {\n try {\n const response = await this.twoFactorApi.beginSetup();\n // Store totp_digits from backend response\n if (response.totp_digits) {\n this.totpDigits = response.totp_digits;\n }\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupStarted, { secret: response.secret });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Begin 2FA setup');\n }\n }\n\n /**\n * Confirm 2FA setup with TOTP code\n * Returns recovery codes that MUST be displayed to user\n */\n async confirmSetup(code: string): Promise<TwoFactorConfirmResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n try {\n const response = await this.twoFactorApi.confirmSetup({ code });\n\n // Emit event with recovery codes and cleanup callback\n // The clearRecoveryCodes callback modifies the original response.recovery_codes array\n this.subscribeStore.notify(PassflowEvent.TwoFactorEnabled, {\n recoveryCodes: response.recovery_codes,\n clearRecoveryCodes: () => {\n // Clear the array in place to ensure all references are cleared\n response.recovery_codes.length = 0;\n },\n });\n\n // Return response with recovery codes intact\n // Apps must handle secure storage/display of codes\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Confirm 2FA setup');\n }\n }\n\n /**\n * Verify TOTP code during login\n * Completes authentication if successful\n *\n * @param options.trustDevice - ask the server to trust this device for later sign-ins\n */\n async verify(code: string, options?: TwoFactorCompletionOptions): Promise<TwoFactorVerifyResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n // Attempt to recover partial auth state from sessionStorage\n this.recoverPartialAuthState();\n\n if (!this.isVerificationRequired()) {\n throw new Error('2FA verification expired or not required. User must sign in first.');\n }\n\n // Validate that tfa_token exists (now required)\n if (!this.partialAuthState?.tfaToken) {\n throw new Error('No TFA token found. User must sign in first.');\n }\n\n try {\n const response = await this.twoFactorApi.verify({\n code,\n tfa_token: this.partialAuthState.tfaToken,\n ...(options?.trustDevice ? { trust_device: true } : {}),\n });\n\n const completed = await this.finishSignIn(response);\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorVerified, { tokens: completed });\n\n return completed;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Verify 2FA code');\n }\n }\n\n /**\n * Use recovery code for authentication\n * Completes authentication if successful\n *\n * @param options.trustDevice - ask the server to trust this device for later sign-ins\n */\n async useRecoveryCode(code: string, options?: TwoFactorCompletionOptions): Promise<TwoFactorRecoveryResponse> {\n try {\n // Normalize and validate recovery code format\n const normalizedCode = normalizeRecoveryCode(code);\n if (!normalizedCode) {\n throw new Error('Invalid recovery code format. Expected format: XXXX-XXXX or XXXXXXXX (alphanumeric).');\n }\n\n // Attempt to recover partial auth state from sessionStorage\n this.recoverPartialAuthState();\n\n if (!this.isVerificationRequired()) {\n throw new Error('2FA verification expired or not required. User must sign in first.');\n }\n\n // Validate that tfa_token exists (now required)\n if (!this.partialAuthState?.tfaToken) {\n throw new Error('No TFA token found. User must sign in first.');\n }\n\n const response = await this.finishSignIn(\n await this.twoFactorApi.useRecoveryCode({\n recovery_code: normalizedCode,\n tfa_token: this.partialAuthState.tfaToken,\n ...(options?.trustDevice ? { trust_device: true } : {}),\n }),\n );\n\n // Check for recovery codes exhaustion\n if (response.remaining_recovery_codes === 0) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorRecoveryCodesExhausted, { tokens: response });\n } else if (response.remaining_recovery_codes <= 2) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorRecoveryCodesLow, {\n tokens: response,\n remainingCodes: response.remaining_recovery_codes,\n });\n }\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorRecoveryUsed, {\n tokens: response,\n remainingCodes: response.remaining_recovery_codes,\n });\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorVerified, { tokens: response });\n\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Use recovery code');\n }\n }\n\n /**\n * Disable 2FA (requires TOTP verification)\n */\n async disable(code: string): Promise<TwoFactorDisableResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n try {\n const response = await this.twoFactorApi.disable({ code });\n this.subscribeStore.notify(PassflowEvent.TwoFactorDisabled, {});\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Disable 2FA');\n }\n }\n\n /**\n * Regenerate recovery codes\n */\n async regenerateRecoveryCodes(code: string): Promise<TwoFactorRegenerateResponse> {\n // Validate TOTP code format with configured digits\n if (!isValidTOTPCode(code, this.totpDigits)) {\n throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);\n }\n\n try {\n const response = await this.twoFactorApi.regenerateRecoveryCodes({ code });\n\n // Create a copy of recovery codes for potential event handling\n const codesCopy = [...response.recovery_codes];\n\n // Clear recovery codes from response immediately (security)\n response.recovery_codes = [];\n\n // Note: No event is emitted for regeneration, but we clear codes from response\n // as a security measure. Apps should extract codes synchronously from the return value.\n\n // Restore codes to the copy so they can be returned\n response.recovery_codes = codesCopy;\n\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Regenerate recovery codes');\n }\n }\n\n /**\n * Check if 2FA verification is required (local state check)\n * Returns true if user has signed in but needs 2FA verification\n */\n isVerificationRequired(): boolean {\n // Attempt to recover partial auth state from sessionStorage\n // This is needed when the Passflow instance is recreated (e.g., due to React state changes)\n // but the 2FA challenge data was stored by a previous instance\n this.recoverPartialAuthState();\n\n if (!this.partialAuthState) return false;\n\n // Check if expired\n if (Date.now() > this.partialAuthState.expiresAt) {\n this.clearPartialAuthState();\n return false;\n }\n\n return true;\n }\n\n /**\n * Set partial auth state when login requires 2FA\n * Called internally via event listener when AuthService emits TwoFactorRequired\n */\n setPartialAuthState(email?: string, challengeId?: string, tfaToken?: string): void {\n this.partialAuthState = {\n email,\n challengeId,\n tfaToken,\n timestamp: Date.now(),\n expiresAt: Date.now() + this.PARTIAL_AUTH_TIMEOUT_MS,\n };\n\n // Persist to sessionStorage for page refresh recovery\n // SECURITY NOTE: Storing tfaToken in sessionStorage is acceptable because:\n // 1. The tfaToken is a JWT with short expiration (5 minutes)\n // 2. It cannot be used alone - requires a valid TOTP code (6-digit from authenticator app)\n // 3. It expires after 5 minutes (PARTIAL_AUTH_TIMEOUT_MS and JWT expiration)\n // 4. It's session-scoped (cleared on tab close)\n // 5. Storing it enables better UX (page refresh recovery during 2FA flow)\n // The security boundary is the TOTP code, not the TFA token.\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.setItem(this.SESSION_STORAGE_KEY, JSON.stringify(this.partialAuthState));\n } catch {\n // Ignore sessionStorage errors (e.g., quota exceeded, disabled)\n }\n }\n }\n\n /**\n * Clear partial auth state\n * Called on logout or successful verification\n */\n clearPartialAuthState(): void {\n this.partialAuthState = undefined;\n this.signInCompleter?.forgetPkceSignIn();\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.removeItem(this.SESSION_STORAGE_KEY);\n } catch {\n // Ignore sessionStorage errors\n }\n }\n }\n\n /**\n * Ends the pending sign-in with the second factor's answer. On a PKCE app that\n * answer is a code, redeemed and replaced by the tokens, so no caller or\n * subscriber ever gets a code where tokens belong. Tokens are applied once, by\n * the completer, in the app's delivery mode. The pending state goes only after\n * that has worked, except when no verifier can ever redeem the code.\n */\n private async finishSignIn<T extends { code?: string }>(response: T): Promise<T & Tokens> {\n const { code, ...rest } = response;\n if (code && !this.signInCompleter?.hasPendingPkceSignIn()) {\n this.clearPartialAuthState();\n const error: ErrorWithId = new Error('The sign-in answered with a PKCE code, but its verifier is gone');\n error.id = 'PKCE_VERIFIER_MISSING';\n throw error;\n }\n\n let completed = response as T & Tokens;\n if (code && this.signInCompleter) {\n const tokens = await this.signInCompleter.completePkceSignIn(code);\n completed = { ...rest, ...tokens } as unknown as T & Tokens;\n } else {\n await this.signInCompleter?.applyTokens(completed);\n }\n\n this.clearPartialAuthState();\n return completed;\n }\n\n /**\n * The 2FA token of the sign-in waiting on its second factor, if any.\n */\n private pendingTfaToken(): string | undefined {\n return this.isVerificationRequired() ? this.partialAuthState?.tfaToken || undefined : undefined;\n }\n\n /**\n * Attempt to recover partial auth state from sessionStorage\n * Called before verification operations to handle page refresh\n */\n private recoverPartialAuthState(): void {\n if (this.partialAuthState) return; // Already in memory\n\n if (typeof sessionStorage === 'undefined') return;\n\n try {\n const storedState = sessionStorage.getItem(this.SESSION_STORAGE_KEY);\n if (!storedState) return;\n\n const parsed = JSON.parse(storedState) as PartialAuthState;\n\n // Check if expired\n if (Date.now() < parsed.expiresAt) {\n this.partialAuthState = parsed;\n } else {\n // Expired - clean up\n sessionStorage.removeItem(this.SESSION_STORAGE_KEY);\n }\n } catch {\n // Invalid data - clean up\n try {\n sessionStorage.removeItem(this.SESSION_STORAGE_KEY);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n // ============================================\n // Magic Link 2FA Setup Methods\n // ============================================\n\n /**\n * Validate magic link token for 2FA setup\n *\n * This method validates an admin-generated magic link token and\n * creates a scoped session (scope: \"2fa_setup\") that can ONLY be\n * used for completing 2FA setup operations.\n *\n * Session characteristics:\n * - Stored in memory only (no persistence across page reloads)\n * - Short-lived (typically 1 hour expiration)\n * - Cannot be refreshed\n * - Cannot be promoted to full authentication\n * - Only valid for 2FA setup endpoints\n *\n * @param token - Magic link token from URL parameter\n * @returns Validation response with session details\n */\n async validateTwoFactorSetupMagicLink(token: string): Promise<TwoFactorSetupMagicLinkValidationResponse> {\n // Call backend validation endpoint (API client handles all error cases)\n const response = await this.twoFactorApi.validateTwoFactorSetupMagicLink(token);\n\n // If validation successful, store session in memory\n if (response.success && response.sessionToken && response.userId) {\n this.magicLinkSession = {\n sessionToken: response.sessionToken,\n userId: response.userId,\n appId: response.appId,\n scope: '2fa_setup',\n timestamp: Date.now(),\n expiresAt: Date.now() + (response.expiresIn || 3600) * 1000,\n };\n\n // Emit success event\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupMagicLinkValidated, {\n userId: response.userId,\n appId: response.appId,\n expiresIn: response.expiresIn || 3600,\n sessionToken: response.sessionToken,\n });\n } else if (response.error) {\n // Emit failure event\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupMagicLinkFailed, {\n error: response.error,\n });\n }\n\n return response;\n }\n\n /**\n * Get current magic link session (if any)\n * Used by React SDK to access session token for API calls\n *\n * @returns Active magic link session or null if none/expired\n */\n getMagicLinkSession(): TwoFactorSetupMagicLinkSession | null {\n if (!this.magicLinkSession) return null;\n\n // Check if expired\n if (Date.now() > this.magicLinkSession.expiresAt) {\n this.clearMagicLinkSession();\n return null;\n }\n\n return this.magicLinkSession;\n }\n\n /**\n * Clear magic link session\n * Called after successful setup completion or on error\n */\n clearMagicLinkSession(): void {\n this.magicLinkSession = undefined;\n }\n\n /**\n * Check if magic link session is active\n * Used by React SDK to determine if form can use magic link auth\n */\n hasMagicLinkSession(): boolean {\n return this.getMagicLinkSession() !== null;\n }\n\n /**\n * Get the session token from magic link session (if active)\n * Used by AxiosClient for injecting auth header on 2FA setup endpoints\n */\n getMagicLinkSessionToken(): string | null {\n const session = this.getMagicLinkSession();\n return session?.sessionToken || null;\n }\n\n /**\n * Get configured TOTP digit count\n * Returns the number of digits (6 or 8) for TOTP codes\n * Useful for UI components that need to render the correct number of input fields\n */\n getTotpDigits(): 6 | 8 {\n return this.totpDigits;\n }\n\n // ============================================\n // v2 Multi-Method 2FA Service Methods\n // ============================================\n\n /**\n * Get available 2FA methods for current user\n */\n async getAvailableMethods(): Promise<TwoFactorMethod[]> {\n try {\n return await this.twoFactorApi.getAvailableMethods();\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get available 2FA methods');\n }\n }\n\n /**\n * Get registered 2FA methods for current user\n */\n async getRegisteredMethods(): Promise<RegisteredTwoFactorMethod[]> {\n try {\n return await this.twoFactorApi.getRegisteredMethods();\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get registered 2FA methods');\n }\n }\n\n /**\n * Start 2FA method setup\n */\n async startMethodSetup(method: TwoFactorMethod, payload?: Record<string, unknown>): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.startMethodSetup(method, payload);\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupStarted, { secret: '', method });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Start 2FA method setup');\n }\n }\n\n /**\n * Complete 2FA method setup\n */\n async completeMethodSetup(method: TwoFactorMethod, payload: Record<string, unknown>): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.completeMethodSetup(method, payload);\n this.subscribeStore.notify(PassflowEvent.TwoFactorEnabled, {\n recoveryCodes: [],\n clearRecoveryCodes: () => {\n // No-op: v2 multi-method setup doesn't return recovery codes\n },\n });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Complete 2FA method setup');\n }\n }\n\n /**\n * Remove registered 2FA method\n */\n async removeMethod(methodId: string): Promise<void> {\n try {\n await this.twoFactorApi.removeMethod(methodId);\n } catch (error) {\n this.emitErrorAndThrow(error, 'Remove 2FA method');\n }\n }\n\n /**\n * Request 2FA challenge during login\n */\n async requestChallenge(payload: TwoFactorChallengeRequest): Promise<TwoFactorChallengeResponse> {\n try {\n const response = await this.twoFactorApi.requestChallenge(payload, this.pendingTfaToken());\n this.subscribeStore.notify(PassflowEvent.TwoFactorChallengeReceived, {\n challengeId: response.challenge_id,\n method: response.method,\n alternativeMethods: response.alternative_methods,\n });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Request 2FA challenge');\n }\n }\n\n /**\n * Verify 2FA challenge (v2)\n * Set `trust_device` on the payload to ask the server to trust this device.\n */\n async verifyV2(payload: TwoFactorVerifyRequestV2): Promise<TwoFactorVerifyResponseV2> {\n try {\n const response = await this.twoFactorApi.verifyV2(payload, this.pendingTfaToken());\n if (!response.success) {\n return response;\n }\n const completed = await this.finishSignIn(response);\n this.subscribeStore.notify(PassflowEvent.TwoFactorVerified, {\n tokens: {\n access_token: completed.access_token,\n refresh_token: completed.refresh_token,\n },\n });\n if (completed.device_trusted) {\n this.subscribeStore.notify(PassflowEvent.TwoFactorDeviceTrusted, {});\n }\n return completed;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Verify 2FA challenge');\n }\n }\n\n /**\n * Switch to alternative 2FA method during challenge\n */\n async switchToAlternative(payload: TwoFactorAlternativeRequest): Promise<TwoFactorChallengeResponse> {\n try {\n const response = await this.twoFactorApi.switchToAlternative(payload, this.pendingTfaToken());\n this.subscribeStore.notify(PassflowEvent.TwoFactorMethodSwitched, {\n challengeId: response.challenge_id,\n method: response.method,\n alternativeMethods: response.alternative_methods,\n });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Switch to alternative 2FA method');\n }\n }\n\n /**\n * List the devices the user trusted to skip the second factor on this app.\n * The one this request came from has `current: true`.\n */\n async getTrustedDevices(): Promise<TwoFactorTrustedDevice[]> {\n try {\n return await this.twoFactorApi.getTrustedDevices();\n } catch (error) {\n this.emitErrorAndThrow(error, 'Get trusted devices');\n }\n }\n\n /**\n * Revoke a trusted device; its next sign-in asks for the second factor again.\n * Rejects with the server's 404 when the user has no such device.\n */\n async revokeTrustedDevice(deviceId: string): Promise<void> {\n try {\n await this.twoFactorApi.revokeTrustedDevice(deviceId);\n } catch (error) {\n this.emitErrorAndThrow(error, 'Revoke trusted device');\n }\n }\n}\n","import { PassflowSuccessResponse, UserAPI, getOSFromDeviceType } from '../api';\nimport type { PlatformAdapter } from '../platform';\nimport { webAdapter } from '../platform';\n\n/**\n * Service for managing user profile and passkeys\n */\nexport class UserService {\n constructor(\n private userAPI: UserAPI,\n private deviceService: DeviceService,\n private platform: PlatformAdapter = webAdapter,\n ) {}\n\n /**\n * Get user's registered passkeys\n * @returns Promise with passkeys array\n */\n getUserPasskeys() {\n return this.userAPI.getUserPasskeys();\n }\n\n /**\n * Rename a user passkey\n * @param name The new name for the passkey\n * @param passkeyId The ID of the passkey to rename\n * @returns Promise with success response\n */\n renameUserPasskey(name: string, passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.userAPI.renameUserPasskey(name, passkeyId);\n }\n\n /**\n * Delete a user passkey\n * @param passkeyId The ID of the passkey to delete\n * @returns Promise with success response\n */\n deleteUserPasskey(passkeyId: string): Promise<PassflowSuccessResponse> {\n return this.userAPI.deleteUserPasskey(passkeyId);\n }\n\n /**\n * Add a new passkey for the current user\n * @param options Optional parameters for the passkey\n * @returns Promise that resolves when the passkey is added\n */\n async addUserPasskey({\n relyingPartyId,\n passkeyUsername,\n passkeyDisplayName,\n }: {\n relyingPartyId?: string;\n passkeyUsername?: string;\n passkeyDisplayName?: string;\n } = {}): Promise<void> {\n if (!this.platform.passkeys) throw new Error('Passkeys are not supported on this platform');\n const deviceId = this.deviceService.getDeviceId();\n const os = getOSFromDeviceType(this.platform.getDeviceType());\n const { challenge_id, publicKey } = await this.userAPI.addUserPasskeyStart({\n relyingPartyId: (relyingPartyId || this.platform.getCurrentUrl()?.hostname) ?? '',\n deviceId,\n os,\n passkeyDisplayName,\n passkeyUsername,\n });\n // user handle should be base64 encoded for simplewebauthn lib we are using\n publicKey.user.id = this.platform.btoa(publicKey.user.id);\n const webauthn = await this.platform.passkeys.startRegistration(publicKey);\n if (!webauthn) throw new Error('Passkey registration was not completed');\n return await this.userAPI.addUserPasskeyComplete(webauthn, deviceId, challenge_id);\n }\n}\n\n// We need to import DeviceService after the class definition to avoid circular dependency\nimport { DeviceService } from '../device';\n","/**\n * Upgrade factor abstraction.\n *\n * A factor knows two things: whether proving it needs a server round-trip\n * first, and how to turn whatever the user did into the `upgrade.assertion`\n * blob `POST /auth/refresh` expects.\n *\n * `requiresPrepare` is declared statically rather than discovered by trying.\n * The server actively REJECTS a prepare call for one-shot factors\n * (`ErrorUpgradePrepareNotRequired`), so \"call /begin and see what happens\"\n * costs a guaranteed round-trip and a 400 on every TOTP upgrade.\n */\n\nimport type { AuthenticationResponseJSON, PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types';\nimport type { PlatformAdapter } from '../platform';\n\n/** Factor types the upgrade protocol understands, named as `upgrade.type` on the wire. */\nexport type UpgradeFactorType = 'totp' | 'recovery_codes' | 'passkey' | 'email_otp' | 'sms_otp';\n\n/** The prepare round's result — `POST /auth/upgrade/begin`, normalised to camelCase. */\nexport type UpgradePrepared = {\n challengeId: string;\n method: string;\n /** RFC 3339. The server gives a challenge five minutes; treat it as a hard deadline. */\n expiresAt: string;\n /** WebAuthn assertion options, present only for a passkey upgrade. */\n webauthn?: PublicKeyCredentialRequestOptionsJSON;\n /** Masked delivery target (`j***@x.com`), present only for email / SMS OTP. */\n codeSentTo?: string;\n};\n\n/**\n * The `upgrade.assertion` payload. Deliberately an open record: the server\n * types this field as `json.RawMessage` so new factors can extend the shape\n * without a wire-format change.\n */\nexport type UpgradeAssertion = {\n challenge_id?: string;\n code?: string;\n credential?: AuthenticationResponseJSON;\n};\n\n/** What a factor needs from the UI before it can be asserted. */\nexport type UpgradeFactorInput =\n /** A user-typed value: an OTP or a recovery code. */\n | 'code'\n /** Nothing typed — the ceremony itself collects the proof (a passkey prompt). */\n | 'none';\n\nexport type UpgradeAssertContext = {\n /** The value the user typed, for factors whose `input` is `'code'`. */\n code?: string;\n /** The prepare round's result, for factors where `requiresPrepare` is true. */\n prepared?: UpgradePrepared;\n /** Platform seam for the WebAuthn ceremony — keeps non-web adapters working. */\n platform: PlatformAdapter;\n};\n\nexport type UpgradeFactor = {\n type: UpgradeFactorType;\n /** Whether `POST /auth/upgrade/begin` must run before the user can produce proof. */\n requiresPrepare: boolean;\n /** What the UI must collect before `assert` can run. */\n input: UpgradeFactorInput;\n /** Turns the user's response into the assertion blob sent as `upgrade.assertion`. */\n assert(ctx: UpgradeAssertContext): Promise<UpgradeAssertion>;\n};\n\n/** Thrown when a factor cannot produce proof — a cancelled prompt, or missing prerequisites. */\nexport class UpgradeFactorError extends Error {\n readonly factor: UpgradeFactorType;\n /** True when the user dismissed the ceremony rather than failing it. */\n readonly cancelled: boolean;\n\n constructor(factor: UpgradeFactorType, message: string, cancelled = false) {\n super(message);\n this.name = 'UpgradeFactorError';\n this.factor = factor;\n this.cancelled = cancelled;\n }\n}\n\n/** Requires a typed value, rejecting the empty string that an untouched input yields. */\nconst requireCode = (factor: UpgradeFactorType, code: string | undefined): string => {\n const trimmed = code?.trim();\n if (!trimmed) {\n throw new UpgradeFactorError(factor, 'A verification code is required');\n }\n return trimmed;\n};\n\n/**\n * TOTP — the canonical one-shot factor. The code is derived from a shared\n * secret and the clock, so nothing needs to be fetched first.\n */\nexport const totpFactor: UpgradeFactor = {\n type: 'totp',\n requiresPrepare: false,\n input: 'code',\n assert: async ({ code }) => ({ code: requireCode('totp', code) }),\n};\n\n/**\n * Recovery codes — also one-shot. Verified against the user's stored code set,\n * so like TOTP there is no challenge to issue.\n */\nexport const recoveryCodesFactor: UpgradeFactor = {\n type: 'recovery_codes',\n requiresPrepare: false,\n input: 'code',\n assert: async ({ code }) => ({ code: requireCode('recovery_codes', code) }),\n};\n\n/**\n * Passkey — the challenge-response factor. The prepare round supplies the\n * server nonce and, critically, an `allowCredentials` list scoped to the\n * signed-in user; the ceremony signs it and the assertion goes back with the\n * challenge id that binds it to this session.\n */\nexport const passkeyFactor: UpgradeFactor = {\n type: 'passkey',\n requiresPrepare: true,\n input: 'none',\n assert: async ({ prepared, platform }) => {\n if (!prepared?.webauthn) {\n throw new UpgradeFactorError('passkey', 'The upgrade challenge carried no WebAuthn options');\n }\n // `passkeys` is optional on the adapter: a platform without WebAuthn cannot\n // run this ceremony at all. Fail before the prepare round is spent rather\n // than after, so the user can fall back to another factor with the\n // challenge budget intact.\n const passkeys = platform.passkeys;\n if (!passkeys) {\n throw new UpgradeFactorError('passkey', 'This platform does not support passkeys');\n }\n\n let credential: AuthenticationResponseJSON | undefined;\n try {\n credential = await passkeys.startAuthentication(prepared.webauthn);\n } catch (e) {\n // A dismissed or timed-out WebAuthn prompt rejects. That is a cancel, not\n // a failure: the caller should let the user retry or back out, never\n // treat it as a bad credential.\n throw new UpgradeFactorError('passkey', (e as Error)?.message || 'Passkey verification was cancelled', true);\n }\n if (!credential) {\n throw new UpgradeFactorError('passkey', 'Passkey verification was cancelled', true);\n }\n\n return { challenge_id: prepared.challengeId, credential };\n },\n};\n\n/**\n * The factors this SDK can actually complete.\n *\n * `email_otp` and `sms_otp` are intentionally ABSENT. The server routes them\n * (`upgradePrepareMethod` accepts both) but has no OTP transport, so no user can\n * enrol them and `upgradeMethodRegistered` refuses the prepare round. Adding\n * them later is one entry here plus a factor whose assert returns\n * `{ challenge_id, code }` — no change to anything that consumes this registry.\n */\nexport const DEFAULT_UPGRADE_FACTORS: readonly UpgradeFactor[] = Object.freeze([\n totpFactor,\n passkeyFactor,\n recoveryCodesFactor,\n]);\n\n/** Indexes a factor list by type for lookup during a challenge. */\nexport const factorRegistry = (\n factors: readonly UpgradeFactor[] = DEFAULT_UPGRADE_FACTORS,\n): Map<UpgradeFactorType, UpgradeFactor> => new Map(factors.map((f) => [f.type, f]));\n","/**\n * Which factor do we put in front of the user, and in what order?\n *\n * The challenge names what the server wants (`acrValues`, `maxAge`); this\n * module decides which of the SDK's registered factors to offer and which one\n * leads. The first entry is the \"suggested\" method the UI renders immediately\n * — everything after it sits behind \"use another way\" (PFL-509).\n *\n * ## Rank, never filter\n *\n * It is tempting to drop factors that \"cannot reach\" the demanded level. The\n * SDK cannot know that. The server computes `acr` from the *combination* of the\n * session's base methods and the verified factor, so a code factor DOES satisfy\n * an AAL3 challenge when the base session already reaches AAL3 — and for a\n * passkey the level depends on what the assertion reports, which is unknowable\n * until the ceremony runs.\n *\n * Getting that wrong is asymmetric. Offering a factor that turns out to be\n * insufficient costs one round-trip and a retry. Hiding one that would have\n * worked tells the user to re-authenticate when they did not have to, and\n * there is no way back from that. So every factor stays on the list; the\n * ordering carries the opinion, and the server stays the authority.\n *\n * ## Enrolment is filtered upstream, not here\n *\n * `available` is what this SDK can complete, not what the user has. The server\n * refuses a prepare round for an unenrolled factor\n * (`ErrorUpgradeMethodNotRegistered`). Narrowing `available` to the user's\n * actual credentials belongs to the caller — see the note at the bottom.\n */\n\nimport { requiredAcrLevel } from './acr';\nimport type { UpgradeChallenge } from './challenge';\nimport type { UpgradeFactor, UpgradeFactorType } from './factors';\n\n/** The assurance level a challenge demands, when it names one at all. */\nconst AAL3 = 3;\n\n/**\n * Ceremony preference among factors that are equally suitable, lowest first.\n *\n * Passkey leads because it is the cheapest ceremony for the user — one gesture,\n * nothing to read or type, nothing to mistype — and the strongest: it is the\n * only factor that can carry a session to AAL3 on its own, and the only one\n * that is phishing-resistant.\n *\n * Recovery codes sit last on purpose. They verify perfectly well, but each one\n * is single-use and the user's supply is finite and usually inconvenient to\n * reach. Spending one on a routine elevation is a bad trade when a repeatable\n * factor is available, so they are an escape hatch rather than a suggestion.\n */\nconst CEREMONY_ORDER: Record<UpgradeFactorType, number> = {\n passkey: 0,\n totp: 1,\n email_otp: 2,\n sms_otp: 3,\n recovery_codes: 9,\n};\n\n/**\n * The level this challenge demands.\n *\n * Delegates to the ONE shared recognizer in `acr.ts` — the same one\n * `challenge.ts` uses — so there is a single answer in this package to \"what\n * level does this `acr_values` string demand\". The local `Number.parseInt` list\n * reduction this replaced read `\"2gold\"` as level 2, which contradicted the rule\n * that opaque values do not participate.\n *\n * A merged challenge already carries the cross-channel bar in `requiredLevel`,\n * so that is preferred when present: re-deriving it from `acrValues` alone would\n * drop the other channel's contribution and could rank for a weaker bar than\n * the one the prompt is showing.\n *\n * Returns undefined when the challenge names no level (a `max_age`-only\n * freshness challenge), in which case any factor re-proves recency equally.\n */\nconst requiredLevel = (challenge: UpgradeChallenge): number | undefined =>\n challenge.requiredLevel ?? requiredAcrLevel(challenge.acrValues);\n\n/**\n * Ranks a factor against the demanded level. Lower sorts first.\n *\n * Only one rule applies beyond ceremony preference: when the challenge demands\n * AAL3, a factor that cannot reach it unaided is demoted out of the suggested\n * slot. It is still offered — the base session may carry the level — but it\n * should not be the thing the user is shown first, because for most sessions it\n * will not be enough.\n */\nconst rank = (factor: UpgradeFactor, demanded: number | undefined): number => {\n const ceremony = CEREMONY_ORDER[factor.type] ?? 5;\n const needsAAL3 = demanded !== undefined && demanded >= AAL3;\n // `psk` + `hwk` + `user` is the only marker set ComputeACR scores as AAL3,\n // and only a passkey ceremony can report it.\n const reachesAAL3Unaided = factor.type === 'passkey';\n const demoted = needsAAL3 && !reachesAAL3Unaided ? 100 : 0;\n return demoted + ceremony;\n};\n\n/**\n * Orders the factors to offer for a challenge. Index 0 is the suggested\n * method; the rest are the \"use another way\" alternatives.\n *\n * The suggested slot is not only a UX choice: PFL-510 treats a non-suggested\n * choice as a minor risk signal, so whatever leads here becomes the behavioural\n * baseline deviation is measured against.\n *\n * @param available - Factors this SDK can complete. Narrow to the user's\n * enrolled credentials before calling, when known.\n * @param challenge - What the server demanded (`acrValues`, `maxAge`).\n * @returns The factors to offer, most-preferred first. Empty only when\n * `available` was empty — this never drops a factor of its own accord.\n */\nexport const selectFactors = (available: readonly UpgradeFactor[], challenge: UpgradeChallenge): readonly UpgradeFactor[] => {\n const demanded = requiredLevel(challenge);\n // Sort a copy: `available` may be the caller's registry, and a stable sort\n // keeps registration order as the tie-break for equally ranked factors.\n return [...available].sort((a, b) => rank(a, demanded) - rank(b, demanded));\n};\n\n/*\n * TODO(PFL-497): narrow `available` to the user's enrolled factors before this\n * runs. Today the SDK would need two calls — `GET /user/passkey` and\n * `GET /v2/user/2fa/methods` — and would have to reimplement the union rule the\n * server already owns privately in `upgradeMethodRegistered`. That rule is\n * scheduled to change (passkeys move into the method registry), so duplicating\n * it client-side would break on a backend release. Wire this to the single\n * `?purpose=upgrade` discovery endpoint once it exists; until then an\n * unenrolled suggestion costs one refused prepare round.\n */\n","/**\n * Upgrade orchestration — ONE state machine, and it is the single authority for\n * lifecycle.\n *\n * Turns a parsed RFC 9470 challenge into an elevated session: pick a factor,\n * ask the user, run the prepare round if the factor needs one, assert, then\n * commit through `POST /auth/refresh` with the `upgrade` field.\n *\n * The controller owns no UI. It calls out to a prompt handler, which the React\n * layer (or any consumer) implements — that is what keeps the same logic usable\n * from a headless client.\n *\n * ## Why a state machine rather than rules\n *\n * Three review rounds each found two independently-correct lifecycle fixes\n * colliding: fold-versus-single-flight, replay-binding-versus-aggregation, and\n * a prompt outliving the flight whose state had already been cleared. The cause\n * was not any individual rule — it was that lifecycle was specified as rules\n * scattered across four sections, so two of them could each be locally right\n * and jointly impossible. Everything about when state is set, read and cleared\n * lives here, and nothing else writes these fields.\n *\n * ## The states\n *\n * | state | meaning |\n * |--------------|----------------------------------------------------------------|\n * | `idle` | no flight; every field clear; `inFlight === null` |\n * | `collecting` | prompt open, no factor selected yet |\n * | `proving` | a factor is selected; `prepare`/commit on the wire |\n * | `replaying` | proof accepted; waiters replaying their own bound requests |\n * | `settling` | every waiter has reported; the summary is complete |\n * | `done` | prompt settled and every waiter settled; one step from `idle` |\n *\n * ## The decision that shapes all of it\n *\n * **An elevation ends when every waiting request has been retried and settled.**\n * Proof acceptance does NOT end the flight. The previous implementation cleared\n * `inFlight` in a `.finally` on proof completion, and that `.finally` was a real\n * window: a prompt on screen with `inFlight === null`, in which an unrelated\n * challenged request opened a SECOND modal and a waiter could report into a\n * controller that had already forgotten its flight. Clearing happens in one\n * synchronous step, after the prompt has settled, and nowhere else.\n *\n * Two costs the design accepts for that: the prompt stays open for one extra\n * round trip while the replays settle, and a genuinely new challenge arriving in\n * that window queues behind the flight rather than opening a second prompt.\n */\n\nimport type { AssuranceBar, UpgradeChallenge } from './challenge';\nimport { UpgradeInsufficientError, UpgradeSessionChangedError, UpgradeTransportError, UpgradeUnavailableError } from './errors';\nimport {\n DEFAULT_UPGRADE_FACTORS,\n type UpgradeAssertion,\n type UpgradeFactor,\n UpgradeFactorError,\n type UpgradeFactorType,\n type UpgradePrepared,\n factorRegistry,\n} from './factors';\nimport { selectFactors } from './select';\n\nimport type { PlatformAdapter } from '../platform';\n\n/** What the UI is shown when an upgrade is required. */\nexport type UpgradePromptRequest = {\n /** What the server demanded — the MERGED requirement, when several challenges joined. */\n challenge: UpgradeChallenge;\n /** Usable factors, already ordered — the suggested one first. */\n factors: readonly UpgradeFactor[];\n /** Failure from the previous attempt, when the user is retrying. */\n previousError?: Error;\n /**\n * Called the moment the user picks a factor and BEFORE any answer is\n * submitted. REQUIRED of every prompt implementation — the SDK's own modal,\n * the `useUpgrade` hook and a render-prop override alike.\n *\n * `selected` is defined off this call and off nothing else. Deriving it from\n * `prompt` resolving would implement the SUBMISSION boundary instead: the\n * shipped `UpgradePromptResult` resolves with the code already collected, so\n * `selected` would stay false for the whole interval in which the user has\n * picked TOTP and is typing six digits — and a stronger challenge arriving\n * then would re-rank the list under their fingers, the exact UI jump the\n * selection boundary exists to prevent.\n *\n * An override that never calls it leaves the controller in `collecting` for\n * the whole ceremony, so a late join re-ranks mid-entry: degraded, not silent,\n * because `onRequirementRaised` still fires and the re-rank is observable.\n */\n onFactorSelected: (factor: UpgradeFactorType) => void;\n /**\n * The channel by which the controller pushes a raised bar and a re-ranked list\n * into an OPEN prompt, which updates without resolving. Only ever used before\n * a factor is selected (branch 2). The one-answer resolution contract is\n * unchanged.\n *\n * A prompt SUBSCRIBES by assigning its own handler to this field on the\n * request object it was handed, synchronously, before it awaits anything:\n *\n * ```ts\n * const prompt: UpgradePromptHandler = (request) => {\n * request.onRequirementRaised = (next) => setView(next);\n * return collectAnswer(request);\n * };\n * ```\n *\n * It is a slot rather than a controller-supplied function because the\n * direction is controller → prompt: only the prompt knows how to re-render\n * itself. An override that leaves it unset shows the old bar, and the raised\n * bar then surfaces as a re-challenged replay reporting `insufficient` —\n * degraded, never silent.\n */\n onRequirementRaised?: (next: { challenge: UpgradeChallenge; factors: readonly UpgradeFactor[] }) => void;\n};\n\n/** The user's answer to an upgrade prompt. */\nexport type UpgradePromptResult = { type: 'submit'; factor: UpgradeFactorType; code?: string } | { type: 'cancel' };\n\n/** Renders the upgrade UI and resolves once the user submits or backs out. */\nexport type UpgradePromptHandler = (request: UpgradePromptRequest) => Promise<UpgradePromptResult>;\n\n/** Transport seam — the controller never touches axios directly, so it stays testable. */\nexport type UpgradeTransport = {\n /**\n * `POST /auth/upgrade/start`. Only called for factors whose `requiresPrepare`\n * is true.\n *\n * Resolves `undefined` when no prepare round was needed after all — a one-shot\n * factor, or a server that answers `prepare_not_required` because its view of\n * the factor differs from ours. Both are recoveries: the factor then asserts\n * with no `prepared`, which is already the TOTP path.\n */\n prepare(method: UpgradeFactorType): Promise<UpgradePrepared | undefined>;\n /** `POST /auth/refresh` carrying `upgrade`. Resolves once elevated tokens are stored. */\n commit(factor: UpgradeFactorType, assertion: UpgradeAssertion): Promise<void>;\n};\n\n/** The six states. The controller is always in exactly one. */\nexport type UpgradeFlightState = 'idle' | 'collecting' | 'proving' | 'replaying' | 'settling' | 'done';\n\n/**\n * Who is waiting on this elevation.\n *\n * A `request` waiter owns a replay bound to its own URL and method. An\n * `imperative` waiter has nothing to replay, but when its caller STATED a bar\n * it still has a locally checkable requirement — which is why `requested`\n * exists, and why an AAL3 imperative call joining an AAL2 flight can no longer\n * be reported as `elevated`.\n */\nexport type Waiter = { kind: 'request'; url: string; method: string } | { kind: 'imperative'; requested?: AssuranceBar };\n\n/** A waiter's terminal classification. `pending` until it reports. */\nexport type WaiterOutcome = 'pending' | 'replayed' | 'insufficient' | 'failed';\n\n/** What a waiter reports once its own replay has settled. */\nexport type WaiterReport = {\n outcome: 'replayed' | 'insufficient' | 'failed';\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n};\n\n/**\n * A registered waiter.\n *\n * `report()` is the declared inbound edge: classification happens in the\n * adapter, after `elevate()` resolved, and this is the channel by which it\n * reaches the controller. Because `waiters` is cleared in the final transition\n * rather than on proof completion, the entries are still alive when the summary\n * reads them.\n */\nexport type WaiterEntry = Waiter & {\n outcome: WaiterOutcome;\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n /** Called exactly once. Idempotent: later calls are ignored. */\n report(result: WaiterReport): void;\n};\n\n/** The proof's own outcome, which is NOT any operation's outcome. */\nexport type UpgradeProof = { status: 'proven'; obtained: AssuranceBar } | { status: 'failed'; reason: Error };\n\n/** What a successful `elevate()` hands back to its caller. */\nexport type UpgradeElevation = {\n /** This caller's own registered waiter. `report()` is its edge back. */\n waiter: WaiterEntry;\n /** The bar the proof reached, as far as the client can know it. */\n obtained: AssuranceBar;\n};\n\n/** One row of the per-flight outcome summary. */\nexport type UpgradeWaiterSummary = {\n url?: string;\n method?: string;\n outcome: WaiterOutcome;\n obtained?: AssuranceBar;\n required?: AssuranceBar;\n};\n\n/**\n * The per-flight outcome summary: a pure function of `waiters` plus `proof`.\n *\n * `terminal` is what the prompt may report. The modal must NOT say \"success\"\n * while any waiter is re-challenged — for AAL3 endpoints with TOTP-only users\n * that is the NORMAL path, not an edge case, and a design cannot both claim the\n * server is the authority and delete the path by which its second refusal\n * reaches the user.\n */\nexport type UpgradeFlightSummary = {\n proof: UpgradeProof;\n waiters: UpgradeWaiterSummary[];\n terminal: 'success' | 'insufficient' | 'failed';\n /** The bars of the STRONGEST unmet waiter, so the message is honest. */\n insufficient?: { obtained: AssuranceBar; required: AssuranceBar };\n /** The proof failure, when the flight never produced a proof. */\n error?: Error;\n};\n\nexport type UpgradeControllerOptions = {\n transport: UpgradeTransport;\n prompt: UpgradePromptHandler;\n platform: PlatformAdapter;\n /** Defaults to the factors this SDK can complete. */\n factors?: readonly UpgradeFactor[];\n /** How many times the user may answer wrongly before the elevation gives up. */\n maxAttempts?: number;\n /**\n * The bar the elevation actually reached, when the host can read it — from the\n * `acr` claim of the freshly stored access token, for instance.\n *\n * Without it the controller reports the bar the ceremony was RANKED for, which\n * is the honest client-side approximation: the server scores `acr` from the\n * base session plus the verified factor and is the only authority, and a\n * replay that is re-challenged corrects the approximation immediately.\n */\n readObtainedBar?: () => AssuranceBar | undefined;\n /**\n * Called once per flight, in the settling transition, after every waiter has\n * reported. This is how the open prompt is given its terminal state, and how\n * the React layer gets exactly one terminal state per flight.\n */\n onFlightSettled?: (summary: UpgradeFlightSummary) => void;\n /**\n * Liveness guard: how long to wait for waiters to report before settling the\n * flight anyway. A waiter that never calls `report()` is an adapter bug, and\n * without a bound it would hold `inFlight` open forever — so every later\n * elevation would join a dead flight, which is a worse failure than settling\n * early. Defaults to 30s.\n */\n replaySettleTimeoutMs?: number;\n};\n\n/**\n * Raised when the user dismissed the upgrade rather than failing it.\n *\n * `cause` is carried for one case only: a prompt implementation that threw or\n * rejected is treated as a failed COLLECTION and settles on the cancellation\n * contract, because there is no answer and no proof — but the thrown error is\n * the only evidence of an integration bug, so it travels rather than being\n * swallowed.\n */\nexport class UpgradeCancelledError extends Error {\n readonly code = 'upgrade_cancelled' as const;\n /** Declared here rather than inherited: `Error.cause` is ES2022, this build targets ES2021 libs. */\n readonly cause?: unknown;\n\n constructor(cause?: unknown) {\n super('Upgrade authentication was cancelled');\n this.name = 'UpgradeCancelledError';\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n\n/** Raised when no registered factor can satisfy the challenge. */\nexport class UpgradeUnsatisfiableError extends Error {\n readonly code = 'upgrade_unsatisfiable' as const;\n\n constructor(message = 'No available authentication factor can satisfy this challenge') {\n super(message);\n this.name = 'UpgradeUnsatisfiableError';\n }\n}\n\n/**\n * The server writes `Attempts: 0, MaxAttempts: 5` on every NEWLY CREATED\n * challenge (`upgrade.go:177-180`), so the budget is per challenge, not per\n * session: a later sequential elevation gets its own and consumes nothing from\n * this one. Four local attempts therefore stop one short of the server's five,\n * which keeps the SDK from being the thing that burns the budget while leaving a\n * user who mistypes room to recover.\n */\nconst DEFAULT_MAX_ATTEMPTS = 4;\n\n/** Default liveness bound on waiter reports. See `replaySettleTimeoutMs`. */\nconst DEFAULT_SETTLE_TIMEOUT_MS = 30_000;\n\n/** A promise with its resolvers, which is all `elevate`'s per-waiter settlement needs. */\ntype Deferred<T> = { promise: Promise<T>; resolve: (value: T) => void; reject: (reason: unknown) => void };\n\nconst deferred = <T>(): Deferred<T> => {\n let resolve: (value: T) => void = () => undefined;\n let reject: (reason: unknown) => void = () => undefined;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n};\n\n/** A registered waiter plus the controller's own bookkeeping for it. */\ntype Registration = {\n entry: WaiterEntry;\n /** The bar THIS waiter arrived demanding. Checked against the proof. */\n demanded: AssuranceBar;\n /**\n * True only when the CALLER stated a bar on an imperative call.\n *\n * §13.3's `n/a` cell is the options-LESS imperative call: it states no bar and\n * has no protected operation, so it can never report `insufficient` (C24b).\n * Deriving \"stated\" from `demanded` is impossible once `demanded` falls back\n * to the challenge's own bar, and with `barMeets` no longer fail-open on an\n * unknown level that fallback would have started rejecting exactly the call\n * the spec says never rejects.\n */\n statedBar: boolean;\n settlement: Deferred<UpgradeElevation>;\n reported: boolean;\n};\n\n/** The bar a challenge demands, in the shape the errors and the hook share. */\nexport const barOf = (challenge: UpgradeChallenge): AssuranceBar => ({\n acrValues: challenge.acrValues,\n level: challenge.requiredLevel,\n maxAge: challenge.maxAge,\n});\n\n/**\n * Does `obtained` reach `required`?\n *\n * Only the `acr` axis is decidable client-side. A completed proof always\n * satisfies a freshness demand, since it just happened, and a requirement with\n * no level constrains nothing on this axis — so both of those are `true`.\n *\n * **An UNKNOWN obtained level does not meet a bar.** This is the one place in\n * the feature where fail-open is wrong, and §13.4 revision 6 draws the line\n * explicitly: fail-open governs *recovery* — availability, not logging the user\n * out, not blocking the app — never the *granting of access*. Answering \"meets\"\n * for a level we could not read is granting access on no evidence.\n *\n * It is NOT pre-judging satisfiability, and the difference is what §13.2 line\n * 1599 and §13.4 line 2213 protect: nothing is refused outright, no elevation\n * is declined, no factor is filtered. The SDK merely declines to ASSERT that a\n * bar was reached; the server's replay re-challenge decides, and that surfaces\n * as §13.3's `insufficient` with both bars attached. The only waiters this can\n * settle locally are imperative ones that STATED a bar, which have no replay to\n * ask the server about.\n */\nexport const barMeets = (obtained: AssuranceBar, required: AssuranceBar): boolean => {\n if (required.level === undefined) {\n return true;\n }\n if (obtained.level === undefined) {\n return false;\n }\n return obtained.level >= required.level;\n};\n\n/** Field-wise merge of two requirements: `acr` tightens up, freshness tightens down. */\nexport const mergeChallenges = (current: UpgradeChallenge, incoming: UpgradeChallenge): UpgradeChallenge => {\n const level =\n current.requiredLevel === undefined\n ? incoming.requiredLevel\n : incoming.requiredLevel === undefined\n ? current.requiredLevel\n : Math.max(current.requiredLevel, incoming.requiredLevel);\n\n // The raw list follows the bar that won, so a diagnostic never shows a string\n // that did not set the requirement.\n const acrValues =\n level === undefined\n ? (current.acrValues ?? incoming.acrValues)\n : level === current.requiredLevel\n ? current.acrValues\n : incoming.acrValues;\n\n const maxAge =\n current.maxAge === undefined\n ? incoming.maxAge\n : incoming.maxAge === undefined\n ? current.maxAge\n : Math.min(current.maxAge, incoming.maxAge);\n\n const channels = Array.from(new Set([...(current.channels ?? []), ...(incoming.channels ?? [])]));\n\n return {\n acrValues,\n requiredLevel: level,\n maxAge,\n status: current.status,\n ...(channels.length > 0 ? { channels } : {}),\n };\n};\n\n/** Is `incoming` STRICTLY stronger than `current` on either axis? */\nconst isStrictlyStronger = (current: UpgradeChallenge, incoming: UpgradeChallenge): boolean => {\n const acrRose =\n incoming.requiredLevel !== undefined &&\n (current.requiredLevel === undefined || incoming.requiredLevel > current.requiredLevel);\n const freshnessTightened =\n incoming.maxAge !== undefined && (current.maxAge === undefined || incoming.maxAge < current.maxAge);\n return acrRose || freshnessTightened;\n};\n\nexport class UpgradeController {\n private readonly transport: UpgradeTransport;\n private readonly prompt: UpgradePromptHandler;\n private readonly platform: PlatformAdapter;\n private readonly registry: Map<UpgradeFactorType, UpgradeFactor>;\n private readonly available: readonly UpgradeFactor[];\n private readonly maxAttempts: number;\n private readonly readObtainedBar?: () => AssuranceBar | undefined;\n private readonly onFlightSettled?: (summary: UpgradeFlightSummary) => void;\n private readonly settleTimeoutMs: number;\n\n /** The current state. Public for diagnostics and tests; nothing outside writes it. */\n private state: UpgradeFlightState = 'idle';\n\n /**\n * The elevation currently running.\n *\n * Single-flight is the STATE SET, not a flag: `elevate` returns the existing\n * flight in every state but `idle`, and starts one only from `idle`. A screen\n * that fires several protected requests at once gets several challenges, and\n * every one of them must land on ONE prompt — otherwise the user sees a stack\n * of modals and every extra elevation burns a challenge for nothing.\n */\n private inFlight: Promise<void> | null = null;\n\n /** The merged requirement. Written at registration and at every join. */\n private requirement: UpgradeChallenge | null = null;\n\n /** Non-null from the moment the prompt reports a selection, and from nowhere else. */\n private selected: UpgradeFactorType | null = null;\n\n /** The requirement in force when the factor was selected — what the ceremony was ranked for. */\n private rankedRequirement: UpgradeChallenge | null = null;\n\n private registrations: Registration[] = [];\n\n private proof: UpgradeProof | null = null;\n\n /** Factors the SERVER refused for this user, dropped for the duration of this flight only. */\n private pruned = new Set<UpgradeFactorType>();\n\n /** The request handed to the open prompt. Its `onRequirementRaised` slot is branch 2's channel. */\n private openPrompt: UpgradePromptRequest | null = null;\n\n /** Set while `awaitReports` is waiting; called by the reporting path. See H7. */\n private reportsSettled?: () => void;\n\n constructor(options: UpgradeControllerOptions) {\n this.transport = options.transport;\n this.prompt = options.prompt;\n this.platform = options.platform;\n this.available = options.factors ?? DEFAULT_UPGRADE_FACTORS;\n this.registry = factorRegistry(this.available);\n this.maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n this.readObtainedBar = options.readObtainedBar;\n this.onFlightSettled = options.onFlightSettled;\n this.settleTimeoutMs = options.replaySettleTimeoutMs ?? DEFAULT_SETTLE_TIMEOUT_MS;\n }\n\n /** The current lifecycle state. Read-only. */\n get currentState(): UpgradeFlightState {\n return this.state;\n }\n\n /** True while a flight is open — from the first challenge until the last waiter settled. */\n get active(): boolean {\n return this.inFlight !== null;\n }\n\n /**\n * Elevates the session so the challenged request can be retried.\n *\n * Every call registers a waiter, including the imperative one. Concurrent\n * callers share one elevation and one prompt; the returned promise settles for\n * THIS caller when the proof settles, and carries that caller's own waiter\n * entry so it can report its replay's outcome back.\n *\n * It resolves when the server accepted a factor and recorded the elevation. It\n * rejects for cancelled, exhausted, unsatisfiable, unavailable, session-changed\n * and non-retryable transport failure — and, for a waiter whose own demanded\n * bar the proof did not reach, with `UpgradeInsufficientError`.\n */\n elevate(challenge: UpgradeChallenge, waiter: Waiter = { kind: 'imperative' }): Promise<UpgradeElevation> {\n const registration = this.register(challenge, waiter);\n\n if (this.state === 'idle') {\n // T1 — `inFlight` is created BEFORE the prompt opens, so there is no\n // instant in which a modal is on screen and a second challenge finds the\n // controller idle.\n this.state = 'collecting';\n this.requirement = challenge;\n this.inFlight = this.runFlight();\n } else {\n this.join(challenge);\n // T8 and later: the proof has already settled, so this waiter cannot be\n // released by the flight's own loop — it is settled against the existing\n // proof right here. If the proof meets its bar it replays with the others;\n // if not, it is `insufficient` and its caller's retry drives a fresh,\n // SEQUENTIAL elevation. Either way no second prompt opens and no second\n // server challenge is created.\n const settled = this.proof;\n if (settled?.status === 'proven') {\n this.release(registration, settled.obtained);\n } else if (settled?.status === 'failed') {\n this.receiveReport(registration, { outcome: 'failed' });\n registration.settlement.reject(settled.reason);\n }\n }\n\n return registration.settlement.promise;\n }\n\n /** Registers a waiter and the bar it arrived demanding. */\n private register(challenge: UpgradeChallenge, waiter: Waiter): Registration {\n const statedBar = waiter.kind === 'imperative' && waiter.requested !== undefined;\n const demanded = waiter.kind === 'imperative' && waiter.requested ? waiter.requested : barOf(challenge);\n const registration: Registration = {\n demanded,\n statedBar,\n settlement: deferred<UpgradeElevation>(),\n reported: false,\n entry: {\n ...waiter,\n outcome: 'pending',\n report: (result: WaiterReport) => this.receiveReport(registration, result),\n },\n };\n this.registrations.push(registration);\n return registration;\n }\n\n /**\n * Folds a joining challenge into the running flight.\n *\n * The boundary is factor SELECTION. Before it, nothing is on the wire and\n * there is no race, so a strictly stronger challenge folds in place: the\n * requirement rises, the list is re-ranked for the higher bar, and the open\n * prompt is told. After it, a ceremony is running — a `prepare` round trip, a\n * TOTP commit, a WebAuthn ceremony holding the platform authenticator — and\n * neither interrupting it nor starting a second concurrent flight is\n * acceptable, so it completes at the bar it was ranked for and each waiter's\n * own replay decides its own fate.\n */\n private join(incoming: UpgradeChallenge): void {\n const current = this.requirement;\n if (!current) {\n this.requirement = incoming;\n return;\n }\n\n const stronger = isStrictlyStronger(current, incoming);\n this.requirement = mergeChallenges(current, incoming);\n\n if (!stronger) {\n // Branch 1 — the common case: two guards on one screen demanding the same\n // bar. Nothing observable changes.\n return;\n }\n\n if (this.state === 'collecting' && this.selected === null) {\n // Branch 2 — fold in place. One prompt, one ceremony, one challenge.\n this.openPrompt?.onRequirementRaised?.({ challenge: this.requirement, factors: this.offeredFactors() });\n }\n // Branch 3 (`proving`, `replaying`, `settling`, `done`) — the merged\n // requirement is recorded for diagnostics and for the NEXT elevation, but it\n // does not reshape the ceremony in progress. A TOTP commit cannot become an\n // AAL3 proof by rewriting a requirement, so the honest options were\n // \"interrupt the ceremony\" or \"let it finish and tell the late joiner the\n // truth\", and the second is chosen.\n }\n\n /** The factors to offer right now: ranked for the merged bar, minus what the server refused. */\n private offeredFactors(): readonly UpgradeFactor[] {\n const usable = this.available.filter((factor) => !this.pruned.has(factor.type));\n const challenge = this.requirement;\n return challenge ? selectFactors(usable, challenge) : usable;\n }\n\n /** The waiter's inbound edge. Idempotent — a second call is ignored. */\n private receiveReport(registration: Registration, result: WaiterReport): void {\n if (registration.reported) {\n return;\n }\n registration.reported = true;\n registration.entry.outcome = result.outcome;\n if (result.obtained) {\n registration.entry.obtained = result.obtained;\n }\n if (result.required) {\n registration.entry.required = result.required;\n }\n // The reporting path RESOLVES the wait, rather than a timer discovering it.\n // This is the only edge on which a waiter becomes non-pending, so it is the\n // only place the check belongs.\n this.notifyReportsSettled();\n }\n\n /** Resolves `awaitReports`' promise when the last outstanding report lands. */\n private notifyReportsSettled(): void {\n if (!this.reportsSettled || this.registrations.some((registration) => !registration.reported)) {\n return;\n }\n const settle = this.reportsSettled;\n this.reportsSettled = undefined;\n settle();\n }\n\n /**\n * The flight, start to finish.\n *\n * Nothing here clears state: clearing is one synchronous step at the very end,\n * after the prompt has settled, so nothing can observe a half-cleared\n * controller and no waiter can report into a flight the controller has\n * forgotten.\n */\n private async runFlight(): Promise<void> {\n // `prove()` is written not to throw, and this catch is the backstop that\n // makes \"every path settles\" structural rather than a property of one\n // function. An escaping throw would leave `inFlight` a rejected promise\n // with the state stuck mid-flight: every waiter already registered would\n // hang forever, and every later challenge would join a flight that can\n // never settle. H5's prompt-that-throws was one way in; this closes the\n // class, not the instance.\n let proof: UpgradeProof;\n try {\n proof = await this.prove();\n } catch (e) {\n proof = { status: 'failed', reason: e instanceof Error ? e : new Error(String(e)) };\n }\n this.proof = proof;\n\n if (proof.status === 'failed') {\n // T7 — no replay is EVER performed without a completed proof. A cancelled\n // elevation produced nothing to replay with, and re-issuing the user's\n // mutation after they declined would be the worst possible reading of\n // \"retry\".\n for (const registration of this.registrations) {\n this.receiveReport(registration, { outcome: 'failed' });\n registration.settlement.reject(proof.reason);\n }\n } else {\n // T6 → `replaying`. Each waiter is released to replay its OWN bound\n // request, and whether the proof satisfied that waiter's demand is the\n // SERVER's answer to that replay, never a client-side comparison made\n // here (§13.4). Only an imperative waiter, which has no request to\n // replay, is classified in `release()`.\n this.state = 'replaying';\n for (const registration of this.registrations) {\n this.release(registration, proof.obtained);\n }\n await this.awaitReports();\n }\n\n // T9 → T10 — the summary is complete, the prompt is settled exactly once.\n this.state = 'settling';\n const summary = this.summarise(proof);\n try {\n this.onFlightSettled?.(summary);\n } catch {\n // A consumer's settlement handler must not be able to wedge the flight\n // open: the clearing below is what makes the next call start from `idle`.\n }\n\n // T11 — one synchronous step, no `await` inside it.\n this.state = 'done';\n this.inFlight = null;\n this.requirement = null;\n this.selected = null;\n this.rankedRequirement = null;\n this.registrations = [];\n this.proof = null;\n this.pruned = new Set();\n this.openPrompt = null;\n this.reportsSettled = undefined;\n this.state = 'idle';\n }\n\n /**\n * Settles one waiter against the proof.\n *\n * An imperative waiter has no operation to replay, so it is classified here\n * and nowhere else: with no stated bar it simply succeeded, and with a stated\n * bar the proof is checked against it — which is what stops an AAL3 imperative\n * call that joined an AAL2 flight from resolving as though it had been\n * elevated to AAL3.\n *\n * A REQUEST waiter is never pre-judged here, and §13 is explicit about it\n * twice: \"a demand above 3 is never refused client-side — the SDK does not\n * pre-judge satisfiability (§13.4) — it ranks factors for the highest bar it\n * knows and lets the server's replay re-challenge decide, surfacing as\n * §13.3's `insufficient`\", and \"never refuse outright on the default path\".\n * The reason is not conformance alone: the client-side comparison is the\n * fragile half — ACR ordinals, the minimum-of-list rule — so refusing here\n * can skip a replay the server would have accepted, and it loses the\n * `elevated-insufficient` diagnostic the replay path raises. So the waiter is\n * released whatever the bar comparison says; `handleChallenge` replays, reads\n * the server's answer, and reports `insufficient` if that is what it is.\n */\n private release(registration: Registration, obtained: AssuranceBar): void {\n if (registration.entry.kind === 'imperative') {\n // Only a STATED bar is checked. An options-less imperative call resolves\n // `{status:'elevated'}` and never `insufficient` — §13.3's `n/a` cell.\n if (registration.statedBar && !barMeets(obtained, registration.demanded)) {\n this.receiveReport(registration, { outcome: 'insufficient', obtained, required: registration.demanded });\n registration.settlement.reject(new UpgradeInsufficientError(obtained, registration.demanded));\n return;\n }\n this.receiveReport(registration, { outcome: 'replayed', obtained });\n registration.settlement.resolve({ waiter: registration.entry, obtained });\n return;\n }\n\n registration.settlement.resolve({ waiter: registration.entry, obtained });\n }\n\n /**\n * Waits for every released waiter to report, under the liveness bound.\n *\n * Report-driven, not polled. The previous implementation re-armed a\n * `setTimeout(…, 0)` until the last waiter reported, which burned a timer per\n * event-loop turn for the whole replay window and made the state machine\n * untestable under fake timers — `runAllTimers()` never terminates against a\n * self-re-arming zero-delay timer. `receiveReport` is the one edge on which a\n * waiter stops being pending, so it resolves this promise directly.\n *\n * The deadline stays: a waiter that never reports is an adapter bug, and\n * settling it as `failed` is better than holding `inFlight` open forever,\n * which would make every later elevation join a dead flight.\n */\n private awaitReports(): Promise<void> {\n if (!this.registrations.some((registration) => !registration.reported)) {\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve) => {\n const deadline = setTimeout(() => {\n this.reportsSettled = undefined;\n for (const registration of this.registrations) {\n if (!registration.reported) {\n registration.reported = true;\n registration.entry.outcome = 'failed';\n }\n }\n resolve();\n }, this.settleTimeoutMs);\n\n this.reportsSettled = () => {\n clearTimeout(deadline);\n resolve();\n };\n });\n }\n\n /** The summary: a pure function of the waiters and the proof. */\n private summarise(proof: UpgradeProof): UpgradeFlightSummary {\n const waiters: UpgradeWaiterSummary[] = this.registrations.map((registration) => ({\n ...(registration.entry.kind === 'request' ? { url: registration.entry.url, method: registration.entry.method } : {}),\n outcome: registration.entry.outcome,\n ...(registration.entry.obtained ? { obtained: registration.entry.obtained } : {}),\n ...(registration.entry.required ? { required: registration.entry.required } : {}),\n }));\n\n if (proof.status === 'failed') {\n return { proof, waiters, terminal: 'failed', error: proof.reason };\n }\n\n // The strongest unmet waiter supplies the message, so it reads \"elevated to\n // AAL2; this action requires AAL3\" rather than an arbitrary one of several.\n let strongest: UpgradeWaiterSummary | undefined;\n for (const waiter of waiters) {\n if (waiter.outcome !== 'insufficient') {\n continue;\n }\n const level = waiter.required?.level ?? 0;\n if (!strongest || level > (strongest.required?.level ?? 0)) {\n strongest = waiter;\n }\n }\n\n if (strongest) {\n return {\n proof,\n waiters,\n terminal: 'insufficient',\n insufficient: {\n obtained: strongest.obtained ?? proof.obtained,\n required: strongest.required ?? {},\n },\n };\n }\n\n // A waiter that failed, or that never reported at all, is not a success.\n //\n // `success` is a claim about the OPERATIONS, not about the proof: the proof\n // succeeding is already `proof.status === 'proven'` above. Falling through\n // to `success` here let a proven proof plus a `failed` waiter tell the modal\n // \"Verification complete\" for a request that never completed — and a\n // `pending` waiter means the liveness bound expired with an adapter still\n // owing a report, which is even less of a success.\n if (waiters.some((waiter) => waiter.outcome === 'failed' || waiter.outcome === 'pending')) {\n return { proof, waiters, terminal: 'failed' };\n }\n\n // Every waiter replayed successfully and none stated an unmet bar. A waiter\n // that stated a bar IS a waiter, so \"no waiters → success\" can no longer\n // launder an unmet requested bar into a success message.\n return { proof, waiters, terminal: 'success' };\n }\n\n /**\n * Collects an answer and proves it, re-prompting on every recoverable failure.\n *\n * The prepare round is re-run on every attempt rather than cached: a challenge\n * is single-use and the server consumes it atomically, so a failed attempt has\n * already spent it. Reusing the id would fail with\n * `ErrorUpgradeChallengeInvalid` and look like a bad code to the user.\n */\n private async prove(): Promise<UpgradeProof> {\n let previousError: Error | undefined;\n\n for (let attempt = 0; attempt < this.maxAttempts; attempt++) {\n const factors = this.offeredFactors();\n if (factors.length === 0) {\n // Reachable without any enrolment knowledge: a caller-supplied `factors`\n // option whose intersection with what the SDK can complete is empty, or\n // flight-local pruning that removed the last factor.\n return { status: 'failed', reason: new UpgradeUnsatisfiableError() };\n }\n\n const challenge = this.requirement as UpgradeChallenge;\n\n // A prompt that throws synchronously, or rejects, is a FAILED COLLECTION.\n //\n // There is no answer, so there is nothing to prove and no proof to\n // replay — exactly the shape of a cancellation, which is why it settles\n // on the per-surface cancellation contract (fetch resolves the untouched\n // 401/403, axios and the imperative surface reject\n // `UpgradeCancelledError`, the hook may resolve `null`). Re-prompting\n // would call the same broken implementation again; letting the throw\n // escape left the controller in `collecting` forever, so every later\n // challenge joined a flight that could never settle. The thrown error\n // travels on `cause` so an integration bug is still visible.\n let answer: UpgradePromptResult;\n try {\n answer = await this.collect(challenge, factors, previousError);\n } catch (e) {\n return { status: 'failed', reason: new UpgradeCancelledError(e) };\n }\n\n if (answer.type === 'cancel') {\n return { status: 'failed', reason: new UpgradeCancelledError() };\n }\n\n const factor = this.registry.get(answer.factor);\n if (!factor) {\n previousError = new UpgradeUnsatisfiableError(`Unknown authentication factor: ${answer.factor}`);\n this.state = 'collecting';\n this.selected = null;\n continue;\n }\n\n try {\n await this.proveAndCommit(factor, answer.code);\n const obtained = this.readObtainedBar?.() ?? barOf(this.rankedRequirement ?? challenge);\n return { status: 'proven', obtained };\n } catch (e) {\n // A cancelled ceremony ends the elevation — the user made a choice, and\n // re-prompting them would be nagging, not recovery.\n if (e instanceof UpgradeFactorError && e.cancelled) {\n return { status: 'failed', reason: new UpgradeCancelledError() };\n }\n\n const terminal = this.classifyProofFailure(e);\n if (terminal) {\n return { status: 'failed', reason: terminal };\n }\n\n if (e instanceof UpgradeTransportError && e.kind === 'not_registered') {\n // The server stating a fact about this user, not the SDK guessing one:\n // drop that factor for THIS flight only and re-prompt with the rest.\n // Never persisted, and it does not affect a later flight.\n this.pruned.add(factor.type);\n }\n\n previousError = e as Error;\n // T5 — a recoverable failure returns to `collecting` with the prompt\n // re-shown; `requirement` and the waiters are untouched.\n this.state = 'collecting';\n this.selected = null;\n }\n }\n\n // The local attempt budget is spent, and that fact is TERMINAL.\n //\n // Returning `previousError` returned the last answer's own error, which is\n // usually a `wrong_code` with `retryable === true` — so every surface above\n // read \"ask them again\" for a flight that will never ask again, and the UI\n // re-prompted forever against a budget the server had already closed. The\n // budget being spent is what must travel; the last failure travels on\n // `cause`.\n return {\n status: 'failed',\n reason: new UpgradeTransportError({\n kind: 'attempts_exhausted',\n ...(previousError ? { cause: previousError } : {}),\n }),\n };\n }\n\n /** Opens (or re-opens) the prompt and wires the two prompt → controller edges. */\n private collect(\n challenge: UpgradeChallenge,\n factors: readonly UpgradeFactor[],\n previousError: Error | undefined,\n ): Promise<UpgradePromptResult> {\n const request: UpgradePromptRequest = {\n challenge,\n factors,\n ...(previousError ? { previousError } : {}),\n onFactorSelected: (factor: UpgradeFactorType) => {\n // T3 — the ONLY place `selected` becomes non-null.\n if (this.state === 'collecting') {\n this.state = 'proving';\n }\n this.selected = factor;\n this.rankedRequirement = this.requirement;\n },\n };\n\n // The prompt subscribes by assigning `request.onRequirementRaised`\n // synchronously, so the object is kept rather than the function: branch 2\n // reads the slot at the moment it needs it.\n this.openPrompt = request;\n return this.prompt(request);\n }\n\n /** Maps a proof failure to a terminal reason, or `undefined` when the user may retry. */\n private classifyProofFailure(e: unknown): Error | undefined {\n if (e instanceof UpgradeInsufficientError || e instanceof UpgradeUnavailableError) {\n return e;\n }\n if (e instanceof UpgradeSessionChangedError) {\n return e;\n }\n if (e instanceof UpgradeTransportError) {\n switch (e.kind) {\n case 'unavailable':\n // Wrapped, like `session_changed` below, because §13.3's table names\n // `UpgradeUnavailableError` as the rejection on the two throwing\n // surfaces. The fetch and axios adapters re-mapped this kind in\n // `coreOutcomeToError`, so only the imperative surface — which goes\n // straight to the controller — saw the unwrapped\n // `UpgradeTransportError`, and the two surfaces disagreed.\n return new UpgradeUnavailableError(e.message);\n case 'exhausted':\n case 'attempts_exhausted':\n case 'fatal':\n case 'protocol':\n case 'network':\n return e;\n case 'session_changed':\n return new UpgradeSessionChangedError(e.message);\n default:\n // `wrong_code`, `challenge_spent`, `not_registered`,\n // `prepare_not_required` — the user answers again.\n return undefined;\n }\n }\n return undefined;\n }\n\n /** Runs one full proof for a single factor. */\n private async proveAndCommit(factor: UpgradeFactor, code: string | undefined): Promise<void> {\n const prepared = factor.requiresPrepare ? await this.transport.prepare(factor.type) : undefined;\n const assertion = await factor.assert({ code, prepared, platform: this.platform });\n await this.transport.commit(factor.type, assertion);\n }\n}\n","/**\n * The request snapshot: ONE normalised, immutable description of the request\n * that carried a challenge, captured before dispatch.\n *\n * ## Why this module exists\n *\n * Every authorization decision the upgrade feature makes — is this URL in scope,\n * what does the diagnostic name, what does the replay re-send — must be made on\n * the SAME description of the request, and that description must be the one the\n * platform will actually act on. Three defects in this feature were the same\n * defect from three angles:\n *\n * 1. the scope check hand-parsed the caller's URL *string*, while the browser\n * normalises it before dispatch;\n * 2. the challenge context re-read the caller's live `input` at challenge\n * time, while the replay was pinned to the plan-time URL and body;\n * 3. the replay could re-send a different header set than the one that was\n * examined.\n *\n * So the request is normalised and frozen ONCE, here, and the snapshot is the\n * only thing the scope check, the challenge context, the diagnostics and the\n * replay are given. Nothing downstream holds a live reference to the caller's\n * `Request`, `RequestInit` or axios config.\n *\n * ## The normalisation rule, and why it is the platform's own parser\n *\n * **Resolve with `new URL(raw, base)` and compare `origin` on the result.**\n *\n * WHATWG URL parsing is not a string scan. For a special scheme it converts\n * backslashes to forward slashes and strips ASCII tab (0x09), LF (0x0A) and CR\n * (0x0D) *before* parsing. A scope check that inspects the raw string therefore\n * sees a same-origin relative path where the browser resolves a foreign origin:\n * `'/\\\\evil.example/x'`, `'/\\t/evil.example/x'` and `'\\\\\\\\evil.example/x'` all\n * resolve to `https://evil.example` (§13.4's SF-3, SF-4, SF-7). Each one would\n * attach a step-up-elevated credential to a request leaving for an\n * attacker-controlled host.\n *\n * **The percent-encoded forms are the mirror image, and they are POSITIVES.**\n * `'/%5Cevil.example/x'`, `'/%09/evil.example/x'` and `'/%2F%2Fevil.example/x'`\n * all resolve to the page's OWN origin and must stay in scope (SF-9..SF-12):\n * the parser decodes nothing that could change the authority, so `%5C` is not a\n * backslash, `%09` stays an ordinary path character and `%2F%2F` does not begin\n * an authority. They are legitimate same-origin requests with odd-looking paths.\n *\n * That asymmetry is the entire argument for resolving rather than filtering. A\n * list of rejected characters is a denylist against a parser, and the parser is\n * the authority: it is bypassed by the raw forms, whose bytes it rewrites before\n * it parses them, AND it is wrong in the opposite direction on the encoded\n * forms, which it refuses as hostile when the platform treats them as plain\n * path text. Resolving first gets both directions — and the vectors nobody has\n * thought of yet — for free.\n *\n * The operative consequence for anyone editing this file: hand the input to\n * `new URL` **undecoded and un-normalised**. Any decoding, trimming or\n * rewriting before that call reintroduces one half of the defect or the other.\n *\n * Off the browser there is no document, so there is no \"same origin\" to resolve\n * against. A relative URL is then resolved against a RESERVED sentinel origin\n * (`.invalid` is reserved by RFC 2606 and can never be a real host), which keeps\n * exactly one code path: a URL that resolves to the sentinel's origin is a\n * genuinely relative one, and anything that steers the authority elsewhere —\n * `//evil.example/x` included — resolves away from it and is no longer relative.\n * The sentinel is `https:` precisely because it must be a SPECIAL scheme: the\n * backslash and tab rules above apply only to special schemes, so a custom\n * scheme would be a different parser and would miss the vectors.\n */\n\nimport type { BodyKind } from './replay';\n\n/** One captured header. Lower-cased name, value verbatim. */\nexport type SnapshotHeader = readonly [name: string, value: string];\n\n/**\n * The frozen description of one request.\n *\n * Immutable by construction: `Object.freeze` on the snapshot and on its header\n * list, and the body already captured by value where the shape is mutable. It\n * carries no live reference to anything the caller can still change.\n */\nexport type RequestSnapshot = {\n /** Exactly what the caller passed, for diagnostics only. NEVER for a decision. */\n readonly raw: string;\n /**\n * The absolute, platform-normalised URL the request will actually be sent to,\n * or `raw` when it could not be resolved at all (a malformed URL).\n *\n * This is the value every decision and the replay use.\n */\n readonly href: string;\n /**\n * The URL to actually DISPATCH a replay to.\n *\n * Identical to `href` wherever a real base existed — a browser, or any host\n * that resolved the URL absolutely — which is the case every decision cares\n * about. It falls back to `raw` only when resolution used the reserved\n * sentinel base, i.e. a relative URL on a host with no document: the\n * sentinel's origin is a reserved name that resolves to nothing, so sending\n * `href` there would send the request into a hole. The transport's own base\n * (axios' `baseURL`, the runtime's fetch base) then resolves the captured\n * string, exactly as it did for the original send.\n *\n * It is still a captured STRING, never the caller's object, so the TOCTOU\n * this module exists to close stays closed on that path too.\n */\n readonly dispatchUrl: string;\n /** The resolved origin, or `undefined` when the URL could not be resolved. */\n readonly origin?: string;\n /** The resolved pathname, or the raw path when the URL could not be resolved. */\n readonly pathname: string;\n /** False when the URL could not be parsed even against the base. */\n readonly resolved: boolean;\n /**\n * True when the resolved origin is the base's own — the page's origin on a\n * browser, the sentinel's off it, which means \"genuinely relative\".\n *\n * This is what a same-origin path-prefix pattern (`backend: '/api'`) tests,\n * and it is the one bit that makes `'/\\\\evil.example/x'` fail where `'/api/x'`\n * passes.\n */\n readonly relativeToBase: boolean;\n /** Upper-cased, with the same precedence the transport itself applies. */\n readonly method: string;\n /** The effective header set, captured once. Frozen. */\n readonly headers: readonly SnapshotHeader[];\n /** How the body can be re-sent, which is what decides replayability. */\n readonly bodyKind: BodyKind;\n /**\n * The already-captured body. `undefined` means \"the caller passed no body\n * key\", which is distinct from an explicit `null`.\n */\n readonly body?: unknown;\n};\n\n/**\n * The reserved origin a relative URL is resolved against when there is no\n * document. RFC 2606 reserves `.invalid`, so no real deployment can collide\n * with it, and `https:` keeps the parser on its special-scheme rules.\n */\nexport const RELATIVE_BASE = 'https://passflow-relative.invalid';\n\n/**\n * The base relative URLs resolve against, read at CALL time.\n *\n * `document.baseURI` first, because that — not `location.href` — is what `fetch`\n * and XHR resolve a relative URL against, and a `<base href>` moves it.\n * Reading a global at module scope is the SSR failure this feature avoids\n * everywhere, so this is a function.\n */\nconst resolutionBase = (): string => {\n const scope = globalThis as {\n document?: { baseURI?: unknown };\n location?: { href?: unknown };\n };\n const baseUri = scope.document?.baseURI;\n if (typeof baseUri === 'string' && baseUri !== '') {\n return baseUri;\n }\n const href = scope.location?.href;\n return typeof href === 'string' && href !== '' && href !== 'about:blank' ? href : RELATIVE_BASE;\n};\n\n/** What a URL normalised to. Pure: it takes the base, it does not read one. */\nexport type NormalisedUrl = {\n href: string;\n origin?: string;\n pathname: string;\n resolved: boolean;\n relativeToBase: boolean;\n /** True when no real base existed and the reserved sentinel was used. */\n sentinelBase: boolean;\n};\n\n/**\n * Resolves one URL with the platform's own parser.\n *\n * `base` defaults to the document base (or the sentinel off the browser). The\n * ONLY comparison any caller may make afterwards is on `origin` — never on the\n * raw string.\n */\nexport const normaliseUrl = (raw: string, base: string = resolutionBase()): NormalisedUrl => {\n const sentinelBase = base === RELATIVE_BASE;\n let baseOrigin: string | undefined;\n try {\n baseOrigin = new URL(base).origin;\n } catch {\n baseOrigin = undefined;\n }\n\n let resolved: URL | undefined;\n try {\n resolved = new URL(raw, base);\n } catch {\n resolved = undefined;\n }\n\n if (!resolved) {\n // Unparseable even against a base. It is not relative and it is not any\n // origin, so every origin comparison below fails and the request is out of\n // scope — the conservative reading used throughout this feature.\n const path = raw.split(/[?#]/)[0] ?? raw;\n return { href: raw, pathname: path, resolved: false, relativeToBase: false, sentinelBase };\n }\n\n return {\n href: resolved.href,\n origin: resolved.origin,\n pathname: resolved.pathname,\n resolved: true,\n relativeToBase: baseOrigin !== undefined && resolved.origin === baseOrigin,\n sentinelBase,\n };\n};\n\n/** Lower-cases a header name once, so every later comparison is a plain `===`. */\nconst headerEntry = (name: string, value: unknown): SnapshotHeader | undefined =>\n typeof value === 'string' ? ([name.toLowerCase(), value] as const) : undefined;\n\n/**\n * Captures a header bag by value.\n *\n * Every shape the two transports produce arrives here: a DOM `Headers`, an\n * `AxiosHeaders`, a plain object, an entry array, or an array-valued node bag.\n * Copying is the point — the caller may still hold and mutate the original.\n */\nexport const captureHeaders = (headers: unknown): readonly SnapshotHeader[] => {\n const captured: SnapshotHeader[] = [];\n\n if (headers === undefined || headers === null) {\n return Object.freeze(captured);\n }\n\n // A `Headers` (or anything with the same iteration contract) enumerates\n // lower-cased, already-combined entries.\n const iterable = headers as { forEach?: unknown; entries?: unknown };\n if (typeof iterable.forEach === 'function' && typeof iterable.entries === 'function') {\n (headers as Headers).forEach((value, name) => {\n const entry = headerEntry(name, value);\n if (entry) {\n captured.push(entry);\n }\n });\n return Object.freeze(captured);\n }\n\n if (Array.isArray(headers)) {\n for (const pair of headers as unknown[]) {\n if (Array.isArray(pair) && pair.length >= 2) {\n const entry = headerEntry(String(pair[0]), pair[1]);\n if (entry) {\n captured.push(entry);\n }\n }\n }\n return Object.freeze(captured);\n }\n\n if (typeof headers === 'object') {\n for (const [name, value] of Object.entries(headers as Record<string, unknown>)) {\n // axios' node bag can hold an array per name; join it the way the wire does.\n const flat = Array.isArray(value) ? value.join(', ') : value;\n const entry = headerEntry(name, flat);\n if (entry) {\n captured.push(entry);\n }\n }\n }\n\n return Object.freeze(captured);\n};\n\n/** Reads a captured header. Case is already normalised, so this is exact. */\nexport const snapshotHeader = (snapshot: RequestSnapshot, name: string): string | undefined => {\n const wanted = name.toLowerCase();\n for (const [header, value] of snapshot.headers) {\n if (header === wanted) {\n return value;\n }\n }\n return undefined;\n};\n\n/** What a caller must supply to take a snapshot. Everything else is derived. */\nexport type RequestSnapshotInit = {\n url: string;\n method?: string;\n headers?: unknown;\n bodyKind: BodyKind;\n /** Pass the key only when the caller supplied a body, so `undefined` stays meaningful. */\n body?: unknown;\n /** Overrides the document base. Tests and non-browser hosts only. */\n base?: string;\n};\n\n/**\n * Takes the snapshot. Call it ONCE per request, BEFORE dispatch.\n *\n * Frozen on the way out: a snapshot that could be edited after the scope\n * decision would reintroduce the very TOCTOU this module removes.\n */\nexport const createRequestSnapshot = (init: RequestSnapshotInit): RequestSnapshot => {\n const normalised = init.base === undefined ? normaliseUrl(init.url) : normaliseUrl(init.url, init.base);\n\n const snapshot: RequestSnapshot = {\n raw: init.url,\n href: normalised.href,\n dispatchUrl: normalised.sentinelBase || !normalised.resolved ? init.url : normalised.href,\n ...(normalised.origin === undefined ? {} : { origin: normalised.origin }),\n pathname: normalised.pathname,\n resolved: normalised.resolved,\n relativeToBase: normalised.relativeToBase,\n method: (init.method ?? 'GET').toUpperCase(),\n headers: captureHeaders(init.headers),\n bodyKind: init.bodyKind,\n ...('body' in init ? { body: init.body } : {}),\n };\n\n return Object.freeze(snapshot);\n};\n\n/**\n * Applies the §5.5 credential swap to a captured header set, by EXACT match.\n *\n * Equality with the token we hold is what proves the header was ours; any other\n * value is forwarded untouched, because rewriting a header we do not own would\n * be a security bug. Returns the same frozen array when nothing changed, so the\n * common path allocates nothing.\n */\nexport const swapAuthorization = (\n headers: readonly SnapshotHeader[],\n swap?: { from: string; to: string },\n): readonly SnapshotHeader[] => {\n if (!swap || swap.from === swap.to) {\n return headers;\n }\n let changed = false;\n const next = headers.map<SnapshotHeader>(([name, value]) => {\n if (name === 'authorization' && value === swap.from) {\n changed = true;\n return [name, swap.to] as const;\n }\n return [name, value] as const;\n });\n return changed ? Object.freeze(next) : headers;\n};\n\n/** The captured headers as the plain object both transports accept. */\nexport const headersToObject = (headers: readonly SnapshotHeader[]): Record<string, string> => {\n const bag: Record<string, string> = {};\n for (const [name, value] of headers) {\n bag[name] = value;\n }\n return bag;\n};\n","/**\n * The HTTP adapter behind the `UpgradeTransport` port.\n *\n * It owns no axios instance. Both rounds arrive as narrow, function-shaped\n * dependencies, which keeps HTTP vocabulary one level away from the controller\n * and lets every test here be a synchronous fake instead of an HTTP mock.\n *\n * It is also STATELESS by design: no cached challenge id, no cached prepare\n * result, no attempt counter. A challenge is single-use and the server consumes\n * it atomically, so there is nothing a second attempt could legitimately reuse.\n * Statelessness is the whole of this module's obligation to \"never reuse a spent\n * challenge_id\", and it is checkable by calling `prepare()` twice.\n */\n\nimport type { UpgradeRefreshField, UpgradeStartWire } from '../api/model';\n\nimport type { UpgradeTransport } from './controller';\nimport {\n UpgradeTransportError,\n classifyUpgradeError,\n invalidatesSessionOnCommit,\n isUpgradeSdkError,\n isUpgradeTransportError,\n} from './errors';\nimport { DEFAULT_UPGRADE_FACTORS, type UpgradeAssertion, type UpgradeFactorType, type UpgradePrepared } from './factors';\nimport type { UpgradeSessionPin } from './session';\n\nexport type UpgradeTransportDeps = {\n /** `POST /auth/upgrade/start`. Returns the raw snake_case body; rejects with `PassflowError`. */\n startUpgrade(payload: { method: string; relying_party_id?: string }): Promise<UpgradeStartWire>;\n /**\n * `POST /auth/refresh` carrying `upgrade`. Resolves once the elevated tokens\n * are STORED.\n *\n * `pin` is the session and user identity the flight began under (§13.4\n * revision 6). The host verifies it immediately before it applies the rotated\n * tokens and discards them on mismatch — the check has to live inside the\n * token-applying path, because that is the only place it cannot be bypassed\n * by another entry point. The transport only carries the value; the facade\n * supplies it, since the flight's start is what it is pinned to.\n */\n commitRefresh(upgrade: UpgradeRefreshField, pin?: UpgradeSessionPin): Promise<void>;\n /** Reports whether this delivery mode can carry an upgrade at all. BFF cannot. */\n canCommit(): { ok: true } | { ok: false; reason: string };\n /** Optional relying-party id for passkey upgrades; forwarded verbatim. */\n relyingPartyId?: () => string | undefined;\n /**\n * Invalidates the session. Called ONLY for a commit 401 whose error id\n * positively means the session died (`invalidatesSessionOnCommit`) — a\n * genuine expiry during an elevation is still an expiry, and the transport is\n * the only code that can tell, because it knows it issued the request and can\n * read the error id. Everything else, including an id this SDK does not\n * recognise, leaves the session alone.\n */\n markSessionInvalid?: () => void;\n};\n\n/**\n * Factors that need no prepare round, derived from the factor table rather than\n * restated, so adding a factor cannot leave this set stale.\n */\nconst ONE_SHOT_METHODS: ReadonlySet<string> = new Set(\n DEFAULT_UPGRADE_FACTORS.filter((factor) => !factor.requiresPrepare).map((factor) => factor.type),\n);\n\n/**\n * Normalises the prepare round's wire body.\n *\n * `webauthn` is passed through by reference, never re-serialised: the ceremony\n * hands those exact bytes to the authenticator and the server re-parses them on\n * the way back.\n *\n * A missing `challenge_id` or `expires_at` is a protocol violation, not an\n * optional field. Producing `challengeId: undefined` would surface two round\n * trips later as an opaque 401 with nothing to point at.\n */\nexport const toPrepared = (wire: UpgradeStartWire): UpgradePrepared => {\n if (!wire || typeof wire !== 'object' || !wire.challenge_id || !wire.expires_at) {\n throw new UpgradeTransportError({\n kind: 'protocol',\n message: 'The upgrade start response is missing challenge_id or expires_at',\n });\n }\n\n return {\n challengeId: wire.challenge_id,\n method: wire.method,\n expiresAt: wire.expires_at,\n webauthn: wire.webauthn,\n codeSentTo: wire.code_sent_to,\n };\n};\n\n/**\n * A rejection that is already one of the SDK's own upgrade outcomes, other than\n * a classified transport failure.\n *\n * `UpgradeTransportError` is excluded because `classifyUpgradeError` passes it\n * through on its own and the commit round still has to read its id for the\n * invalidation predicate.\n */\nconst isAlreadySettled = (e: unknown): boolean => isUpgradeSdkError(e) && !isUpgradeTransportError(e);\n\n/** Builds the transport. Returns the port type, so the controller stays HTTP-agnostic. */\nexport const createUpgradeTransport = (deps: UpgradeTransportDeps): UpgradeTransport => ({\n async prepare(method: UpgradeFactorType): Promise<UpgradePrepared | undefined> {\n // The server actively REJECTS a prepare call for a one-shot factor, so\n // \"call it and see\" costs a guaranteed round trip and a 400 on every TOTP\n // upgrade. The controller already gates on `requiresPrepare`; this is the\n // second line of that same defence, for a caller that does not.\n if (ONE_SHOT_METHODS.has(method)) {\n return undefined;\n }\n\n try {\n const relyingPartyId = deps.relyingPartyId?.();\n const wire = await deps.startUpgrade(relyingPartyId ? { method, relying_party_id: relyingPartyId } : { method });\n return toPrepared(wire);\n } catch (e) {\n if (isAlreadySettled(e)) {\n throw e;\n }\n const error = classifyUpgradeError(e, 'start');\n // `prepare_not_required` is a recovery, not a failure: our static\n // `requiresPrepare` disagreed with the server's, which is version skew.\n // Resolving `undefined` lets the factor assert directly, turning a future\n // backend rename into one wasted round trip instead of a dead elevation.\n if (error.kind === 'prepare_not_required') {\n return undefined;\n }\n throw error;\n }\n },\n\n async commit(factor: UpgradeFactorType, assertion: UpgradeAssertion): Promise<void> {\n // Checked before the network, so a delivery mode that can never commit says\n // so instead of taking a code from the user and discarding it.\n const permitted = deps.canCommit();\n if (!permitted.ok) {\n throw new UpgradeTransportError({ kind: 'unavailable', message: permitted.reason });\n }\n\n try {\n await deps.commitRefresh({ type: factor, assertion });\n } catch (e) {\n // An outcome the SDK has already decided is NOT an HTTP failure to\n // classify. The host's token-application pin check and the facade's\n // epoch guard both raise session-change outcomes from inside this call,\n // and neither carries a `response`, so `classifyUpgradeError` read them\n // as `network` — §13.3's `session changed mid-flight` row reached every\n // waiter tagged `upgrade_transport`. Re-thrown untouched, and before the\n // invalidation decision: a mid-flight identity change must never\n // invalidate (§13.4 revision 6, C38/C41).\n if (isAlreadySettled(e)) {\n throw e;\n }\n\n const error = classifyUpgradeError(e, 'refresh');\n\n // Invalidate only on an id that POSITIVELY means the session died.\n //\n // The commit path's 401s are not ambiguous: `LocalizedRouter.HTTPError`\n // always writes a `PassflowError.id` into the body, and\n // `refreshErrorStatus` (`src/web/api/refresh_token.go:157-178`) returns\n // 401 for exactly five enumerated ids, every one of them the upgrade\n // protocol talking. A dead or refused refresh token is NOT among them —\n // it falls to that switch's `default: http.StatusInternalServerError`.\n //\n // So the old \"anything else on a 401 is a real expiry\" reasoning was\n // defending against a response this server cannot send, while admitting\n // one it can: `error.2fa.not_enabled` is a 401, was absent from the\n // recoverable list, and signed out a user who simply had no 2FA enrolled.\n // Inverted, the default is fail-open, which is the rule this feature is\n // built on: fail toward \"I don't know\" rather than \"log them out\".\n if (error.status === 401 && invalidatesSessionOnCommit(error)) {\n deps.markSessionInvalid?.();\n }\n\n throw error;\n }\n },\n});\n","/**\n * The upgrade core: ONE implementation, transport-agnostic by construction.\n *\n * Every behaviour lives here — recognise a challenge on both channels, decide\n * whether the URL is in scope, run or join the elevation, replay once behind the\n * re-entry guard, and decide the outcome. The four surfaces the SDK offers\n * (`withAuth`, `attachAuth`, `createClient` and the internal axios seam) are\n * ADAPTERS over this function, and the prohibition on them is absolute:\n *\n * **NO LOGIC IN AN ADAPTER.**\n *\n * An adapter translates transport shapes in and out and does nothing else. It\n * may not parse a challenge, compare a bar, decide scope, count a retry, choose\n * an error class or read a body. If an adapter would need a decision, the\n * decision belongs here instead. The reason is not elegance: an axios response\n * interceptor receives an `AxiosError`, not a callable `next`, and re-issues\n * through `instance.request(config)` — so four independent wirings with two\n * outcome vocabularies is what happens by default, and it is the largest\n * quantity of parallel behaviour in this feature. A row that passes on one\n * adapter and fails on another is a leak of logic out of this file, and the fix\n * is to move it back in, never to fork the expectation per surface.\n *\n * `CoreOutcome` is the settlement table as a discriminated union, which is what\n * makes that table the adapters' specification rather than a prose parallel.\n */\n\nimport {\n type AssuranceBar,\n type ChallengeChannel,\n type UpgradeChallenge,\n type UpgradeDiagnostic,\n type UpgradeDiagnosticReason,\n type UpgradeDiagnosticSink,\n readUpgradeChallenge,\n} from './challenge';\nimport { UpgradeCancelledError, type UpgradeElevation, UpgradeUnsatisfiableError, type Waiter, barOf } from './controller';\nimport { UpgradeInsufficientError, UpgradeSessionChangedError, UpgradeTransportError, UpgradeUnavailableError } from './errors';\nimport type { ReplayHints, ReplayPolicy } from './replay';\nimport type { RequestSnapshot } from './snapshot';\n\n/**\n * Where the SDK is allowed to act on a challenge.\n *\n * Two domains, each with its own rule, and NO derived default. Deriving the set\n * was the category error the previous model made: same-origin by default, the\n * document origin, \"the origin the first 401 came from\" — every one of those is\n * the SDK inferring a trust boundary it has no information about, and a\n * fabricated 403 body from an observed endpoint can provoke a genuine elevation\n * whose replay then carries the freshly elevated credential to that endpoint.\n *\n * The resolved scope is passed IN. No adapter and no wrapper derives it.\n */\nexport type InterceptionScope = {\n /**\n * Domain 1 — Passflow's own API, derived from `PassflowConfig.url`. Always in\n * scope on any surface the SDK is attached to, which is what makes the\n * zero-config half of the design work. `null` when no `url` is configured:\n * domain 1 is then EMPTY and nothing throws, but the first parseable challenge\n * raises a diagnostic naming `url`, because an app with no `url` would\n * otherwise silently drop `/auth/session/exchange`'s own 403.\n */\n passflow: { origin: string; pathPrefix?: string } | null;\n /**\n * Domain 2 — the customer's own backend, exactly as declared. An absolute\n * origin (optionally with a path prefix) or a same-origin path prefix\n * (`'/api'`). `[]` means nothing beyond domain 1 is in scope — and the\n * predicate must NOT early-return on it.\n */\n backend: string[];\n};\n\nexport type UpgradeInterceptorOptions = {\n /**\n * Starts or joins the single flight, registering this waiter.\n *\n * Typed to accept a `void`-returning host as well, so a caller that does not\n * use the controller's waiter bookkeeping still works — it just forfeits the\n * per-waiter outcome summary, and the core then trusts the server's verdict on\n * the replay for the `insufficient` decision, which it does anyway.\n */\n elevate: (challenge: UpgradeChallenge, waiter: Waiter) => Promise<UpgradeElevation | void>;\n /** REQUIRED. Resolved once, where the client is built. */\n scope: InterceptionScope;\n /** `'header'` requires the standards channel and never reads the body. */\n requireChallengeChannel?: 'header';\n /**\n * NARROWING ONLY. Returning `false` excludes a URL the two domains admitted;\n * returning `true` can never widen, so an always-true predicate cannot\n * restore the allow-all default this design abolished.\n *\n * It is given the snapshot's **normalised absolute `href`**, never the raw\n * string the caller passed: a narrowing decision is an authorization decision,\n * and those are made on the URL the platform will actually send to (§13.4\n * revision 6). A predicate written against relative paths must therefore\n * match on the path of an absolute URL — `new URL(url).pathname` — rather than\n * on `startsWith('/…')`.\n */\n shouldIntercept?: (url: string) => boolean;\n replay?: ReplayPolicy;\n /** Default `'resolve-original'`. The fetch adapter's only option. */\n onCancel?: 'resolve-original' | 'reject';\n /** §5.5. Snapshotted before the elevation and read again after it. */\n authorizationSwap?: () => { from: string; to: string } | undefined;\n /** Default sink is `console.warn`; pass a function to route elsewhere. */\n onUpgradeDiagnostic?: UpgradeDiagnosticSink;\n /** The bar the elevation reached, when the host can read it from the new token. */\n readObtainedBar?: () => AssuranceBar | undefined;\n};\n\n/**\n * How an adapter is given its options: a value, or a getter read per challenge.\n *\n * The getter form is the one the facade uses, and the reason is a defect this\n * seam actually had. `enable()` resolves the interception policy — the scope in\n * particular — and an adapter handed the RESULT of that resolution freezes it at\n * attach time: a client built at module scope, or in a child's `useMemo`, is\n * attached before the provider's enable effect runs, so it would hold\n * `backend: []` forever and never intercept the customer's own backend. A\n * getter also makes re-enabling with a NARROWER `backend` take effect on\n * clients that were already attached, instead of leaving them on the wider\n * scope.\n *\n * A plain value is still accepted: a caller wiring the adapters directly, with\n * a scope it resolved itself, has nothing to re-read.\n */\nexport type UpgradeInterceptorOptionsSource = UpgradeInterceptorOptions | (() => UpgradeInterceptorOptions);\n\n/** Resolves an options source. Call it ONCE per challenge, never per module. */\nexport const resolveInterceptorOptions = (source: UpgradeInterceptorOptionsSource): UpgradeInterceptorOptions =>\n typeof source === 'function' ? source() : source;\n\n/**\n * What the core is given about one challenged request, and what it can do with\n * it. The adapter builds this and nothing more.\n */\n/**\n * A live view of one caller's abort state, with optional subscription.\n *\n * `AbortSignal` satisfies this structurally, and so does the minimal\n * `{ get aborted() }` getter object the adapters build. The listener pair is\n * what makes the abort an EVENT rather than a flag polled after the fact.\n */\nexport type AbortView = {\n aborted: boolean;\n reason?: unknown;\n addEventListener?(type: 'abort', listener: () => void): void;\n removeEventListener?(type: 'abort', listener: () => void): void;\n};\n\nexport type ChallengeContext = {\n status: number;\n /** The shipped callback shape, which is why one parser serves both surfaces. */\n getHeader(name: string): string | null | undefined;\n /**\n * Reads the body. MUST be safe to call only in scope, and MUST NOT disturb the\n * original payload — on the fetch surface that means `res.clone().json()`, so\n * the response the caller receives still has `bodyUsed === false`. The core\n * calls it only when the scope and the channel policy both permit.\n */\n readBody(): Promise<unknown>;\n /**\n * The normalised, immutable snapshot of the request that was challenged —\n * §13.4 revision 6's invariant, and the ONLY description of the request this\n * function will look at.\n *\n * The adapter takes it once, before dispatch, and hands the same object to\n * the scope check, this context, the diagnostics and `planReplay`. Nothing\n * here re-reads the caller's `Request`, `RequestInit` or axios config, so a\n * mutation while the prompt is open cannot change what was authorized, what\n * was reported or what is replayed.\n */\n request: RequestSnapshot;\n /**\n * Replays the ORIGINAL request, once, with the elevated credential. The\n * adapter supplies it; the core decides whether and when it is called, and\n * marks it replayed.\n */\n replay(hints?: ReplayHints): Promise<ChallengeContext>;\n /** Where the adapter stores the core's per-request markers. */\n markers: { upgradeTransaction?: true; replayed?: true };\n /**\n * The adapter's native payload — a `Response`, an `AxiosResponse`, an\n * `AxiosError`. The core never inspects it; it only hands it back so the\n * adapter can return the real thing.\n */\n raw?: unknown;\n /** `false` when `planReplay` refused. The prompt is then never opened. */\n replayable?: boolean;\n /** Why the replay is unsupported, for the diagnostic. */\n replayUnsupportedReason?: string;\n /**\n * The caller's abort state. The SDK never aborts it.\n *\n * Read as a LIVE view, never a snapshot, because the core reads it after\n * awaiting the elevation. The two listener methods are optional and carry\n * REQ-C12: with them the core races the abort against the pending proof, so\n * the caller's own promise settles at abort time instead of waiting for a\n * prompt it has walked away from; without them (a bare `{ aborted }` view)\n * the core falls back to reading the flag once the elevation settles. The\n * elevation itself is never cancelled either way — it is shared.\n */\n signal?: AbortView;\n /** `'cors'` distinguishes the one actionable `'no-channel'` variant. */\n responseType?: string;\n /**\n * `true` when the adapter received this exchange as a REJECTION rather than\n * as a response — axios' `validateStatus` contract, where every non-2xx\n * rejects. `fetch` resolves a 401, so its adapter never sets it.\n *\n * The core classifies the exchange identically either way: this flag decides\n * only how a `replayed` outcome is handed back, so a replay that failed on\n * its own terms keeps failing on the surface's own convention instead of\n * being resolved as a value.\n */\n rejected?: boolean;\n};\n\n/**\n * Every terminal outcome, as the settlement table defines them.\n *\n * The invariant: **every path settles.** There is no input that produces a\n * pending promise.\n */\nexport type CoreOutcome =\n /** Not a challenge. Pass the response through untouched. */\n | { kind: 'not-a-challenge' }\n /** A challenge the SDK is not allowed to act on. Diagnostic only; no body was read. */\n | { kind: 'out-of-scope' }\n | { kind: 'replayed'; response: ChallengeContext }\n /**\n * The proof completed and the operation's replay was challenged again.\n *\n * `response` is the SECOND challenge response, which the fetch surface\n * resolves and tags (§13.3) — so the decision \"which response does the caller\n * receive\" stays here rather than becoming a per-adapter `??` chain. It is\n * absent only on the re-entry path, where the adapter already holds the object\n * it was handed.\n */\n | { kind: 'insufficient'; obtained: AssuranceBar; required: AssuranceBar; response?: ChallengeContext }\n | { kind: 'cancelled' }\n | { kind: 'exhausted'; error: Error }\n | { kind: 'unsatisfiable' }\n | { kind: 'unavailable' }\n | { kind: 'session-changed' }\n | { kind: 'transport-failure'; status?: number; retryable: boolean; error: Error }\n | { kind: 'replay-unsupported' }\n | { kind: 'aborted'; reason: unknown };\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Scope\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Parses a PATTERN — never a request URL.\n *\n * Request URLs are normalised once, before dispatch, by\n * `createRequestSnapshot`; by the time they reach this file they are already a\n * resolved `origin` plus a resolved `pathname`. The only strings left to parse\n * here are the `backend` patterns the developer wrote, which are configuration\n * and carry no attacker-controlled bytes.\n */\nconst parsePattern = (pattern: string): URL | undefined => {\n try {\n return new URL(pattern);\n } catch {\n return undefined;\n }\n};\n\nconst pathMatches = (path: string, prefix: string | undefined): boolean => {\n if (!prefix || prefix === '/') {\n return true;\n }\n const normalised = prefix.replace(/\\/+$/, '');\n return path === normalised || path.startsWith(`${normalised}/`);\n};\n\n/** Is this request inside domain 1? Origin-compare on the RESOLVED url. */\nconst inPassflowDomain = (request: RequestSnapshot, domain: InterceptionScope['passflow']): boolean => {\n if (!domain || request.origin === undefined) {\n return false;\n }\n return request.origin === domain.origin && pathMatches(request.pathname, domain.pathPrefix);\n};\n\n/**\n * Is this request inside a declared backend?\n *\n * A pattern starting with `/` is a **same-origin path prefix**, which is how a\n * SPA declares `backend: '/api'`. \"Same-origin\" is load-bearing, and it is now\n * decided the only way it can be decided correctly: the snapshot resolved the\n * caller's URL with the platform's own parser, against the document base, and\n * `relativeToBase` says whether the result landed on the base's own origin.\n *\n * That single bit is what separates `/api/x` (in scope) from `/\\evil.example/x`,\n * `/<TAB>/evil.example/x` and `//evil.example/x` (all out of scope, because the\n * parser resolves all three to `https://evil.example`) — while leaving\n * `/%5Cevil.example/x` and `/%2F%2Fevil.example/x` IN scope, because an encoded\n * byte is a path character and the parser never decodes it into an authority.\n * Revision 2's `//`-prefix special case is REMOVED rather than stacked on top:\n * two mechanisms for one decision is how the next vector gets in.\n *\n * An absolute pattern matches by origin, plus its own path prefix when it\n * carries one. Same-origin is never implicitly included: that was the derived\n * default this design removed; it is in scope only because `'/…'` says so.\n */\nconst inDeclaredBackend = (request: RequestSnapshot, patterns: string[]): boolean => {\n for (const pattern of patterns) {\n if (pattern === '') {\n continue;\n }\n\n if (pattern.startsWith('/')) {\n if (request.relativeToBase && pathMatches(request.pathname, pattern)) {\n return true;\n }\n continue;\n }\n\n const patternUrl = parsePattern(pattern) ?? parsePattern(`https://${pattern}`);\n if (!patternUrl || request.origin === undefined) {\n continue;\n }\n if (request.origin === patternUrl.origin && pathMatches(request.pathname, patternUrl.pathname)) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * The scope decision, identical for every surface, and made on the SNAPSHOT.\n *\n * Precedence, in one ordered rule: domain 1, then domain 2, then\n * `shouldIntercept` as a narrowing-only hook. There is no allow-all opt-in — a\n * developer who genuinely wants every origin lists every origin, which is a\n * sentence they can read in a diff.\n *\n * It takes a `RequestSnapshot` and not a string by design (§13.4 revision 6): a\n * string parameter is an invitation to decide authorization on an un-normalised\n * value, which is exactly the defect this signature removes.\n */\nexport const isInterceptionScope = (\n request: RequestSnapshot,\n scope: InterceptionScope,\n shouldIntercept?: (url: string) => boolean,\n): boolean => {\n const admitted = inPassflowDomain(request, scope.passflow) || inDeclaredBackend(request, scope.backend);\n if (!admitted) {\n return false;\n }\n return shouldIntercept ? shouldIntercept(request.href) !== false : true;\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Diagnostics\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** The default sink: `console.warn`, because `debug` is invisible in production. */\nconst warn = (diagnostic: UpgradeDiagnostic): void => {\n const detail = diagnostic.detail;\n const message = detail?.message ?? `[passflow] upgrade challenge not actioned: ${diagnostic.reason}`;\n // biome-ignore lint/suspicious/noConsole: this IS the fail-open diagnostic. §13.1/§13.4 make it mandatory that a deployment where step-up silently never fires is distinguishable from one where it was never required, and the default sink must be visible in production — `debug` is not, and an exception would turn a fail-open into a fail-closed. A consumer who wants it elsewhere passes `onUpgradeDiagnostic`.\n console.warn(message, { status: diagnostic.status, url: diagnostic.url, reason: diagnostic.reason, detail });\n};\n\ntype Reporter = (reason: UpgradeDiagnosticReason, detail?: UpgradeDiagnostic['detail']) => void;\n\n/** Builds the per-request reporter, which fills in the url and method the parser lacks. */\nconst reporterFor = (ctx: ChallengeContext, options: UpgradeInterceptorOptions): Reporter => {\n const sink = options.onUpgradeDiagnostic ?? warn;\n return (reason, detail) => {\n sink({\n status: ctx.status,\n url: ctx.request.href,\n reason,\n detail: { method: ctx.request.method, ...(detail ?? {}) },\n });\n };\n};\n\n/** Forwards a parse's own findings, which are knowable only inside the parse. */\nconst forwardParseDiagnostics = (\n diagnostics: UpgradeDiagnostic[],\n ctx: ChallengeContext,\n options: UpgradeInterceptorOptions,\n): void => {\n const sink = options.onUpgradeDiagnostic ?? warn;\n for (const diagnostic of diagnostics) {\n sink({ ...diagnostic, url: ctx.request.href, detail: { method: ctx.request.method, ...(diagnostic.detail ?? {}) } });\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Reading the challenge\n * ──────────────────────────────────────────────────────────────────────────── */\n\nconst JSON_CONTENT_TYPE = /\\bjson\\b/i;\n\n/**\n * Reads a challenge from an IN-SCOPE response.\n *\n * The body-handling rule is strict, and the original response always comes back\n * with `bodyUsed === false`:\n *\n * ```\n * status not in {401,403} → never read, never cloned\n * status in {401,403}, content-type JSON → ALSO parse a CLONE of the body, in\n * try/catch, and merge both channels\n * status in {401,403}, not JSON → header only; body never read\n * requireChallengeChannel: 'header' → header only; body never read\n * ```\n *\n * A header hit no longer suppresses the clone read. The short-circuit it\n * replaces (\"header parse succeeds → body NEVER read\") was an optimisation\n * mistaken for a safety property: reading a CLONE was always the mechanism that\n * kept the caller's body intact, and the merge rule cannot run behind a\n * first-match chain.\n */\nconst readChallenge = async (\n ctx: ChallengeContext,\n options: UpgradeInterceptorOptions,\n): Promise<{ challenge?: UpgradeChallenge; diagnostics: UpgradeDiagnostic[] }> => {\n const headerOnly = options.requireChallengeChannel === 'header';\n const contentType = ctx.getHeader('content-type') ?? '';\n const mayReadBody = !headerOnly && JSON_CONTENT_TYPE.test(contentType);\n\n let body: unknown;\n if (mayReadBody) {\n try {\n body = await ctx.readBody();\n } catch {\n // A malformed or already-consumed body is not an error path: the header\n // may still carry the challenge, and the caller's response is untouched.\n body = undefined;\n }\n }\n\n const input = mayReadBody\n ? { status: ctx.status, getHeader: (name: string) => ctx.getHeader(name) ?? undefined, body }\n : { status: ctx.status, getHeader: (name: string) => ctx.getHeader(name) ?? undefined };\n\n return readUpgradeChallenge(input, { requireChallengeChannel: options.requireChallengeChannel });\n};\n\n/** Header-only inspection, for a URL outside the declared scope. */\nconst readOutOfScopeChallenge = (ctx: ChallengeContext): UpgradeChallenge | undefined =>\n readUpgradeChallenge(\n { status: ctx.status, getHeader: (name: string) => ctx.getHeader(name) ?? undefined },\n { requireChallengeChannel: 'header' },\n ).challenge;\n\n/** The message that names the fix, which is the whole value of this diagnostic. */\nconst outOfScopeMessage = (url: string, scope: InterceptionScope): string =>\n scope.passflow === null\n ? `[passflow] a 401/403 from ${url} carried an upgrade challenge, but no Passflow endpoint is configured — set \\`url\\` on the Passflow config.`\n : `[passflow] a 401/403 from ${url} carried an upgrade challenge, but that URL is not covered by the provider's \\`backend\\` prop — add it there to enable step-up for this API.`;\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Classification\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** Maps an elevation failure to its settlement-table row. */\nconst classifyFailure = (e: unknown, obtained: AssuranceBar | undefined): CoreOutcome => {\n if (e instanceof UpgradeCancelledError) {\n return { kind: 'cancelled' };\n }\n if (e instanceof UpgradeUnsatisfiableError) {\n return { kind: 'unsatisfiable' };\n }\n if (e instanceof UpgradeUnavailableError) {\n return { kind: 'unavailable' };\n }\n if (e instanceof UpgradeSessionChangedError) {\n return { kind: 'session-changed' };\n }\n if (e instanceof UpgradeInsufficientError) {\n return { kind: 'insufficient', obtained: e.obtained, required: e.required };\n }\n if (e instanceof UpgradeTransportError) {\n // Both exhaustion kinds settle on the same row of §13.3's table: the\n // server's own budget (`exhausted`) and the controller's local attempt\n // budget (`attempts_exhausted`) are the same statement to a caller — stop,\n // another answer will not help — and both are terminal.\n if (e.kind === 'exhausted' || e.kind === 'attempts_exhausted') {\n return { kind: 'exhausted', error: e };\n }\n if (e.kind === 'unavailable') {\n return { kind: 'unavailable' };\n }\n if (e.kind === 'session_changed') {\n return { kind: 'session-changed' };\n }\n // FR1's non-retryable 400 class — unsupported factor, no session — plus the\n // network and protocol classes. Surfaced, never laundered into a logout:\n // re-authentication is the application's decision, taken on a state it can\n // see.\n return { kind: 'transport-failure', status: e.status, retryable: e.retryable, error: e };\n }\n return {\n kind: 'transport-failure',\n retryable: false,\n error: e instanceof Error ? e : new Error(String(e)),\n ...(obtained ? {} : {}),\n };\n};\n\n/**\n * Classifies the replayed response: did this operation actually succeed?\n *\n * Proof completion and a protected operation's outcome are different events. The\n * user can complete TOTP, the server can accept it and record the elevation, and\n * the replay can STILL be challenged — on a higher `acr` bar or on a freshness\n * demand the new proof does not satisfy. For AAL3 endpoints with TOTP-only users\n * that is the normal path, so it gets a first-class outcome carrying both bars\n * rather than a second 401 returned verbatim with nothing to point at.\n */\nconst classifyReplay = async (\n replayed: ChallengeContext,\n options: UpgradeInterceptorOptions,\n obtained: AssuranceBar,\n report: Reporter,\n): Promise<CoreOutcome> => {\n if (replayed.status !== 401 && replayed.status !== 403) {\n return { kind: 'replayed', response: replayed };\n }\n\n const { challenge, diagnostics } = await readChallenge(replayed, options);\n forwardParseDiagnostics(diagnostics, replayed, options);\n\n if (!challenge) {\n return { kind: 'replayed', response: replayed };\n }\n\n const required = barOf(challenge);\n report('elevated-insufficient', { obtained, required });\n return { kind: 'insufficient', obtained, required, response: replayed };\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * The abort race (REQ-C12)\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/** Either the work finished, or this caller's signal fired first. */\ntype AbortRace<T> = { aborted: true; reason: unknown } | { aborted: false; value: T };\n\n/**\n * Awaits `work`, but settles early if `signal` aborts first.\n *\n * REQ-C12: an abort must settle the CALLER's promise promptly, without waiting\n * for a proof it has walked away from. The asymmetry that makes this safe is\n * that the elevation is shared and is NEVER cancelled here: `work` keeps\n * running for the other waiters, and only this caller stops awaiting it. The\n * caller of this helper owns what happens to a late-resolving `work` — see the\n * aborted branch in `handleChallenge`.\n *\n * Three ordering details, each one a defect if dropped:\n *\n * - **Already aborted** is checked before subscribing, because an\n * `AbortSignal` fires `abort` once and a listener attached afterwards never\n * hears it.\n * - **Work wins** still returns `aborted: false`, so the post-settle flag\n * check in `handleChallenge` keeps its existing meaning. A view with no\n * listeners takes that path for every abort, which is why the legacy\n * `{ aborted }` shape still behaves exactly as it did.\n * - **A rejection of `work` after the abort won** is absorbed by the caller,\n * not here: nothing is awaiting this promise any more, and an unhandled\n * rejection in a customer's app over a step-up they cancelled is not an\n * acceptable trade for a tidier helper.\n */\nconst raceAbort = async <T>(work: Promise<T>, signal: AbortView | undefined): Promise<AbortRace<T>> => {\n if (!signal) {\n return { aborted: false, value: await work };\n }\n if (signal.aborted) {\n return { aborted: true, reason: signal.reason };\n }\n if (typeof signal.addEventListener !== 'function') {\n return { aborted: false, value: await work };\n }\n\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<AbortRace<T>>((resolve) => {\n onAbort = () => resolve({ aborted: true, reason: signal.reason });\n signal.addEventListener?.('abort', onAbort);\n });\n\n try {\n return await Promise.race([work.then((value): AbortRace<T> => ({ aborted: false, value })), aborted]);\n } finally {\n if (onAbort) {\n signal.removeEventListener?.('abort', onAbort);\n }\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * The core\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * Detect → scope → elevate → replay once → classify. The one implementation.\n *\n * \"Retry exactly once\" is structural rather than counted: there is no path from\n * the replay back to the elevation. On a surface that re-enters this function\n * with the replayed request — axios does, because the replay goes back through\n * `instance.request(config)` and hits the same response interceptor — the replay\n * marker is read first and the response is classified without elevating. That\n * guard is written here, once; the adapter only supplies the marker's storage.\n */\nexport const handleChallenge = async (ctx: ChallengeContext, options: UpgradeInterceptorOptions): Promise<CoreOutcome> => {\n if (ctx.status !== 401 && ctx.status !== 403) {\n return { kind: 'not-a-challenge' };\n }\n\n // ── the elevation's OWN two rounds are never challenges to act on ──\n //\n // `/auth/upgrade/start` and the committing `/auth/refresh` are themselves\n // access-token-gated, so a 401 on either can parse as a challenge. Acting on\n // one would open a second prompt from inside the first elevation. The marker\n // is positive evidence that we issued the request, and reading it here — not\n // in an adapter — is what keeps the rule identical on every surface.\n if (ctx.markers.upgradeTransaction) {\n return { kind: 'not-a-challenge' };\n }\n\n const report = reporterFor(ctx, options);\n\n // ── the re-entry guard: classify and settle, never elevate ──\n if (ctx.markers.replayed) {\n const inScope = isInterceptionScope(ctx.request, options.scope, options.shouldIntercept);\n if (!inScope) {\n return { kind: 'not-a-challenge' };\n }\n const { challenge, diagnostics } = await readChallenge(ctx, options);\n forwardParseDiagnostics(diagnostics, ctx, options);\n if (!challenge) {\n return { kind: 'not-a-challenge' };\n }\n const obtained = options.readObtainedBar?.() ?? {};\n const required = barOf(challenge);\n report('elevated-insufficient', { obtained, required });\n return { kind: 'insufficient', obtained, required, response: ctx };\n }\n\n // ── scope, before anything reads a body ──\n if (!isInterceptionScope(ctx.request, options.scope, options.shouldIntercept)) {\n // Out of scope the SDK inspects the `WWW-Authenticate` header ONLY and never\n // reads or clones the body, so declaring a scope cannot be widened by a\n // diagnostic. The cross-origin, body-only, out-of-scope case therefore stays\n // silent: a stated limitation, not an oversight. Parsing for diagnostics\n // never elevates and never replays, so the security decision is untouched.\n const challenge = readOutOfScopeChallenge(ctx);\n if (!challenge) {\n return { kind: 'not-a-challenge' };\n }\n report('challenge-out-of-scope', {\n header: ctx.getHeader('www-authenticate') ?? undefined,\n message: outOfScopeMessage(ctx.request.href, options.scope),\n });\n return { kind: 'out-of-scope' };\n }\n\n const { challenge, diagnostics } = await readChallenge(ctx, options);\n forwardParseDiagnostics(diagnostics, ctx, options);\n\n if (!challenge) {\n // Fail toward \"I don't know\" rather than \"log them out\": no prompt opens, no\n // elevation starts, no blocking state is entered, and the response goes back\n // to the caller untouched. A client-side miss costs a broken flow, never an\n // unauthorized one, because the server is the enforcement point and this is\n // not. But it is never SILENT — a deployment where step-up never fires must\n // be distinguishable from one where it was never required.\n const rawHeader = ctx.getHeader('www-authenticate');\n if (options.requireChallengeChannel !== 'header') {\n report('no-channel', {\n ...(rawHeader ? { header: rawHeader } : {}),\n message:\n ctx.responseType === 'cors' && !rawHeader\n ? `[passflow] a cross-origin ${ctx.status} from ${ctx.request.href} carried no readable challenge: \\`WWW-Authenticate\\` is not CORS-safelisted, so add \\`Access-Control-Expose-Headers: WWW-Authenticate\\` on that server, or have it emit the \\`upgrade_required\\` JSON body.`\n : `[passflow] a ${ctx.status} from ${ctx.request.href} carried no upgrade challenge on either channel; treated as an ordinary ${ctx.status}.`,\n });\n }\n return { kind: 'not-a-challenge' };\n }\n\n // ── can this request even be honoured? ──\n if (ctx.replayable === false) {\n // Prompting a user, elevating their session and only then discovering we\n // cannot honour it is the worst possible ordering: it spends a server\n // challenge and a user interaction for nothing.\n report('replay-unsupported', {\n message: `[passflow] a ${ctx.status} from ${ctx.request.href} carried an upgrade challenge, but the request cannot be replayed (${\n ctx.replayUnsupportedReason ?? 'unsupported body'\n }), so no prompt was opened.`,\n });\n return { kind: 'replay-unsupported' };\n }\n\n // §5.5's ordering: `from` is snapshotted BEFORE the elevation, because the\n // commit overwrites storage, and `to` is read after.\n const before = options.authorizationSwap?.();\n\n const waiter: Waiter = { kind: 'request', url: ctx.request.href, method: ctx.request.method };\n\n // Aborting the original request does NOT cancel the elevation — it is shared,\n // and one caller walking away must not cancel a prompt three other in-flight\n // requests are waiting on. What the abort DOES do (REQ-C12) is stop THIS\n // caller waiting: its promise settles with the abort its own `catch` already\n // expects from `fetch`, while the flight runs on untouched for the others.\n // `Promise.resolve` wraps the call so that an `elevate` which throws\n // SYNCHRONOUSLY still lands in `classifyFailure` below rather than escaping\n // this function — starting the elevation before the `try` is what made that\n // reachable, and a host whose `elevate` throws on, say, a missing endpoint\n // must still settle on the table and never log anyone out.\n let elevating: Promise<UpgradeElevation | void> | undefined;\n let race: AbortRace<UpgradeElevation | void>;\n try {\n elevating = Promise.resolve(options.elevate(challenge, waiter));\n race = await raceAbort(elevating, ctx.signal);\n } catch (e) {\n return classifyFailure(e, undefined);\n }\n\n if (race.aborted) {\n // The proof is still pending, so there is no waiter handle to report with\n // yet — the handle arrives only when the elevation resolves. Reporting is\n // therefore deferred to that moment, and NOTHING else is: no bar is read,\n // no `authorizationSwap` is applied, the replay marker is not set and the\n // request is not replayed. A proof that completes after its waiter walked\n // away must not act on that waiter's behalf.\n //\n // `failed` is this waiter's terminal either way — it is what the liveness\n // bound would have assigned — and it must still arrive, or `awaitReports`\n // holds the prompt and every other waiter to the 30s bound over an ordinary\n // input. `receiveReport` is idempotent and safe on a cleared controller, so\n // a late report can neither double-count nor wedge a later challenge.\n //\n // The rejection arm is the unhandled-rejection guard: the elevation's own\n // failure is already the controller's to classify for the waiters that are\n // still listening, and it is never laundered into a logout here.\n elevating?.then(\n (elevation) => elevation?.waiter.report({ outcome: 'failed' }),\n () => {\n // Absorbed deliberately: see above.\n },\n );\n return { kind: 'aborted', reason: race.reason };\n }\n\n const elevation = race.value;\n\n // The abort that landed while the elevation was already settling, or against\n // a signal view with no listeners to race.\n if (ctx.signal?.aborted) {\n // The waiter still has to REPORT. Released for its adapter to classify, it\n // otherwise stays `pending` and `awaitReports` polls to the 30s liveness\n // bound — holding every other waiter in the flight, and the prompt, on what\n // is an ordinary input rather than an adapter bug. `failed` is the terminal\n // that bound would have assigned anyway; this only makes it arrive now. The\n // two abort checks inside the replay `catch` below already report before\n // they read the signal, so this is the one branch that needed it.\n elevation?.waiter.report({ outcome: 'failed' });\n return { kind: 'aborted', reason: ctx.signal.reason };\n }\n\n const obtained = elevation?.obtained ?? options.readObtainedBar?.() ?? barOf(challenge);\n const after = options.authorizationSwap?.();\n const swap = after ? { from: before?.from ?? after.from, to: after.to } : undefined;\n\n // The marker is set BEFORE the replay, so a surface that re-enters this\n // function with the replayed request takes the guard above instead of\n // elevating again — the loop FR3 prohibits by name.\n ctx.markers.replayed = true;\n\n let replayed: ChallengeContext;\n try {\n replayed = await ctx.replay(swap ? { authorizationSwap: swap } : undefined);\n } catch (e) {\n // A re-challenged replay is a REFUSAL, not a failure.\n //\n // On the axios surfaces the replay goes back through `request(config)` and\n // re-enters this function, where the re-entry guard above classifies it and\n // `settleAxiosOutcome` REJECTS with `UpgradeInsufficientError` — so the\n // refusal arrives here as a throw. Reporting `failed` for it told the\n // controller's `summarise` nothing about the bar, and a flight whose only\n // waiter was refused was then summarised as `success`: the modal said\n // \"Verification complete\" for an operation the server declined. The fetch\n // surface already gets this right through `classifyReplay`; this is the\n // same decision for the other three, made here so it cannot drift again.\n if (e instanceof UpgradeInsufficientError) {\n elevation?.waiter.report({ outcome: 'insufficient', obtained: e.obtained, required: e.required });\n if (ctx.signal?.aborted) {\n return { kind: 'aborted', reason: ctx.signal.reason };\n }\n return { kind: 'insufficient', obtained: e.obtained, required: e.required };\n }\n\n elevation?.waiter.report({ outcome: 'failed' });\n if (ctx.signal?.aborted) {\n return { kind: 'aborted', reason: ctx.signal.reason };\n }\n // A real failure still throws: the replay's own network error is the\n // caller's error, not a step-up outcome.\n throw e;\n }\n\n const outcome = await classifyReplay(replayed, options, obtained, report);\n\n if (outcome.kind === 'insufficient') {\n elevation?.waiter.report({ outcome: 'insufficient', obtained: outcome.obtained, required: outcome.required });\n } else {\n elevation?.waiter.report({ outcome: 'replayed', obtained });\n }\n\n return outcome;\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Outcome → error, for the two throwing surfaces\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * The error class a non-fetch surface rejects with, or `undefined` when the\n * outcome is not a rejection.\n *\n * axios' own contract, expressed in `validateStatus`, is that a non-2xx response\n * REJECTS. Resolving a declined step-up there would invert the library's\n * contract: every caller's post-`await` success path would run after the user\n * clicked \"Not now\", and `await api.post('/transfer')` would resolve with only a\n * status check the customer was never instructed to write standing between that\n * and treating an unperformed elevated action as performed.\n *\n * Every class carries a literal `code` discriminant as well as being\n * `instanceof`-checkable, and the `code` is the testable tag: it survives\n * bundling, duplicate package copies and cross-realm boundaries.\n */\nexport const coreOutcomeToError = (outcome: CoreOutcome): Error | undefined => {\n switch (outcome.kind) {\n case 'cancelled':\n return new UpgradeCancelledError();\n case 'unsatisfiable':\n return new UpgradeUnsatisfiableError();\n case 'unavailable':\n return new UpgradeUnavailableError();\n case 'session-changed':\n return new UpgradeSessionChangedError();\n case 'insufficient':\n return new UpgradeInsufficientError(outcome.obtained, outcome.required);\n case 'exhausted':\n return outcome.error;\n case 'transport-failure':\n return outcome.error;\n default:\n // `not-a-challenge`, `out-of-scope`, `replayed`, `replay-unsupported` and\n // `aborted` are not rejections of ours: the first four hand the caller the\n // transport's own result, and an abort throws the signal's reason.\n return undefined;\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Outcome → settlement, per surface. The adapters translate, they do not decide.\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * What an adapter must do with an outcome: hand back an object, or throw.\n *\n * Both surfaces settle from this, so §13.3's table has exactly one encoding in\n * the codebase. An adapter that reached its own conclusion about which response\n * a caller receives, or which error class, would be the per-surface drift the\n * single core exists to prevent.\n */\nexport type Settlement<T> = { settle: 'resolve'; value: T } | { settle: 'reject'; error: unknown };\n\n/**\n * The fetch surface's settlement.\n *\n * `fetch` resolves on 401 and every caller is written as `if (!res.ok)`, so a\n * declined step-up comes back as the original response — tagged, so it is\n * discriminable — rather than as a rejection their code has never handled. The\n * two exceptions are an abort (which throws the signal's reason, as `fetch`\n * itself does) and the explicit `onCancel: 'reject'` opt-out.\n */\nexport const settleFetchOutcome = <T extends object>(\n outcome: CoreOutcome,\n original: T,\n options: UpgradeInterceptorOptions,\n): Settlement<T> => {\n switch (outcome.kind) {\n case 'replayed':\n return { settle: 'resolve', value: outcome.response.raw as T };\n case 'not-a-challenge':\n case 'out-of-scope':\n case 'replay-unsupported':\n // Untouched and untagged: the SDK either saw nothing to act on, or never\n // opened a prompt, so there is no step-up outcome to report.\n return { settle: 'resolve', value: original };\n case 'aborted':\n return { settle: 'reject', error: outcome.reason };\n case 'cancelled':\n return options.onCancel === 'reject'\n ? { settle: 'reject', error: new UpgradeCancelledError() }\n : { settle: 'resolve', value: tagUpgradeOutcome(original, outcome) };\n case 'insufficient':\n // The SECOND challenge is what the caller receives: it is the response\n // that actually refused the operation, and the bars travel in the tag.\n return { settle: 'resolve', value: tagUpgradeOutcome((outcome.response?.raw as T) ?? original, outcome) };\n default:\n return { settle: 'resolve', value: tagUpgradeOutcome(original, outcome) };\n }\n};\n\n/**\n * The axios surface's settlement.\n *\n * axios' own contract, expressed in `validateStatus`, is that a non-2xx\n * response rejects, so every non-success outcome rejects here — with the tagged\n * error class for a step-up outcome, and with the transport's own error when\n * the SDK did not act.\n */\nexport const settleAxiosOutcome = <T>(outcome: CoreOutcome, originalError: unknown): Settlement<T> => {\n switch (outcome.kind) {\n case 'replayed':\n // A replay that axios itself rejected — a 500, say, carrying no\n // challenge — is still a rejection: resolving it would hand the caller an\n // `AxiosError` as a success value and invert `validateStatus` for the one\n // request step-up touched.\n return outcome.response.rejected\n ? { settle: 'reject', error: outcome.response.raw ?? originalError }\n : { settle: 'resolve', value: outcome.response.raw as T };\n case 'aborted':\n return { settle: 'reject', error: outcome.reason };\n default: {\n const error = coreOutcomeToError(outcome);\n // `not-a-challenge`, `out-of-scope` and `replay-unsupported` produce no\n // error of ours, and the caller gets the rejection axios already had.\n return { settle: 'reject', error: error ?? originalError };\n }\n }\n};\n\n/* ────────────────────────────────────────────────────────────────────────────\n * Fetch-surface discriminators\n * ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * The fetch surface resolves the ORIGINAL 401/403 response, untouched, because\n * `fetch` does not reject on 401 — it resolves with `res.ok === false`, and\n * every caller is written as `if (!res.ok)`. Rejecting would introduce a failure\n * mode their code has never handled. The resolved value is never `null` or\n * `undefined`: that breaks `FetchLike` and throws `TypeError` in customer code\n * on the first `res.ok`.\n *\n * So every non-success outcome needs a discriminator, and they are backed by\n * `WeakSet`s keyed on the object the SDK returned. Two consequences a caller\n * must know, because both produce a confident `false` on a genuine outcome:\n *\n * - **Check before cloning.** `isUpgradeCancelled(res.clone())` is `false`: a\n * clone is a different object.\n * - **One copy of the SDK.** A predicate imported from a second copy of\n * `@passflow/core` in the same tree reads a different `WeakSet`.\n */\nconst cancelled = new WeakSet<object>();\nconst exhausted = new WeakSet<object>();\nconst insufficient = new WeakSet<object>();\nconst unsatisfiable = new WeakSet<object>();\nconst unavailable = new WeakSet<object>();\nconst sessionChanged = new WeakSet<object>();\nconst transportFailure = new WeakSet<object>();\n\n/**\n * Tags the response an adapter is about to resolve with the outcome that\n * produced it. The mapping is here, not in the adapter: choosing which set a\n * `CoreOutcome` belongs to is a decision.\n */\nexport const tagUpgradeOutcome = <T extends object>(response: T, outcome: CoreOutcome): T => {\n switch (outcome.kind) {\n case 'cancelled':\n cancelled.add(response);\n break;\n case 'exhausted':\n exhausted.add(response);\n break;\n case 'insufficient':\n insufficient.add(response);\n break;\n case 'unsatisfiable':\n unsatisfiable.add(response);\n break;\n case 'unavailable':\n unavailable.add(response);\n break;\n case 'session-changed':\n sessionChanged.add(response);\n break;\n case 'transport-failure':\n transportFailure.add(response);\n break;\n default:\n break;\n }\n return response;\n};\n\n/** \"You were challenged and you declined\" — as opposed to \"you were not authorised\". */\nexport const isUpgradeCancelled = (response: object): boolean => cancelled.has(response);\nexport const isUpgradeExhausted = (response: object): boolean => exhausted.has(response);\nexport const isUpgradeInsufficient = (response: object): boolean => insufficient.has(response);\nexport const isUpgradeUnsatisfiable = (response: object): boolean => unsatisfiable.has(response);\nexport const isUpgradeUnavailable = (response: object): boolean => unavailable.has(response);\nexport const isUpgradeSessionChanged = (response: object): boolean => sessionChanged.has(response);\n/** FR1's non-retryable 400 class. */\nexport const isUpgradeTransportFailure = (response: object): boolean => transportFailure.has(response);\n\nexport type { ChallengeChannel };\n","/**\n * The replay planner.\n *\n * Pure, no I/O, and it runs BEFORE the first call — a `Request` body must be\n * cloned while it is still unread, so by the time a 401 has come back it is too\n * late to decide whether a replay is possible. Planning first is also what lets\n * the core fail BEFORE opening a prompt: prompting the user, elevating their\n * session and only then discovering we cannot honour it is the worst possible\n * ordering, because it spends a server challenge and a user interaction for\n * nothing.\n *\n * ## Why replaying a challenged request is safe at all\n *\n * An RFC 9470 challenge is emitted by the authorization middleware —\n * `RequireAuthLevel` / `RequireFreshAuth` — BEFORE the handler runs, so the\n * request had no effect. That is categorically different from a 429 or a 502,\n * where the request may have partially executed, which is why\n * `AxiosClient.handleRateLimitError` correctly refuses to retry non-idempotent\n * requests and why this correctly does the opposite.\n *\n * The guarantee depends on the CUSTOMER's middleware placement, though, so\n * there is an escape hatch rather than an assumption: a backend that emits an\n * `insufficient_user_authentication` challenge after a side effect is violating\n * RFC 9470, and such a backend sets `'idempotent-only'`. Stating the\n * precondition is the design; silently assuming it would not be.\n *\n * ## Per-waiter binding\n *\n * A plan is bound to the request that carried the challenge: its own URL, its\n * own method and its own pre-captured body. No waiter's challenge, outcome or\n * response ever changes what another waiter replays, or where — requirement\n * AGGREGATION across waiters sharing a session is permitted and happens in the\n * controller; replay SUBSTITUTION is prohibited, and that prohibition is this\n * module's whole job.\n */\n\nimport { type RequestSnapshot, createRequestSnapshot, headersToObject, swapAuthorization } from './snapshot';\n\n/** Our own `fetch` shape. Declared here rather than imported so no DOM lib is required. */\nexport type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/** How a body can be re-sent, which is the only thing that decides replayability. */\nexport type BodyKind =\n /** No body at all. Re-issue the same input. */\n | 'none'\n /** A value the caller still holds and `fetch` re-reads: string, Blob, buffer, FormData, params. */\n | 'reusable'\n /** A `Request` with a body, cloned before the first send. */\n | 'request-clone'\n /** Consumed by the first send and unrecoverable: a `ReadableStream`, or a failed clone. */\n | 'one-shot';\n\n/** What the policy predicate is told about a request. */\nexport type UpgradeReplayInfo = { url: string; method: string; bodyKind: BodyKind };\n\n/**\n * Replay policy.\n *\n * The object form is §13.5's: size and streaming limits. The three string and\n * function forms are §5.1's escape hatch, kept because `'idempotent-only'`\n * expresses a constraint the object form cannot — a backend whose challenge may\n * arrive after a side effect.\n */\nexport type ReplayPolicy =\n | {\n /** Refuse to buffer a `Request` body larger than this many bytes, when the size is known. */\n maxBodyBytes?: number;\n /** A stream is never replayable; the field exists so the intent is written down. */\n allowStreamingBody?: false;\n }\n | 'always'\n | 'idempotent-only'\n | ((info: UpgradeReplayInfo) => boolean);\n\n/** Hints the core passes at replay time. The planner applies them; it decides nothing. */\nexport type ReplayHints = {\n /**\n * The §5.5 credential swap, already resolved by the core.\n *\n * Applied if and only if the outgoing `Authorization` header value EXACTLY\n * equals `from`: equality with the token we hold in storage is what proves the\n * header was ours. Any other value is forwarded untouched, because rewriting a\n * header we do not own would be a security bug.\n *\n * Without this the replay re-sends the stale `Authorization` header, every\n * elevation ends in a surfaced second 401, and the feature silently never\n * works. Cookie mode needs none of it: the browser attaches the current —\n * now elevated — cookie by itself.\n */\n authorizationSwap?: { from: string; to: string };\n};\n\nexport type ReplayPlan = {\n /** False when the body cannot be re-sent, or the policy refused. */\n replayable: boolean;\n bodyKind: BodyKind;\n /** Why it is not replayable, for the `'replay-unsupported'` diagnostic. */\n reason?: string;\n /** What to pass to `next()` on the FIRST call. */\n forwardInput: RequestInfo | URL;\n forwardInit?: RequestInit;\n /**\n * The normalised, immutable snapshot of this request — §13.4 revision 6.\n *\n * EXPOSED rather than hidden as a plan-time url, so the scope check, the\n * challenge context, the diagnostics and the replay all read the same object\n * and the request that is *authorized* cannot differ from the one that is\n * *sent*.\n */\n snapshot: RequestSnapshot;\n /** Re-issues the original request, once. Rejects if `!replayable`. */\n replay: (next: FetchLike, hints?: ReplayHints) => Promise<Response>;\n};\n\nconst IDEMPOTENT_METHODS: ReadonlySet<string> = new Set(['GET', 'HEAD', 'OPTIONS']);\n\n/** Duck-typed so no DOM lib is needed and a polyfilled `Request` still matches. */\nconst isRequest = (input: unknown): input is Request =>\n typeof input === 'object' &&\n input !== null &&\n typeof (input as Request).url === 'string' &&\n typeof (input as Request).clone === 'function' &&\n 'method' in (input as Request);\n\n/** The URL of whatever shape the caller passed, without touching the network. */\nexport const urlOf = (input: RequestInfo | URL): string => {\n if (typeof input === 'string') {\n return input;\n }\n if (isRequest(input)) {\n return input.url;\n }\n return String(input);\n};\n\n/** The method of whatever shape the caller passed. `init` wins, as `fetch` itself has it. */\nexport const methodOf = (input: RequestInfo | URL, init?: RequestInit): string => {\n const method = init?.method ?? (isRequest(input) ? input.method : undefined);\n return (method ?? 'GET').toUpperCase();\n};\n\n/**\n * Classifies an `init` body.\n *\n * `reusable` is every value the caller still holds and `fetch` re-reads from:\n * strings, `Blob`, `ArrayBuffer`, typed arrays, `DataView`, `URLSearchParams`\n * and `FormData`. A `ReadableStream` is the one shape that is consumed by the\n * first send, and the `duplex: 'half'` form is no different.\n */\nconst classifyInitBody = (body: BodyInit | null | undefined): BodyKind => {\n if (body === null || body === undefined) {\n return 'none';\n }\n if (typeof body === 'string') {\n return 'reusable';\n }\n // A stream exposes `getReader`; nothing else in the BodyInit union does.\n if (typeof (body as ReadableStream).getReader === 'function') {\n return 'one-shot';\n }\n return 'reusable';\n};\n\n/** The byte length of a body when it is knowable without consuming it. */\nconst knownBodyBytes = (body: BodyInit | null | undefined): number | undefined => {\n if (typeof body === 'string') {\n // Close enough for a guard rail, and it never under-counts for ASCII.\n return body.length;\n }\n const sized = body as { size?: unknown; byteLength?: unknown } | null | undefined;\n if (typeof sized?.size === 'number') {\n return sized.size;\n }\n if (typeof sized?.byteLength === 'number') {\n return sized.byteLength;\n }\n return undefined;\n};\n\n/**\n * Applies the policy. The planner asks; it never decides what the policy means.\n *\n * Exported because the axios adapter needs the same answer about an\n * `AxiosRequestConfig`, which has no `Request` and no `BodyInit` to plan over.\n * Two readings of `'idempotent-only'` — one here and one there — is exactly the\n * per-surface drift the single core forbids, so there is one reading and both\n * surfaces call it.\n */\nexport const policyPermits = (policy: ReplayPolicy | undefined, info: UpgradeReplayInfo, bodyBytes?: number): boolean => {\n if (policy === undefined || policy === 'always') {\n return true;\n }\n if (policy === 'idempotent-only') {\n return IDEMPOTENT_METHODS.has(info.method);\n }\n if (typeof policy === 'function') {\n return policy(info);\n }\n if (policy.maxBodyBytes !== undefined && bodyBytes !== undefined && bodyBytes > policy.maxBodyBytes) {\n return false;\n }\n return true;\n};\n\n/** A plan that refuses, so the core can return the original response untouched. */\nconst refuse = (\n bodyKind: BodyKind,\n reason: string,\n forwardInput: RequestInfo | URL,\n snapshot: RequestSnapshot,\n forwardInit?: RequestInit,\n): ReplayPlan => ({\n replayable: false,\n bodyKind,\n reason,\n forwardInput,\n forwardInit,\n snapshot,\n replay: () => Promise.reject(new Error(`This request cannot be replayed: ${reason}`)),\n});\n\n/**\n * The header set the replay sends: the snapshot's, with the credential swapped.\n *\n * ## Why precedence is resolved ONCE, here\n *\n * `fetch(input, init)` does not merge header sets. When `init.headers` is\n * present it **replaces** a `Request`'s headers entirely; when it is absent the\n * `Request`'s own headers are used. Applying the §5.5 swap to one of the two\n * objects and letting `fetch` pick the other is how a replay went out carrying\n * the PRE-elevation `Authorization` and the whole feature silently did nothing:\n * the elevated credential was written into the set that was then discarded.\n *\n * So the snapshot captures the EFFECTIVE set once, before dispatch, by the\n * `fetch` rule above, the swap is applied to that one set, and the replay sends\n * it explicitly. There is no second set left for anything to prefer.\n */\nconst replayHeaders = (snapshot: RequestSnapshot, hints?: ReplayHints): Record<string, string> =>\n headersToObject(swapAuthorization(snapshot.headers, hints?.authorizationSwap));\n\n/** The caller's `init` minus `headers`, which `replayHeaders` now owns outright. */\nconst initWithoutHeaders = (init: RequestInit | undefined): RequestInit | undefined => {\n if (!init || !('headers' in init)) {\n return init;\n }\n const { headers: _headers, ...rest } = init;\n return rest;\n};\n\n/**\n * The effective header set `fetch` itself would use, per the rule above.\n *\n * An explicit `undefined` counts as absent, which is how WebIDL reads a\n * dictionary member set to `undefined`.\n */\nconst effectiveHeaders = (input: RequestInfo | URL, init?: RequestInit): unknown => {\n if (init && 'headers' in init && init.headers !== undefined) {\n return init.headers;\n }\n return isRequest(input) ? input.headers : undefined;\n};\n\n/**\n * Captures an `init` body by VALUE, once, before the first send.\n *\n * Snapshotting the URL and the method was not enough: a `FormData` the caller\n * still holds is a live object, and `fetch` re-reads it on every send. A\n * transfer planned to Alice, `form.set('to', 'Mallory')` while the prompt is\n * open, and the replay carries Mallory — with the freshly elevated credential\n * attached, against a request no scope or policy decision ever saw. Same TOCTOU\n * class as the re-pointable `URL`, on the other half of the request.\n *\n * Only the MUTABLE shapes are copied. Strings and `Blob`s are immutable, so\n * holding the reference already is a snapshot; `ArrayBuffer`s, views,\n * `URLSearchParams` and `FormData` are not. The `FormData` copy is shallow by\n * design: its values are strings and `Blob`s, both immutable, so re-appending\n * them costs no buffering. A `ReadableStream` never reaches here —\n * `classifyInitBody` has already refused it as `one-shot`.\n *\n * The copy happens inside `planReplay`, AFTER `policyPermits`, so the existing\n * `maxBodyBytes` limit still governs what the SDK is willing to hold.\n */\nconst snapshotBody = (body: BodyInit | null): BodyInit | null => {\n if (body === null || typeof body === 'string') {\n return body;\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n const copy = new FormData();\n for (const [name, value] of body as unknown as Iterable<[string, FormDataEntryValue]>) {\n copy.append(name, value as string | Blob);\n }\n return copy;\n }\n if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) {\n return new URLSearchParams(body);\n }\n if (body instanceof ArrayBuffer) {\n return body.slice(0);\n }\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(body.buffer, body.byteOffset, body.byteLength).slice();\n }\n // A `Blob` (and anything exotic a polyfill hands us) is forwarded as given.\n return body;\n};\n\n/**\n * A `Request` carrying the snapshot's effective, credential-swapped headers.\n *\n * `Request` headers are immutable once constructed, so applying them means\n * building a new `Request` from the clone. Everything else is copied by the\n * `Request` constructor's own init form, which is the only way to preserve\n * `mode`, `credentials`, `cache`, `redirect`, `referrer`, `integrity` and the\n * signal without naming them.\n */\nconst requestWithSnapshotHeaders = (request: Request, snapshot: RequestSnapshot, hints?: ReplayHints): Request =>\n new Request(request, { headers: replayHeaders(snapshot, hints) });\n\n/**\n * Decides whether a request can be replayed, and produces the thunk that does it.\n *\n * Call it BEFORE the first send. The returned `forwardInput` / `forwardInit` are\n * what the first send must use: for a `Request` with a body they are the\n * original, with the spare clone held for the replay.\n */\nexport const planReplay = (input: RequestInfo | URL, init?: RequestInit, policy?: ReplayPolicy): ReplayPlan => {\n const url = urlOf(input);\n const method = methodOf(input, init);\n const headers = effectiveHeaders(input, init);\n\n /** One snapshot per plan, taken before the first send. */\n const snapshotFor = (bodyKind: BodyKind, body?: unknown): RequestSnapshot =>\n createRequestSnapshot({ url, method, headers, bodyKind, ...(body === undefined ? {} : { body }) });\n\n if (isRequest(input)) {\n // `body === null` covers GET/HEAD and any bodyless request: the same\n // `Request` can simply be re-issued.\n if (input.body === null || input.body === undefined) {\n const snapshot = snapshotFor('none');\n const info: UpgradeReplayInfo = { url, method, bodyKind: 'none' };\n if (!policyPermits(policy, info)) {\n return refuse('none', 'the replay policy refused this request', input, snapshot, init);\n }\n return {\n replayable: true,\n bodyKind: 'none',\n forwardInput: input,\n forwardInit: init,\n snapshot,\n // The snapshot's effective header set is applied to the `Request` and\n // `init.headers` is dropped, so `fetch` has only one set to choose\n // from. See `replayHeaders` for why that matters.\n replay: (next, hints) => next(requestWithSnapshotHeaders(input, snapshot, hints), initWithoutHeaders(init)),\n };\n }\n\n const snapshot = snapshotFor('request-clone');\n const info: UpgradeReplayInfo = { url, method, bodyKind: 'request-clone' };\n if (!policyPermits(policy, info)) {\n return refuse('request-clone', 'the replay policy refused this request', input, snapshot, init);\n }\n\n // The clone must be taken NOW, while the body is unread. We pay the buffering\n // cost only for `Request`-with-body inputs, which are the minority; string +\n // init is the common shape and costs nothing.\n let spare: Request;\n try {\n spare = input.clone();\n } catch {\n // `bodyUsed` already — nothing to recover. Degrade rather than throw: the\n // caller still gets their original response, just no step-up.\n return refuse('one-shot', 'the Request body was already consumed and cannot be cloned', input, snapshot, init);\n }\n\n return {\n replayable: true,\n bodyKind: 'request-clone',\n forwardInput: input,\n forwardInit: init,\n snapshot,\n replay: (next, hints) => next(requestWithSnapshotHeaders(spare, snapshot, hints), initWithoutHeaders(init)),\n };\n }\n\n const bodyKind = classifyInitBody(init?.body);\n if (bodyKind === 'one-shot') {\n return refuse('one-shot', 'a streaming request body is consumed by the first send', input, snapshotFor('one-shot'), init);\n }\n\n const info: UpgradeReplayInfo = { url, method, bodyKind };\n if (!policyPermits(policy, info, knownBodyBytes(init?.body))) {\n return refuse(bodyKind, 'the replay policy refused this request', input, snapshotFor(bodyKind), init);\n }\n\n // Captured ONCE, here, before the first send — not reread from the caller's\n // live `init` when the replay goes out. See `snapshotBody`.\n const body = init && 'body' in init ? snapshotBody(init.body ?? null) : undefined;\n const snapshot = snapshotFor(bodyKind, body);\n\n return {\n replayable: true,\n bodyKind,\n forwardInput: input,\n forwardInit: init,\n snapshot,\n // The SNAPSHOT is re-issued, never the caller's live `input`.\n //\n // The url is the snapshot's NORMALISED `href` — the absolute URL the scope\n // decision was actually made on. Dispatching the caller's object instead\n // would let a `URL` instance be re-pointed (`input.href = …`) while the\n // prompt is open, sending the elevated credential somewhere no scope\n // decision ever saw; dispatching the raw string would re-resolve it, and a\n // `<base href>` changing mid-flight would move it. The `Request` branches\n // above need no equivalent: a `Request`'s url and method are immutable and\n // its body clone is taken eagerly.\n //\n // The BODY and the HEADERS are snapshotted for the same reason and by the\n // same rule: every `reusable` body shape is reread by `fetch` on each send,\n // and a `Headers` instance the caller still holds is mutable. The rest of\n // `init` is re-sent as given — the caller's `signal` is forwarded\n // unchanged, so an abort during the replay behaves identically to an abort\n // during the original.\n replay: (next, hints) =>\n next(snapshot.dispatchUrl, {\n ...initWithoutHeaders(init),\n ...(body === undefined ? {} : { body: body as BodyInit | null }),\n headers: replayHeaders(snapshot, hints),\n method: snapshot.method,\n }),\n };\n};\n","/**\n * The AXIOS adapter over `handleChallenge`. Two of the four surfaces use it.\n *\n * **NO LOGIC IN AN ADAPTER.** It builds a `ChallengeContext` from an\n * `AxiosError` and settles through `settleAxiosOutcome`; it parses no\n * challenge, compares no bar, decides no scope, counts no retry and chooses no\n * error class. The replay-once guard in particular is NOT here: the replay goes\n * back through `request(config)` and re-enters this very interceptor, so the\n * guard has to be surface-independent, and it is — the core reads and writes\n * `markers.replayed`, and this file only says where that marker lives (on the\n * request config, where axios hands it back unchanged).\n *\n * ## Why the parameter is structurally typed\n *\n * `ResponseInterceptorHost` is declared by us and satisfied by any axios\n * instance. We never name axios' own types in the signature, so a customer on a\n * different axios major — or on a fetch-based client that mimics the\n * interceptor API — attaches without a type error, and this package's axios\n * version is not a constraint on theirs.\n *\n * ## Why it is the error interceptor\n *\n * axios' own contract, expressed in `validateStatus`, is that a non-2xx\n * response rejects, so a 401/403 arrives as an `AxiosError`. Resolving a\n * declined step-up would invert that contract: every caller's post-`await`\n * success path would run after the user clicked \"Not now\".\n */\n\nimport {\n type AbortView,\n type ChallengeContext,\n type UpgradeInterceptorOptions,\n type UpgradeInterceptorOptionsSource,\n handleChallenge,\n resolveInterceptorOptions,\n settleAxiosOutcome,\n} from './core';\nimport { isUpgradeSdkError } from './errors';\nimport { hasUpgradeReplayedMarker, hasUpgradeTransactionMarker, markUpgradeReplayed } from './markers';\nimport { type ReplayHints, type UpgradeReplayInfo, policyPermits } from './replay';\nimport { type RequestSnapshot, createRequestSnapshot, headersToObject, swapAuthorization } from './snapshot';\n\n/**\n * The only thing we require of a client: that it lets us add and remove a\n * response interceptor. Duck-typed deliberately — see the module comment.\n */\nexport type ResponseInterceptorHost = {\n interceptors: {\n response: {\n // biome-ignore lint/suspicious/noExplicitAny: structural mirror of axios' own looser signature; narrowing it here would reject real axios instances\n use(onFulfilled?: (value: any) => any, onRejected?: (error: any) => any): number;\n eject(id: number): void;\n };\n };\n};\n\n/** Re-issues a request from a config. Supplied by whoever owns the client. */\nexport type AxiosReissue = (config: AxiosConfigLike) => Promise<unknown>;\n\n/** The subset of `AxiosRequestConfig` this adapter reads. */\nexport type AxiosConfigLike = {\n url?: string;\n baseURL?: string;\n method?: string;\n data?: unknown;\n headers?: unknown;\n /**\n * The caller's own signal, forwarded to the core untouched. Typed as the\n * core's `AbortView` so an `AbortSignal`'s listener pair types through: that\n * pair is what lets the core settle this caller at abort time while a proof\n * is still pending (REQ-C12). A plain `{ aborted }` object is still accepted,\n * and then the core reads the flag after the elevation as it always did.\n */\n signal?: AbortView;\n};\n\n/** The subset of `AxiosResponse` this adapter reads. */\ntype AxiosResponseLike = {\n status: number;\n headers?: unknown;\n data?: unknown;\n config?: AxiosConfigLike;\n};\n\n/** The subset of `AxiosError` this adapter reads. */\ntype AxiosErrorLike = {\n response?: AxiosResponseLike;\n config?: AxiosConfigLike;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\n/**\n * Reads a header off an axios headers bag, in the callback shape the one parser\n * takes.\n *\n * axios hands back a plain object, an `AxiosHeaders` instance or (in node) an\n * array value per name. All three are flattened here, which is the whole reason\n * the parser takes a callback instead of a DOM `Headers`.\n */\nconst headerReader =\n (headers: unknown) =>\n (name: string): string | null | undefined => {\n if (!isRecord(headers)) {\n return undefined;\n }\n const direct = headers[name] ?? headers[name.toLowerCase()];\n const value =\n direct ??\n (typeof (headers as { get?: (n: string) => unknown }).get === 'function'\n ? (headers as { get: (n: string) => unknown }).get(name)\n : undefined);\n if (Array.isArray(value)) {\n return value.join(', ');\n }\n return typeof value === 'string' ? value : undefined;\n };\n\n/**\n * The request's absolute URL, as far as the config states it.\n *\n * A relative `url` with no `baseURL` stays relative, which is what lets a SPA's\n * `backend: '/api'` declaration match: the scope predicate understands a path\n * prefix, and inventing an origin here would be the derived model the design\n * removed.\n */\nconst urlOf = (config: AxiosConfigLike | undefined): string => {\n const url = config?.url ?? '';\n const base = config?.baseURL;\n if (!base) {\n return url;\n }\n // axios' own `isAbsoluteURL` is `/^([a-z][a-z\\d+\\-.]*:)?\\/\\//i`, so it treats\n // a PROTOCOL-RELATIVE url as absolute and does not prepend `baseURL` to\n // `//evil.example/x`. Reading it as a path and joining it onto `baseURL` would\n // hand the scope predicate a same-origin-looking url for a request axios\n // actually sends off-origin. Same regex here, so the two cannot disagree.\n if (/^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(url)) {\n return url;\n }\n return `${base.replace(/\\/+$/, '')}/${url.replace(/^\\/+/, '')}`;\n};\n\nconst methodOf = (config: AxiosConfigLike | undefined): string => (config?.method ?? 'GET').toUpperCase();\n\n/** How a config's body can be re-sent. axios serialises `data` on every send. */\nconst bodyKindOf = (data: unknown): UpgradeReplayInfo['bodyKind'] => {\n if (data === undefined || data === null) {\n return 'none';\n }\n // A node stream or a web stream is consumed by the first send.\n if (isRecord(data) && (typeof data.pipe === 'function' || typeof data.getReader === 'function')) {\n return 'one-shot';\n }\n return 'reusable';\n};\n\n/**\n * The config a replay is issued with: the original, re-pointed at the SNAPSHOT.\n *\n * url, method, headers and data all come from the frozen snapshot rather than\n * from the live config object, so a config the caller still holds — axios\n * instances are routinely built once and reused — cannot change where the\n * elevated credential goes or what it carries. The credential swap is applied\n * to the snapshot's header set by exact match: equality with the token we hold\n * is what proves the header was ours, and rewriting a header we do not own\n * would be a security bug.\n *\n * `baseURL` is cleared when the snapshot resolved to an absolute URL, because\n * the snapshot's `href` already includes it; leaving both would depend on\n * axios' `isAbsoluteURL` to not join them twice.\n */\nconst replayConfig = (config: AxiosConfigLike, snapshot: RequestSnapshot, hints?: ReplayHints): AxiosConfigLike => {\n const headers = headersToObject(swapAuthorization(snapshot.headers, hints?.authorizationSwap));\n const absolute = snapshot.dispatchUrl === snapshot.href && snapshot.resolved;\n return {\n ...config,\n ...(absolute ? { baseURL: undefined } : {}),\n url: snapshot.dispatchUrl,\n method: snapshot.method,\n headers,\n ...('body' in snapshot ? { data: snapshot.body } : {}),\n };\n};\n\n/**\n * Wraps an axios response as a `ChallengeContext`.\n *\n * The markers live on the request config: axios hands `error.config` back\n * unchanged, and a non-enumerable property would not survive `mergeConfig`'s\n * `Object.keys` walk (see `markers.ts`). Writing `markers.replayed` therefore\n * means stamping the config of the request we are about to re-issue, which is\n * what the `replay` thunk below does.\n */\nconst contextFor = (\n response: AxiosResponseLike | undefined,\n config: AxiosConfigLike | undefined,\n raw: unknown,\n reissue: AxiosReissue,\n options: UpgradeInterceptorOptions,\n /** The FIRST send's snapshot, threaded through every replayed context. */\n inherited?: RequestSnapshot,\n): ChallengeContext => {\n const effective = config ?? response?.config;\n const bodyKind = bodyKindOf(effective?.data);\n // Taken once per intercepted request and inherited by the replayed context:\n // a second snapshot taken off the replay's own config would be a second\n // description of one request, which is the thing §13.4 revision 6 forbids.\n const snapshot =\n inherited ??\n createRequestSnapshot({\n url: urlOf(effective),\n method: methodOf(effective),\n headers: effective?.headers,\n bodyKind,\n body: effective?.data,\n });\n const info: UpgradeReplayInfo = { url: snapshot.href, method: snapshot.method, bodyKind };\n const permitted = bodyKind !== 'one-shot' && policyPermits(options.replay, info);\n\n return {\n status: response?.status ?? 0,\n getHeader: headerReader(response?.headers),\n // Pre-read by axios into `data`; nothing is consumed by reading it again,\n // and the core still decides whether reading it is in scope.\n readBody: async () => response?.data,\n request: snapshot,\n replay: async (hints?: ReplayHints) => {\n const next = markUpgradeReplayed(replayConfig(effective ?? {}, snapshot, hints));\n try {\n const replayed = (await reissue(next)) as AxiosResponseLike;\n return contextFor(replayed, replayed?.config ?? next, replayed, reissue, options, snapshot);\n } catch (e) {\n // axios REJECTS a non-2xx, so the replay's own 401 — the second\n // challenge, which is the normal AAL3/TOTP-only path — arrives here as\n // a rejection rather than as a response. Handing it to the core as an\n // exchange is the only way the core can classify it; letting it escape\n // sent a raw `AxiosError`, complete with `config` and `response`, over\n // the SDK's public edge where §13.3 requires `upgrade_insufficient`.\n //\n // No logic is added: this adapter still parses nothing and compares no\n // bar. It only translates axios' rejection convention into the one\n // exchange shape the core reads, and flags that the exchange was\n // delivered as a rejection so the settlement can honour the same\n // convention on the way back out.\n //\n // A rejection with no response is not an exchange — a replay that never\n // reached the server is the caller's error, not a step-up outcome — and\n // neither is a settled upgrade outcome, which is what re-entering this\n // same interceptor on the attached surfaces produces.\n const failure = (isRecord(e) ? e : {}) as AxiosErrorLike;\n if (!failure.response || typeof failure.response.status !== 'number' || isUpgradeSdkError(e)) {\n throw e;\n }\n const replayed = contextFor(failure.response, failure.config ?? next, e, reissue, options, snapshot);\n return { ...replayed, rejected: true };\n }\n },\n markers: {\n ...(hasUpgradeTransactionMarker(effective) ? { upgradeTransaction: true as const } : {}),\n ...(hasUpgradeReplayedMarker(effective) ? { replayed: true as const } : {}),\n },\n raw,\n replayable: permitted,\n replayUnsupportedReason:\n bodyKind === 'one-shot'\n ? 'a streaming request body is consumed by the first send'\n : 'the replay policy refused this request',\n signal: effective?.signal,\n };\n};\n\n/**\n * The rejection handler itself: an `AxiosError` in, a replayed response or a\n * tagged rejection out.\n *\n * Shared by `attachUpgradeAuth` (the customer's own instance) and by the SDK's\n * internal seam, so both surfaces settle from the same table.\n */\nexport const createAxiosUpgradeHandler =\n (source: UpgradeInterceptorOptionsSource, reissue: AxiosReissue) =>\n async (error: unknown): Promise<unknown> => {\n // Resolved PER CHALLENGE. A handler built at `enable()` time that captured\n // the RESOLVED options would freeze the interception scope at attach time,\n // which is how a module-scope client ended up pinned to `backend: []`.\n const options = resolveInterceptorOptions(source);\n const typed = (isRecord(error) ? error : {}) as AxiosErrorLike;\n const ctx = contextFor(typed.response, typed.config, typed.response, reissue, options);\n\n const outcome = await handleChallenge(ctx, options);\n const settlement = settleAxiosOutcome<unknown>(outcome, error);\n if (settlement.settle === 'reject') {\n throw settlement.error;\n }\n return settlement.value;\n };\n\n/**\n * Attaches step-up handling to a client the developer already owns, MUTATING\n * it, and returns a disposer that ejects exactly the interceptor it added.\n *\n * It returns the disposer rather than the instance on purpose: it mutates, and\n * returning the instance would invite `const api = attachAuth(axios.create())`\n * and read as if it had built something. The disposer is also what a React\n * effect needs.\n *\n * `reissue` defaults to the host's own `request`, duck-typed, so the replay\n * goes back through the customer's interceptors, `baseURL` and adapter rather\n * than through some instance of ours.\n */\nexport const attachUpgradeAuth = (\n host: ResponseInterceptorHost,\n source: UpgradeInterceptorOptionsSource,\n reissue?: AxiosReissue,\n): (() => void) => {\n const own = (host as unknown as { request?: (config: AxiosConfigLike) => Promise<unknown> }).request;\n const send: AxiosReissue =\n reissue ??\n (typeof own === 'function'\n ? (config) => own.call(host, config)\n : () => Promise.reject(new Error('Passflow: this client exposes no request() to replay through.')));\n\n const id = host.interceptors.response.use(undefined, createAxiosUpgradeHandler(source, send));\n let ejected = false;\n return () => {\n if (ejected) {\n return;\n }\n ejected = true;\n host.interceptors.response.eject(id);\n };\n};\n","/**\n * The FETCH adapter over `handleChallenge`. One of four, and the thinnest.\n *\n * **NO LOGIC IN AN ADAPTER.** Everything here is translation: a `Response`\n * becomes a `ChallengeContext` on the way in, and a `CoreOutcome` becomes a\n * `Response` (or a throw) on the way out, through `settleFetchOutcome`. It\n * parses no challenge, compares no bar, decides no scope, counts no retry and\n * chooses no error class. If something in this file ever looks like a decision,\n * it belongs in `core.ts` instead — a row that passes here and fails on the\n * axios surface is a leak out of the core, not a difference between transports.\n *\n * The one genuinely transport-shaped obligation it carries is the body: a\n * replay needs the request body while it is still unread, so `planReplay` runs\n * BEFORE the first call and the core is told whether a replay is even possible\n * before any prompt opens. The caller's own response is never consumed — every\n * body read is taken from `clone()`, so `bodyUsed === false` holds on the object\n * the caller receives.\n *\n * SSR-safe: no global is read at module scope. `withAuth()`'s default `next` is\n * resolved inside the call, not here.\n */\n\nimport {\n type AbortView,\n type ChallengeContext,\n type UpgradeInterceptorOptionsSource,\n handleChallenge,\n resolveInterceptorOptions,\n settleFetchOutcome,\n} from './core';\nimport { type FetchLike, type ReplayHints, type ReplayPlan, planReplay } from './replay';\n\n/**\n * How many upgrade-aware fetch layers are DISPATCHING through this one, right\n * now, on this synchronous call path.\n *\n * ## The defect\n *\n * `markers` on the fetch surface is a per-wrapper object and is deliberately\n * never on the wire. That is right for one wrapper and wrong for two: with\n * `interceptFetch: true` AND an app-held `withAuth(fetch)` on the same path,\n * the inner layer elevates and replays, and if the server re-challenges the\n * replay the OUTER layer sees a 401 whose markers are empty and elevates again.\n * Two prompts and two challenges for one user action, against a server budget\n * that caps at a few attempts — the single-flight NFR, violated by stacking.\n *\n * ## Why a counter, and why this one\n *\n * Three fixes were on the table. (1) Tag the in-flight request: the only\n * identity both layers share is the caller's `input`/`init` objects, and\n * `fetch('/x')` has neither, so a `WeakSet` keyed on them silently misses the\n * commonest call shape — and a header would make the marker transmissible,\n * which is prohibited. (2) Have `registerFetchMiddleware` refuse to install\n * when a wrapper is already active on that path: it cannot know, because an\n * app-held `withAuth` wrapper is not registered anywhere and the global patch\n * cannot see it. (3) This: make \"a layer above me already owns this call\"\n * observable, and pass through when it is.\n *\n * The counter is safe against concurrency because it is only ever held across\n * SYNCHRONOUS code. `dispatch` increments, calls `next`, and decrements in\n * `finally` — all before the first `await` — so the window is exactly the inner\n * layer's own synchronous prologue, where it reads the counter. No other\n * request can interleave into a synchronous span, so this is a dynamic extent,\n * not ambient state, and it is SSR-safe for the same reason: it is never\n * non-zero across a suspension point.\n *\n * The limit, stated rather than hidden: an intervening wrapper that `await`s\n * before calling `fetch` breaks the extent, and both layers then elevate as\n * they do today. The fix narrows the defect to that case instead of closing it\n * absolutely — a marker that closed it absolutely would have to travel with the\n * request, which is the thing we must not do. The axios seam carries its own\n * config marker and is a separate channel; a stacked fetch-plus-axios path is\n * not covered here.\n */\nlet dispatchingLayers = 0;\n\n/**\n * A live view of the caller's signal.\n *\n * Read through getters because the core checks it AFTER awaiting the elevation:\n * a boolean copied at call time would always say \"not aborted\". The listener\n * pair is forwarded as well — that is what lets the core RACE the abort against\n * a pending proof (REQ-C12) instead of discovering it once the proof settles.\n * Both are bound to the caller's own signal; this adapter never aborts it and\n * never creates a signal of its own.\n */\nconst abortViewOf = (input: RequestInfo | URL, init?: RequestInit): AbortView | undefined => {\n const signal =\n init?.signal ?? (typeof input === 'object' && input !== null ? (input as Request).signal : undefined) ?? undefined;\n if (!signal) {\n return undefined;\n }\n return {\n get aborted(): boolean {\n return signal.aborted;\n },\n get reason(): unknown {\n return (signal as AbortSignal & { reason?: unknown }).reason;\n },\n ...(typeof signal.addEventListener === 'function'\n ? {\n addEventListener: (type: 'abort', listener: () => void) => signal.addEventListener(type, listener),\n removeEventListener: (type: 'abort', listener: () => void) => signal.removeEventListener(type, listener),\n }\n : {}),\n };\n};\n\n/** Reads a header off a `Response`, in the callback shape the one parser takes. */\nconst headerReader =\n (response: Response) =>\n (name: string): string | null | undefined =>\n response.headers?.get(name);\n\n/**\n * Wraps one `Response` as a `ChallengeContext`.\n *\n * `markers` is shared with the original context, which is the fetch surface's\n * answer to marker storage: the wrapper owns the call, so a per-request object\n * is the whole mechanism. The replayed response re-uses it, so the core's\n * re-entry guard reads the same state it wrote.\n */\nconst contextFor = (\n response: Response,\n plan: ReplayPlan,\n next: FetchLike,\n markers: ChallengeContext['markers'],\n abort: AbortView | undefined,\n): ChallengeContext => ({\n status: response.status,\n getHeader: headerReader(response),\n // A CLONE, always. The response the caller receives must still have an unread\n // body, and the core calls this only when the scope and the channel policy\n // both permit a body read.\n readBody: () => response.clone().json(),\n // The PLAN's snapshot, never a live `urlOf(input)` read at challenge time.\n // Reading the caller's input here was the asymmetry §13.4 revision 6 closes:\n // the request that was authorized and the request that is replayed must be\n // the same request, and the only way to guarantee that is for both to read\n // the one frozen object.\n request: plan.snapshot,\n replay: async (hints?: ReplayHints) => {\n const replayed = await plan.replay(next, hints);\n return contextFor(replayed, plan, next, markers, abort);\n },\n markers,\n raw: response,\n replayable: plan.replayable,\n replayUnsupportedReason: plan.reason,\n signal: abort,\n responseType: response.type,\n});\n\n/**\n * Wraps a `fetch`, returning a new function. Installs nothing and touches no\n * global — this is the SSR- and React-Native-safe entry point, and the one the\n * global patch itself is built from.\n */\nexport const upgradeFetch = (next: FetchLike, source: UpgradeInterceptorOptionsSource): FetchLike => {\n /**\n * Sends through `next` while announcing that THIS layer owns the call.\n *\n * Used for the first send and for every replay, so a nested layer passes the\n * replay through too — otherwise the inner layer would claim the replay that\n * this layer's elevation produced, which is the stacking defect itself.\n */\n const dispatch: FetchLike = (nestedInput, nestedInit) => {\n dispatchingLayers += 1;\n try {\n return next(nestedInput, nestedInit);\n } finally {\n dispatchingLayers -= 1;\n }\n };\n\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (dispatchingLayers > 0) {\n // A layer above us is handling this call: it plans the replay, it owns the\n // elevation, and one challenge is spent. Pure pass-through — no plan, no\n // body clone, no prompt. `dispatch` keeps the claim for layers below us.\n return dispatch(input, init);\n }\n\n // Resolved PER REQUEST, not at wrap time: a `withAuth()` wrapper built\n // before `enable()` must still see the scope `enable()` installed, and a\n // later `enable()` with a narrower `backend` must narrow this wrapper too.\n const options = resolveInterceptorOptions(source);\n\n // Before the first send: a `Request` body can only be cloned while unread.\n const plan = planReplay(input, init, options.replay);\n const response = await dispatch(plan.forwardInput, plan.forwardInit);\n\n const ctx = contextFor(response, plan, dispatch, {}, abortViewOf(input, init));\n const outcome = await handleChallenge(ctx, options);\n\n const settlement = settleFetchOutcome(outcome, response, options);\n if (settlement.settle === 'reject') {\n throw settlement.error;\n }\n return settlement.value;\n };\n};\n","/**\n * `createClient` — the greenfield surface, for a developer with no HTTP client\n * of their own.\n *\n * It is **not a fourth code path**: it is `axios.create(config)` followed by the\n * axios adapter, which is why it cannot drift from `attachAuth`. The attach is\n * passed in rather than imported so this module holds no scope, no controller\n * and no options — only the constructor.\n *\n * It also costs no new dependency: `axios` is already a hard dependency of this\n * package. This is the one place axios' own types are deliberately back on the\n * public surface — `attachAuth`'s structural `ResponseInterceptorHost` exists to\n * avoid exactly that, and the asymmetry is the point: a developer asking us to\n * BUILD their client has accepted axios, while one handing us their own must not\n * be pinned to our major.\n */\n\nimport axios, { type AxiosInstance, type CreateAxiosDefaults } from 'axios';\n\nimport type { ResponseInterceptorHost } from './axios-adapter';\n\n/**\n * axios' own create options plus nothing of ours, so `baseURL`, `headers`,\n * `timeout`, `adapter` and interceptor ordering all stay the customer's.\n *\n * A `baseURL` is NOT read as a declared backend: that would be the derived\n * interception model through a side door. An un-declared `baseURL` gets a\n * `'challenge-out-of-scope'` diagnostic naming `backend` as the fix.\n */\nexport type CreateClientConfig = CreateAxiosDefaults;\n\nexport const createUpgradeClient = (\n config: CreateClientConfig | undefined,\n attachAuth: (host: ResponseInterceptorHost) => () => void,\n): AxiosInstance => {\n const instance = axios.create(config);\n attachAuth(instance);\n return instance;\n};\n","/**\n * One `fetch` patch point, many middlewares.\n *\n * This module exists because of a bug, not a requirement (architecture §4.3,\n * reaffirmed by §13.5's \"what survives supersession\"). Two features that each do\n * `globalThis.fetch = wrapper` in an effect and restore their captured original\n * in cleanup compose as `A(B(native))`, and whichever cleanup runs first assigns\n * a fetch that predates the other patch. React guarantees no cleanup ordering\n * across sibling effects, nor across a parent/child boundary under Strict Mode\n * double-invocation, so one of the two interceptors silently disappears —\n * non-deterministically, and only in composed mounts.\n *\n * The fix is to make the patch a shared resource with reference-counted\n * ownership: the registry installs exactly one wrapper on the first\n * registration, composes the registered middlewares by priority on every call,\n * and removes the wrapper on the last removal. **Teardown is the registry's\n * responsibility, not any caller's** — no caller captures or restores\n * `globalThis.fetch` itself, which is precisely the ownership mistake being\n * retired.\n *\n * Composition is a Decorator chain, not a Chain of Responsibility: every\n * middleware MUST delegate to `next`. A link that may legitimately swallow a\n * request would mean a stranger's `fetch` call can vanish inside our wrapper, so\n * non-delegation is detected per invocation and reported as a named error rather\n * than left to surface as a hung promise (§9.4/R7).\n *\n * SSR safety is a stated non-functional requirement and is structural here:\n * `globalThis` is read only inside `install()` and inside the state reader,\n * never at module scope. Importing this module on a server touches no global and\n * throws nothing; `registerFetchMiddleware` returns a no-op unregister when\n * there is no `fetch` to patch (§9.4/R6).\n *\n * The module is generic. It knows nothing about upgrades, OIDC or Passflow\n * endpoints — only about ordering, ownership and delegation.\n */\n\n/** A `fetch`-shaped function: the platform `fetch`, or the next link in the chain. */\nexport type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * A registered interception layer.\n *\n * It receives the next link explicitly rather than closing over a captured\n * global, which is what lets the chain be rebuilt per call and reordered by\n * priority without any middleware knowing its neighbours.\n */\nexport type FetchMiddleware = (next: FetchLike, input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nexport type RegisterOptions = {\n /** Lower runs OUTER (further from the network). Ties break by registration order. */\n priority: number;\n /** Diagnostic label, surfaced in the error thrown when a middleware fails to delegate. */\n name: string;\n};\n\n/**\n * Outer layer. The OIDC AuthFlow interceptor must observe the response the\n * caller will actually receive — including a replayed one — because it follows a\n * `redirect_url` out of that body. Placed inside the upgrade middleware it would\n * inspect the raw 401, do nothing, and the page would then navigate on a\n * response the caller never saw.\n */\nexport const FETCH_PRIORITY_OIDC = 10;\n\n/**\n * Inner layer. The upgrade middleware must see the raw 401 and replace it before\n * anything upstream forms an opinion about it.\n */\nexport const FETCH_PRIORITY_UPGRADE = 20;\n\n/** Thrown when a registered middleware resolves without ever calling `next`. */\nexport class FetchMiddlewareContractError extends Error {\n /** The `name` the offending middleware was registered under. */\n readonly middlewareName: string;\n\n constructor(middlewareName: string) {\n super(\n `Passflow fetch middleware '${middlewareName}' resolved without calling next(). ` +\n 'Every registered middleware must delegate exactly once; swallowing a request would ' +\n \"drop a caller's fetch inside the Passflow wrapper.\",\n );\n this.name = 'FetchMiddlewareContractError';\n this.middlewareName = middlewareName;\n }\n}\n\ntype RegistryEntry = {\n readonly middleware: FetchMiddleware;\n readonly name: string;\n readonly priority: number;\n /** Registration sequence, used only as a stable tie-break within one priority. */\n readonly seq: number;\n};\n\n/** Registered entries, unsorted. Sorting happens per call so order is never stale. */\nlet entries: RegistryEntry[] = [];\n\n/** Monotonic registration counter. Never reset while entries are live. */\nlet nextSeq = 0;\n\n/** True once we have assigned our wrapper and have not restored over it. */\nlet installed = false;\n\n/** The `fetch` captured at install time — the innermost link of every chain. */\nlet capturedFetch: FetchLike | undefined;\n\n/** The exact function we assigned to `globalThis.fetch`, used for identity checks. */\nlet ourWrapper: FetchLike | undefined;\n\n/** Outer-first: ascending priority, then registration order. */\nconst byPriority = (a: RegistryEntry, b: RegistryEntry): number => a.priority - b.priority || a.seq - b.seq;\n\nconst globalScope = (): typeof globalThis | undefined => (typeof globalThis === 'undefined' ? undefined : globalThis);\n\n/**\n * Runs the chain from `index` outward-in, ending at the captured fetch.\n *\n * `chain` is a snapshot taken when the call started: a registration or removal\n * that lands mid-flight changes the next call, never this one. Each frame\n * records whether its middleware delegated, so a non-delegating middleware is\n * named in the error rather than leaving the caller to guess which layer ate the\n * request.\n */\nconst dispatch = async (\n chain: readonly RegistryEntry[],\n index: number,\n input: RequestInfo | URL,\n init?: RequestInit,\n): Promise<Response> => {\n if (index >= chain.length) {\n const target = capturedFetch;\n if (!target) {\n // Unreachable while installed: the capture and the assignment happen\n // together in install(). Explicit rather than a TypeError on undefined.\n throw new Error('Passflow fetch registry: no captured fetch to delegate to.');\n }\n return target(input, init);\n }\n\n const entry = chain[index];\n if (!entry) {\n return dispatch(chain, chain.length, input, init);\n }\n\n let delegated = false;\n const next: FetchLike = (nextInput, nextInit) => {\n delegated = true;\n return dispatch(chain, index + 1, nextInput, nextInit);\n };\n\n const response = await entry.middleware(next, input, init);\n if (!delegated) {\n throw new FetchMiddlewareContractError(entry.name);\n }\n return response;\n};\n\n/**\n * Assigns our single wrapper, capturing the current `fetch` as the chain's\n * innermost link.\n *\n * Returns false when there is nothing to patch — no global scope, or no callable\n * `fetch` — which is the SSR path. Nothing throws there: an import-time failure\n * in a server bundle is the only outcome that actually matters.\n */\nconst install = (): boolean => {\n const scope = globalScope();\n if (!scope || typeof scope.fetch !== 'function') {\n return false;\n }\n\n const native = scope.fetch.bind(scope) as FetchLike;\n const wrapper: FetchLike = (input, init) => dispatch([...entries].sort(byPriority), 0, input, init);\n\n capturedFetch = native;\n ourWrapper = wrapper;\n scope.fetch = wrapper as typeof scope.fetch;\n installed = true;\n return true;\n};\n\n/**\n * Removes our wrapper — but only if it is still the value we assigned.\n *\n * If a third party patched on top of us, their wrapper very likely captured ours\n * as its own next link. Restoring the captured fetch would clobber a stranger's\n * patch; so we leave ours in place and let it degrade to a pass-through, which\n * an empty middleware list already is. This is the pre-existing\n * unconditional-restore hazard in `passflow-provider.tsx` fixed by construction\n * (§9.4/R5).\n *\n * `installed` deliberately stays true in that case. Clearing it would let a\n * later registration install a SECOND wrapper while the first is still reachable\n * through the stranger's chain — the nesting collision this registry exists to\n * prevent.\n */\nconst uninstall = (): void => {\n const scope = globalScope();\n if (!scope || !ourWrapper) {\n return;\n }\n if (scope.fetch !== (ourWrapper as unknown as typeof scope.fetch)) {\n return;\n }\n\n if (capturedFetch) {\n scope.fetch = capturedFetch as typeof scope.fetch;\n }\n installed = false;\n ourWrapper = undefined;\n capturedFetch = undefined;\n};\n\n/**\n * Registers a middleware, installing the one global patch on the FIRST\n * registration, and returns an idempotent unregister that removes the patch on\n * the LAST removal.\n *\n * Safe in both directions and under Strict Mode's double-invoke: ownership is by\n * reference count over entry identity, so register/unregister/register of the\n * same middleware, and removals in any order relative to other registrations,\n * all converge. The returned function is bound to this one registration — a\n * second call does nothing, and registering the same middleware twice yields two\n * independent entries and two independent unregisters (§9.4/R8).\n */\nexport const registerFetchMiddleware = (middleware: FetchMiddleware, opts: RegisterOptions): (() => void) => {\n if (!installed && !install()) {\n // SSR, or a runtime with no fetch: nothing is patched and nothing is queued.\n // A no-op unregister keeps the caller's cleanup path identical either way.\n return () => {\n /* nothing was registered, so there is nothing to remove */\n };\n }\n\n const entry: RegistryEntry = {\n middleware,\n name: opts.name,\n priority: opts.priority,\n seq: nextSeq++,\n };\n entries.push(entry);\n\n let removed = false;\n return () => {\n if (removed) {\n return;\n }\n removed = true;\n entries = entries.filter((candidate) => candidate !== entry);\n if (entries.length === 0) {\n uninstall();\n }\n };\n};\n\n/**\n * Test and diagnostic read of the registry.\n *\n * `names` is in execution order, outermost first. `foreignPatch` is computed\n * live rather than remembered, so it reports a stranger's patch as soon as it\n * lands, not only once a teardown has declined to restore over it.\n */\nexport const fetchRegistryState = (): { installed: boolean; names: string[]; foreignPatch: boolean } => {\n const scope = globalScope();\n return {\n installed,\n names: [...entries].sort(byPriority).map((entry) => entry.name),\n foreignPatch: installed && !!scope && !!ourWrapper && scope.fetch !== (ourWrapper as unknown as typeof scope.fetch),\n };\n};\n","/**\n * The Facade: one object that assembles the controller, the transport and the\n * adapters, and the only construction surface a consumer touches.\n *\n * `UpgradeController`, the transport, `upgradeFetch` and the fetch registry stay\n * internal. Tests reach their types by importing this directory's modules\n * directly, not through the package entry, because a type on `lib/index.ts` is\n * a supported name whether or not anything public produces a value of it.\n * `UpgradeFacadeDeps` is declared here and exported for that in-tree use only.\n * `UpgradeSessionPin`, declared in `./session`, is the exception: it IS on the\n * entry, because `AuthService.readSessionPin()` is public and returns one.\n *\n * **Five members, and the rule is written down because `structural.md` names\n * Facade-as-God-Object as this family's characteristic failure: adding a sixth\n * requires deleting one.** `createClient` is deliberately NOT a member — it is\n * `axios.create(config)` followed by `attachAuth`, so it lives on the `Passflow`\n * instance where the constructor belongs, and it is not a fourth code path.\n *\n * ## The two gates\n *\n * Passflow's own API (domain 1) is derived from `PassflowConfig.url` and handled\n * with zero configuration. The customer's backend (domain 2) is touched only\n * when `backend` is declared AND an adapter is attached: declaring without\n * attaching observes nothing, attaching without declaring matches nothing\n * beyond domain 1. That double gate is why the elevated-credential replay\n * surface is unreachable in the default configuration — there is no default\n * interception surface at all.\n *\n * ## The session-change reset\n *\n * A challenge is server-bound to the session's `sid`, and an abandoned flight\n * must never elevate a NEW session. `UpgradeController.inFlight` is private with\n * no reset, so on `SignIn` / `SignOut` the controller is DISCARDED and a fresh\n * one constructed. A waiter whose elevation was running across that boundary is\n * settled with `UpgradeSessionChangedError` — never replayed — which §13.3's\n * table maps to the original response on fetch and a tagged rejection on axios.\n *\n * SSR-safe: no global is read at module scope, and nothing is installed until\n * `enable()` is called with `interceptFetch`.\n */\n\nimport { FETCH_PRIORITY_UPGRADE, registerFetchMiddleware } from '../interceptors/fetch-registry';\nimport type { PlatformAdapter } from '../platform';\n\nimport { readAcrValues } from './acr';\nimport { type AxiosReissue, type ResponseInterceptorHost, attachUpgradeAuth, createAxiosUpgradeHandler } from './axios-adapter';\nimport type { AssuranceBar, UpgradeChallenge, UpgradeDiagnosticSink } from './challenge';\nimport {\n UpgradeController,\n type UpgradeElevation,\n type UpgradeFlightSummary,\n type UpgradePromptHandler,\n type UpgradeTransport,\n type Waiter,\n} from './controller';\nimport type { InterceptionScope, UpgradeInterceptorOptions } from './core';\nimport { UpgradeSessionChangedError, UpgradeTransportError, UpgradeUnavailableError } from './errors';\nimport type { UpgradeFactor } from './factors';\nimport { upgradeFetch } from './fetch-adapter';\nimport type { FetchLike, ReplayPolicy } from './replay';\nimport type { UpgradeSessionPin } from './session';\nimport { type UpgradeTransportDeps, createUpgradeTransport } from './transport';\n\n/** Domain 1, as resolved once from `PassflowConfig.url`. `null` = unresolved. */\nexport type PassflowUpgradeDomain = { origin: string; pathPrefix?: string } | null;\n\n/**\n * What the facade needs from its host, as narrow function-shaped dependencies.\n *\n * Everything HTTP arrives pre-wired, which is what keeps this module free of\n * endpoints and of any opinion about delivery mode.\n */\nexport type UpgradeFacadeDeps = UpgradeTransportDeps & {\n /** The platform adapter, for the WebAuthn ceremony. */\n platform: PlatformAdapter;\n /** Domain 1. Resolved by the host from `url`; the facade never derives it. */\n passflowDomain: PassflowUpgradeDomain;\n /** Construction-time factor override, from `PassflowConfig.upgrade`. */\n factors?: readonly UpgradeFactor[];\n /** The bar the elevation reached, read from the freshly stored access token. */\n readObtainedBar?: () => AssuranceBar | undefined;\n /** §5.5's credential swap. Snapshotted before the elevation, read again after. */\n authorizationSwap?: () => { from: string; to: string } | undefined;\n /**\n * Installs (or clears, on `undefined`) the handler the SDK's OWN axios clients\n * consult inside their existing error interceptor — §4.4's internal seam,\n * which an ordinary customer-registered interceptor is structurally too late\n * to be.\n *\n * It receives a FACTORY rather than a handler because each of the SDK's\n * clients must replay through its own instance: a handler built around one\n * client's `request` would re-issue another client's request on the wrong\n * instance.\n */\n attachInternalSeam?: (factory: ((reissue: AxiosReissue) => (error: unknown) => Promise<unknown>) | undefined) => void;\n /** Subscribes to session changes (`SignIn` / `SignOut`). Returns unsubscribe. */\n onSessionChange?: (listener: () => void) => () => void;\n /**\n * The session and user identity the host can currently read, or `undefined`\n * when this delivery mode exposes none.\n *\n * Read ONCE per flight, when the flight starts, and handed to `commitRefresh`\n * so the token-applying path can refuse to write user A's tokens into user\n * B's session (§13.4 revision 6 / H2).\n */\n readSessionPin?: () => UpgradeSessionPin | undefined;\n};\n\n/**\n * Everything `enable()` accepts: the prompt, the interception scope and the\n * interception policy.\n *\n * `requireChallengeChannel`, `backend` and `shouldIntercept` are interception\n * policy and deliberately NOT on `PassflowConfig` — they are per-installation,\n * not client-wide.\n */\nexport type UpgradeEnableOptions = {\n /** REQUIRED. The js-sdk ships no UI. */\n prompt: UpgradePromptHandler;\n /**\n * Where the customer's own API is: an origin, an origin with a path prefix, or\n * a same-origin path prefix (`'/api'`). Nothing outside this and domain 1 is\n * ever observed, and there is no allow-all opt-in — a developer who wants\n * every origin lists every origin, which is a sentence they can read in a diff.\n */\n backend?: string | string[];\n /**\n * Patches `globalThis.fetch` through the registry, at the upgrade priority.\n * **Off by default**, and a different decision from enabling the UI: one prop\n * for both would bundle \"show me the 2FA prompt\" with \"patch the global\", and\n * only the second has the blast radius. It widens which call sites are\n * observed, never the scope.\n */\n interceptFetch?: boolean;\n /** Requires the standards channel. The body is then never read. */\n requireChallengeChannel?: 'header';\n /** NARROWING ONLY: `false` excludes an admitted URL; `true` cannot widen. */\n shouldIntercept?: (url: string) => boolean;\n replay?: ReplayPolicy;\n /** The fetch surface's only option. Default `'resolve-original'`. */\n onCancel?: 'resolve-original' | 'reject';\n onUpgradeDiagnostic?: UpgradeDiagnosticSink;\n factors?: readonly UpgradeFactor[];\n maxAttempts?: number;\n /** One terminal state per flight, after every waiter has reported. */\n onFlightSettled?: (summary: UpgradeFlightSummary) => void;\n /** Forwarded verbatim to the prepare round for passkey upgrades. */\n relyingPartyId?: string;\n};\n\n/** What the imperative seam resolves with (§13.4). */\nexport type UpgradeResult = { status: 'elevated'; acr?: string };\n\n/**\n * What the imperative seam accepts: a bar the CALLER states.\n *\n * Stating one is what makes `UpgradeInsufficientError` reachable on this\n * surface; an options-less call states no bar, has no protected operation to\n * replay, and resolves `{ status: 'elevated' }` (§13.3's `n/a` cell).\n */\nexport type UpgradeOptions = { acrValues?: string; maxAge?: number };\n\nexport type UpgradeFacade = {\n /**\n * Drives an elevation directly — §13.4's imperative seam.\n *\n * The facade is CALLABLE, which is why this signature is here rather than on\n * a method: the documented surface is `passflow.upgrade(options?)`, and the\n * same object also carries `enable`, `withAuth` and the rest. Resolves on\n * proof completion; rejects `UpgradeCancelledError`,\n * `UpgradeUnsatisfiableError`, `UpgradeUnavailableError`,\n * `UpgradeSessionChangedError`, `UpgradeTransportError` — and, when the\n * caller stated a bar the proof did not reach, `UpgradeInsufficientError`.\n */\n (options?: UpgradeOptions): Promise<UpgradeResult>;\n /**\n * Installs the prompt handler and the selected interceptors. Returns an\n * idempotent teardown. Calling `enable()` twice replaces the previous\n * installation and tears the first one down.\n */\n enable(options: UpgradeEnableOptions): () => void;\n /** True when a prompt handler is installed AND the delivery mode can commit. */\n readonly enabled: boolean;\n /**\n * Drives an elevation directly — headless clients, a \"verify now\" button,\n * tests. Single-flight with the interceptors: concurrent callers share one\n * prompt, because they share one controller.\n */\n elevate(challenge: UpgradeChallenge, waiter?: Waiter): Promise<UpgradeElevation>;\n /** Wraps a fetch and returns the wrapper. Touches no global. */\n withAuth(next?: FetchLike): FetchLike;\n /** Attaches to an axios instance the developer owns, MUTATING it. Returns `detach()`. */\n attachAuth(instance: ResponseInterceptorHost): () => void;\n};\n\n/** `backend` as declared → the list the scope predicate reads. */\nconst declaredBackends = (backend: string | string[] | undefined): string[] => {\n if (backend === undefined) {\n return [];\n }\n return (Array.isArray(backend) ? backend : [backend]).filter((pattern) => pattern !== '');\n};\n\ntype Installation = {\n controller: UpgradeController;\n options: UpgradeEnableOptions;\n scope: InterceptionScope;\n /** Bumped on every session change; a waiter from an older generation is stale. */\n generation: number;\n /**\n * The identity the CURRENT flight was pinned to, captured when that flight\n * started. Per flight rather than per installation, because an installation\n * outlives a flight.\n */\n pin?: UpgradeSessionPin;\n};\n\nexport const createUpgradeFacade = (deps: UpgradeFacadeDeps): UpgradeFacade => {\n let installation: Installation | undefined;\n\n /** Is this installation still the live one, or has the session moved on? */\n const isCurrent = (owner: Installation | undefined): boolean => owner !== undefined && installation === owner;\n\n /**\n * The transport an installation's controller is given: the host's rounds,\n * each one gated on its owner still being the live installation.\n *\n * The epoch cannot be checked only around `controller.elevate()`, because\n * prepare, commit and token storage all happen INSIDE that one await. A\n * flight whose session changed mid-answer would still commit against the\n * server and store the elevated tokens over the replacement session's, and\n * only then throw. So the check sits immediately before each round that has a\n * side effect, which is the only place it can be both before the commit and\n * after the user's answer.\n *\n * `markSessionInvalid` is gated the same way for the opposite reason: a\n * DISCARDED controller must not be able to reach into the session that\n * replaced it. Its callbacks become no-ops rather than acting on a session\n * they know nothing about.\n *\n * It stays stateless, so one per installation costs nothing; the\n * relying-party id still prefers the one `enable()` was given.\n */\n const transportFor = (owner: () => Installation | undefined): UpgradeTransport => {\n const assertCurrent = (): void => {\n if (!isCurrent(owner())) {\n // Classified, not bare: `classifyUpgradeError` passes an\n // `UpgradeTransportError` through untouched, and `session_changed` is\n // the kind the controller already maps to a terminal\n // `UpgradeSessionChangedError`. A bare throw would be re-bucketed as a\n // network failure by the transport's own catch.\n throw new UpgradeTransportError({ kind: 'session_changed' });\n }\n };\n\n return createUpgradeTransport({\n ...deps,\n relyingPartyId: () => installation?.options.relyingPartyId ?? deps.relyingPartyId?.(),\n startUpgrade: (payload) => {\n assertCurrent();\n return deps.startUpgrade(payload);\n },\n commitRefresh: (upgrade) => {\n assertCurrent();\n // The pin travels with the commit so the host can verify it at the\n // moment it APPLIES the rotated tokens. `assertCurrent` above is the\n // epoch half and runs first; the two are complementary — the epoch says\n // something changed, the pin says whether it is still the same\n // principal, and only the second can be checked inside the storing path.\n return deps.commitRefresh(upgrade, owner()?.pin);\n },\n ...(deps.markSessionInvalid\n ? {\n markSessionInvalid: (): void => {\n if (isCurrent(owner())) {\n deps.markSessionInvalid?.();\n }\n },\n }\n : {}),\n });\n };\n\n /**\n * Builds an installation and the controller that belongs to it.\n *\n * The box exists because the controller's transport must be able to name its\n * owner, and the owner is not constructed until the controller is in hand.\n */\n const buildInstallation = (options: UpgradeEnableOptions, generation: number): Installation => {\n const box: { current?: Installation } = {};\n const controller = new UpgradeController({\n transport: transportFor(() => box.current),\n prompt: options.prompt,\n platform: deps.platform,\n factors: options.factors ?? deps.factors,\n ...(options.maxAttempts === undefined ? {} : { maxAttempts: options.maxAttempts }),\n ...(deps.readObtainedBar ? { readObtainedBar: deps.readObtainedBar } : {}),\n ...(options.onFlightSettled ? { onFlightSettled: options.onFlightSettled } : {}),\n });\n\n const built: Installation = {\n controller,\n options,\n scope: { passflow: deps.passflowDomain, backend: declaredBackends(options.backend) },\n generation,\n };\n box.current = built;\n return built;\n };\n\n /**\n * The `elevate` the core is given.\n *\n * Two obligations live here and nowhere else: route every surface through the\n * ONE controller instance (that is what makes single-flight hold across the\n * fetch wrapper, an attached axios instance and the internal seam), and refuse\n * to hand back an elevation that belongs to a different session than the\n * challenge did.\n */\n const elevateFor = async (challenge: UpgradeChallenge, waiter: Waiter): Promise<UpgradeElevation> => {\n const current = installation;\n if (!current) {\n // Nothing installed means no prompt and no way to ask the user, which is\n // exactly the `unavailable` row of the settlement table.\n throw new UpgradeUnavailableError('Step-up verification is not enabled on this Passflow instance');\n }\n\n // Pin the flight to the principal it STARTS under. `active` is false only\n // between flights — it is set before the prompt opens and cleared in the\n // final synchronous step — so this is exactly \"the first waiter of a new\n // flight\", and every joiner reuses the pin the flight began with.\n if (!current.controller.active) {\n current.pin = deps.readSessionPin?.();\n }\n\n const elevation = await current.controller.elevate(challenge, waiter);\n\n if (!isCurrent(current)) {\n // The session changed under the flight. Replaying now would carry a NEW\n // session's elevated credential to a request challenged under the old\n // one. The prepare and commit rounds are guarded separately, inside this\n // installation's own transport, because they run inside the await above —\n // this check protects the replay, and only the replay.\n throw new UpgradeSessionChangedError();\n }\n\n return elevation;\n };\n\n /** The resolved interception options, read fresh per call so `enable()` is live. */\n const interceptorOptions = (): UpgradeInterceptorOptions => {\n const current = installation;\n const scope: InterceptionScope = current?.scope ?? { passflow: deps.passflowDomain, backend: [] };\n const options = current?.options;\n\n return {\n elevate: elevateFor,\n scope,\n ...(options?.requireChallengeChannel ? { requireChallengeChannel: options.requireChallengeChannel } : {}),\n ...(options?.shouldIntercept ? { shouldIntercept: options.shouldIntercept } : {}),\n ...(options?.replay === undefined ? {} : { replay: options.replay }),\n ...(options?.onCancel ? { onCancel: options.onCancel } : {}),\n ...(deps.authorizationSwap ? { authorizationSwap: deps.authorizationSwap } : {}),\n ...(options?.onUpgradeDiagnostic ? { onUpgradeDiagnostic: options.onUpgradeDiagnostic } : {}),\n ...(deps.readObtainedBar ? { readObtainedBar: deps.readObtainedBar } : {}),\n };\n };\n\n let teardown: (() => void) | undefined;\n\n const enable = (options: UpgradeEnableOptions): (() => void) => {\n // Calling enable() twice replaces the previous installation rather than\n // stacking a second prompt handler and a second global patch.\n teardown?.();\n\n installation = buildInstallation(options, 0);\n\n const disposers: Array<() => void> = [];\n\n if (deps.attachInternalSeam) {\n const attach = deps.attachInternalSeam;\n // `interceptorOptions` is handed over as a GETTER, not invoked here: the\n // handler resolves it per challenge, so a client attached before this\n // `enable()` — or before a later, narrower one — sees the live scope.\n attach((reissue) => createAxiosUpgradeHandler(interceptorOptions, reissue));\n disposers.push(() => attach(undefined));\n }\n\n if (options.interceptFetch) {\n // The registry, never a bare assignment: two features that each capture\n // and restore `globalThis.fetch` lose one interceptor non-deterministically.\n disposers.push(\n registerFetchMiddleware((next, input, init) => upgradeFetch(next, interceptorOptions)(input, init), {\n priority: FETCH_PRIORITY_UPGRADE,\n name: 'passflow-upgrade',\n }),\n );\n }\n\n if (deps.onSessionChange) {\n disposers.push(\n deps.onSessionChange(() => {\n const current = installation;\n if (!current) {\n return;\n }\n // Discard, do not reset: `inFlight` is private and has no reset, and\n // replacing the instance is both the cleanest and the only\n // non-invasive answer. The abandoned flight can no longer release a\n // waiter into the new session — `elevateFor` checks the generation.\n installation = buildInstallation(current.options, current.generation + 1);\n }),\n );\n }\n\n let torn = false;\n teardown = () => {\n if (torn) {\n return;\n }\n torn = true;\n for (const dispose of disposers) {\n dispose();\n }\n installation = undefined;\n teardown = undefined;\n };\n\n return teardown;\n };\n\n /**\n * The imperative seam: `passflow.upgrade(options?)`.\n *\n * There is no request to replay, so there is no challenge on the wire to\n * parse — the \"challenge\" is the bar the CALLER stated, synthesised here into\n * the one shape the controller ranks factors against. An options-less call\n * synthesises no bar at all, which is what makes it unable to produce\n * `insufficient`.\n */\n const upgradeImperatively = async (options?: UpgradeOptions): Promise<UpgradeResult> => {\n const level = options?.acrValues === undefined ? undefined : readAcrValues(options.acrValues).level;\n const challenge: UpgradeChallenge = {\n status: 401,\n ...(options?.acrValues === undefined ? {} : { acrValues: options.acrValues }),\n ...(level === undefined ? {} : { requiredLevel: level }),\n ...(options?.maxAge === undefined ? {} : { maxAge: options.maxAge }),\n };\n\n const stated = options?.acrValues !== undefined || options?.maxAge !== undefined;\n const requested: AssuranceBar = {\n ...(options?.acrValues === undefined ? {} : { acrValues: options.acrValues }),\n ...(level === undefined ? {} : { level }),\n ...(options?.maxAge === undefined ? {} : { maxAge: options.maxAge }),\n };\n\n const elevation = await elevateFor(challenge, { kind: 'imperative', ...(stated ? { requested } : {}) });\n const acr = elevation.obtained.acrValues;\n return { status: 'elevated', ...(acr === undefined ? {} : { acr }) };\n };\n\n // A callable object, not a plain record: §13.4's documented surface is\n // `passflow.upgrade(options?)` AND `passflow.upgrade.enable({...})`, and both\n // have to be the same thing a consumer holds. `enabled` is a getter, so the\n // members are defined rather than assigned.\n const facade = upgradeImperatively as UpgradeFacade;\n\n return Object.defineProperties(facade, {\n enable: { value: enable, enumerable: true },\n enabled: {\n enumerable: true,\n get(): boolean {\n return installation !== undefined && deps.canCommit().ok;\n },\n },\n elevate: {\n enumerable: true,\n value: (challenge: UpgradeChallenge, waiter?: Waiter) => elevateFor(challenge, waiter ?? { kind: 'imperative' }),\n },\n withAuth: {\n enumerable: true,\n value: (next?: FetchLike): FetchLike => {\n if (next) {\n // The developer's own function, wrapped as given: binding it would\n // change its `this` and break a method-shaped fetch.\n return upgradeFetch(next, interceptorOptions);\n }\n // Resolved inside the call, never at module or construction scope:\n // reading a global while a module is being imported is the SSR failure.\n const global = typeof globalThis === 'undefined' ? undefined : globalThis;\n if (!global || typeof global.fetch !== 'function') {\n throw new Error('Passflow: withAuth() needs a fetch to wrap — this runtime has none.');\n }\n return upgradeFetch(global.fetch.bind(global) as FetchLike, interceptorOptions);\n },\n },\n attachAuth: {\n enumerable: true,\n value: (instance: ResponseInterceptorHost): (() => void) => attachUpgradeAuth(instance, interceptorOptions),\n },\n });\n};\n","import type { AxiosInstance } from 'axios';\nimport {\n AppAPI,\n type AppSettings,\n AuthAPI,\n InvitationAPI,\n type InvitationsPaginatedList,\n type InviteLinkResponse,\n type PassflowAuthorizationResponse,\n type PassflowConfig,\n PassflowError,\n PassflowFederatedAuthPayload,\n type PassflowPasskeyAuthenticateStartPayload,\n type PassflowPasskeyRegisterStartPayload,\n type PassflowPasskeySettings,\n type PassflowPasswordPolicySettings,\n type PassflowPasswordlessResponse,\n type PassflowPasswordlessSignInCompletePayload,\n type PassflowPasswordlessSignInPayload,\n type PassflowSendPasswordResetEmailPayload,\n type PassflowSettingsAll,\n type PassflowSignInPayload,\n type PassflowSignUpPayload,\n type PassflowSuccessResponse,\n type PassflowTenantResponse,\n type PassflowValidationResponse,\n type RequestInviteLinkPayload,\n SettingAPI,\n TenantAPI,\n TwoFactorApiClient,\n type TwoFactorCompletionOptions,\n type TwoFactorConfirmResponse,\n type TwoFactorDisableResponse,\n type TwoFactorRecoveryResponse,\n type TwoFactorRegenerateResponse,\n type TwoFactorSetupResponse,\n type TwoFactorStatusResponse,\n type TwoFactorTrustedDevice,\n type TwoFactorVerifyResponse,\n UpgradeAPI,\n type UpgradeDomain,\n UserAPI,\n resolveUpgradeDomain,\n} from './api';\n\nimport type { AxiosClient, AxiosUpgradeHandler } from './api/axios-client';\nimport { DEFAULT_SCOPES, PASSFLOW_CLOUD_URL, SDK_VERSION } from './constants';\nimport { DeviceService } from './device';\nimport type { CurrentUrlInfo, PlatformAdapter } from './platform';\nimport { webAdapter } from './platform';\nimport { AuthService, InvitationService, TenantService, TokenCacheService, TwoFactorService, UserService } from './services';\nimport { StorageManager } from './storage';\nimport { type ErrorPayload, PassflowEvent, PassflowStore, type PassflowSubscriber } from './store';\nimport { TokenType, parseToken } from './token';\nimport {\n type AssuranceBar,\n type CreateClientConfig,\n type FetchLike,\n type ResponseInterceptorHost,\n type UpgradeFacade,\n createUpgradeClient,\n createUpgradeFacade,\n readAcrValues,\n} from './upgrade';\n\nimport type { ParsedTokens, SessionParams, Tokens } from './types';\nimport { isValidJWTFormat, sanitizeErrorMessage } from './utils/validation';\n\nexport class Passflow {\n /**\n * SDK version string.\n * Useful for debugging and reporting version-specific issues.\n * @example\n * ```typescript\n * console.log('Using Passflow SDK version:', Passflow.version);\n * ```\n */\n static readonly version: string = SDK_VERSION;\n\n // API clients\n private authApi: AuthAPI;\n private appApi: AppAPI;\n private userApi: UserAPI;\n private settingApi: SettingAPI;\n private tenantApi: TenantAPI;\n private invitationApi: InvitationAPI;\n private twoFactorApi: TwoFactorApiClient;\n private upgradeApi: UpgradeAPI;\n\n // Configuration\n private scopes: string[];\n private createTenantForNewUser: boolean;\n private doRefreshTokens = false;\n\n // Services\n private deviceService: DeviceService;\n private storageManager: StorageManager;\n private subscribeStore: PassflowStore;\n private authService: AuthService;\n private userService: UserService;\n private tenantService: TenantService;\n private invitationService: InvitationService;\n private tokenCacheService: TokenCacheService;\n private twoFactorService: TwoFactorService;\n\n // Public services\n public tenant: TenantService;\n public twoFactor: TwoFactorService;\n\n /**\n * Step-up (assurance upgrade) wiring. Always present, inert until\n * `enable({ prompt })` is called — constructing a `Passflow` must never patch\n * a global or install a prompt.\n */\n readonly upgrade: UpgradeFacade;\n\n /**\n * Passflow's own API as an interception scope (\"domain 1\"), resolved ONCE\n * here from `PassflowConfig.url` so no adapter or wrapper ever derives it.\n * `null` when no `url` was configured, which is empty — never a throw, and\n * never silent: the first parseable challenge then names `url` as the fix.\n */\n private readonly upgradeDomain: UpgradeDomain | null;\n\n // Session callbacks\n private createSessionCallback?: ({ tokens, parsedTokens }: { tokens?: Tokens; parsedTokens?: ParsedTokens }) => Promise<void>;\n private expiredSessionCallback?: () => Promise<void>;\n\n // The app's PKCE setting, read once per appId\n private pkceEnabledRead?: Promise<boolean>;\n\n // State\n error?: Error;\n private platform: PlatformAdapter;\n get origin(): string {\n return this.platform.getCurrentUrl()?.origin ?? '';\n }\n url: string;\n appId?: string;\n\n constructor(config: PassflowConfig) {\n const { url, appId, scopes } = config;\n this.url = url || PASSFLOW_CLOUD_URL;\n this.appId = appId;\n this.platform = config.platform ?? webAdapter;\n\n // Initialize single StorageManager instance\n this.storageManager = new StorageManager({\n prefix: config.keyStoragePrefix ?? '',\n storage: this.platform.storage,\n });\n\n // Initialize single DeviceService instance with shared StorageManager\n this.deviceService = new DeviceService(this.storageManager);\n\n // Initialize API clients with shared instances\n this.authApi = new AuthAPI(config, this.storageManager, this.deviceService);\n this.appApi = new AppAPI(config, this.storageManager, this.deviceService);\n this.userApi = new UserAPI(config, this.storageManager, this.deviceService);\n this.settingApi = new SettingAPI(config, this.storageManager, this.deviceService);\n this.tenantApi = new TenantAPI(config, this.storageManager, this.deviceService);\n this.invitationApi = new InvitationAPI(config, this.storageManager, this.deviceService);\n this.twoFactorApi = new TwoFactorApiClient(config, this.storageManager, this.deviceService);\n this.upgradeApi = new UpgradeAPI(config, this.storageManager, this.deviceService);\n this.upgradeDomain = resolveUpgradeDomain(config.url);\n\n // Initialize PassflowStore\n this.subscribeStore = new PassflowStore();\n\n this.tokenCacheService = new TokenCacheService(this.storageManager, this.authApi, this.subscribeStore, this.platform);\n\n this.scopes = scopes ?? DEFAULT_SCOPES;\n this.createTenantForNewUser = config.createTenantForNewUser ?? false;\n\n // Initialize domain services with dependencies\n this.authService = new AuthService(\n this.authApi,\n this.deviceService,\n this.storageManager,\n this.subscribeStore,\n this.tokenCacheService,\n this.scopes,\n this.createTenantForNewUser,\n this.url,\n {\n createSession: this.createSessionCallback,\n expiredSession: this.expiredSessionCallback,\n },\n this.appId ?? '',\n config.tokenExchange,\n this.platform,\n () => this.readPkceEnabled(),\n );\n\n this.userService = new UserService(this.userApi, this.deviceService, this.platform);\n\n this.tenantService = new TenantService(this.tenantApi, this.scopes);\n this.tenant = this.tenantService;\n\n this.invitationService = new InvitationService(this.invitationApi);\n\n this.twoFactorService = new TwoFactorService(this.twoFactorApi, this.subscribeStore, this.authService);\n this.twoFactor = this.twoFactorService;\n\n this.upgrade = createUpgradeFacade({\n // The prepare round. The commit round is `/auth/refresh`, which must go\n // through AuthService: it is the only path that STORES the rotated tokens,\n // so posting directly would elevate the session on the server and leave\n // the client holding stale ones.\n startUpgrade: (payload) => this.upgradeApi.startUpgrade(payload),\n commitRefresh: async (upgrade, pin) => {\n await this.authService.refreshToken(upgrade, pin);\n },\n // The identity the flight is pinned to, read once when it starts. The\n // check itself lives inside `refreshToken`, immediately before the\n // rotated tokens are applied — see `AuthService.assertSessionPin`.\n readSessionPin: () => this.authService.readSessionPin(),\n canCommit: () =>\n this.authService['tokenDeliveryManager'].isBFFMode()\n ? { ok: false, reason: 'Step-up verification cannot be committed through a BFF refresh endpoint' }\n : { ok: true },\n markSessionInvalid: () => this.authService['tokenDeliveryManager'].setSessionInvalid(),\n platform: this.platform,\n passflowDomain: this.upgradeDomain,\n ...(config.upgrade?.factors ? { factors: config.upgrade.factors } : {}),\n ...(config.upgrade?.relyingPartyId ? { relyingPartyId: () => config.upgrade?.relyingPartyId } : {}),\n readObtainedBar: () => this.readAssuranceBar(),\n // §5.5's credential swap. Called twice by the core — before the elevation\n // and after it — so returning the CURRENT token both times is what makes\n // `{from: stale, to: fresh}` come out right, and exact-match on `from` is\n // what proves the outgoing header was ours.\n authorizationSwap: () => {\n const token = this.storageManager.getToken(TokenType.access_token);\n return token ? { from: `Bearer ${token}`, to: `Bearer ${token}` } : undefined;\n },\n attachInternalSeam: (factory) => this.attachUpgradeSeam(factory),\n onSessionChange: (listener) => this.subscribeToSessionChange(listener),\n });\n\n // Check for tokens in query params if configured\n if (config.parseQueryParams) {\n this.checkAndSetTokens();\n }\n\n this.setTokensToCacheFromLocalStorage();\n }\n\n /**\n * Whether the current app wants PKCE. A sign-in asks before a passkey ceremony,\n * where a round-trip eats into the browser's user-gesture window, so the answer\n * is kept for the appId. A failed read is not kept: the next sign-in asks again.\n */\n private readPkceEnabled(): Promise<boolean> {\n if (!this.appId) {\n return Promise.resolve(false);\n }\n if (!this.pkceEnabledRead) {\n const read: Promise<boolean> = this.appApi.getAppSettings().then((settings) => settings.pkce_enabled);\n this.pkceEnabledRead = read;\n read.catch(() => {\n if (this.pkceEnabledRead === read) {\n this.pkceEnabledRead = undefined;\n }\n });\n }\n return this.pkceEnabledRead;\n }\n\n /**\n * Update the appId and propagate it to all API clients.\n * This ensures that all future API requests use the new appId in their headers.\n *\n * @param appId - The new application ID to set\n *\n * @example\n * ```typescript\n * // Update appId after discovery\n * passflow.setAppId('discovered-app-id-123');\n * ```\n */\n setAppId(appId: string): void {\n this.appId = appId;\n this.pkceEnabledRead = undefined;\n\n // Update all API clients\n this.authApi.setAppId(appId);\n this.appApi.setAppId(appId);\n this.userApi.setAppId(appId);\n this.settingApi.setAppId(appId);\n this.tenantApi.setAppId(appId);\n this.invitationApi.setAppId(appId);\n this.twoFactorApi.setAppId(appId);\n this.upgradeApi.setAppId(appId);\n\n // Update authService appId if needed\n if (this.authService) {\n // AuthService stores appId internally but doesn't have a setter\n // The API clients will handle the headers, so this is sufficient\n }\n }\n\n /**\n * Wraps a `fetch`, returning a new function with step-up handling attached.\n *\n * Installs nothing and touches no global — this is the SSR- and\n * React-Native-safe surface. Two verbs exist because the semantics genuinely\n * differ: an axios interceptor mutates, a fetch wrapper returns, and one name\n * would hide which happened.\n *\n * A challenge from Passflow's own API is handled with no further\n * configuration; a challenge from the developer's own backend needs that\n * backend declared via `upgrade.enable({ backend })`.\n *\n * @example\n * ```typescript\n * const authedFetch = passflow.withAuth(fetch);\n * const res = await authedFetch('https://api.example.com/transfer', { method: 'POST' });\n * if (!res.ok && isUpgradeCancelled(res)) {\n * // the user declined the step-up — not an authorization failure\n * }\n * ```\n */\n withAuth(next?: FetchLike): FetchLike {\n return this.upgrade.withAuth(next);\n }\n\n /**\n * Attaches step-up handling to an axios instance the caller already owns,\n * **mutating** it, and returns a `detach()` disposer.\n *\n * The parameter is typed structurally — duck-typed on\n * `interceptors.response.use` — so this package's axios version is not a\n * constraint on the caller's. It returns the disposer rather than the\n * instance on purpose: it mutates, so returning the instance would read as if\n * it had built something.\n *\n * @example\n * ```typescript\n * const api = axios.create({ baseURL: 'https://api.example.com' });\n * const detach = passflow.attachAuth(api);\n * ```\n */\n attachAuth(instance: ResponseInterceptorHost): () => void {\n return this.upgrade.attachAuth(instance);\n }\n\n /**\n * Builds a ready axios instance with step-up handling already wired, for a\n * caller with no HTTP client of their own.\n *\n * Pure passthrough of axios' own create options, so `baseURL`, `headers`,\n * `timeout` and `adapter` stay the caller's. A `baseURL` is NOT read as a\n * declared backend — that would derive an interception scope through a side\n * door; declare it with `upgrade.enable({ backend })`.\n *\n * @example\n * ```typescript\n * const api = passflow.createClient({ baseURL: 'https://api.example.com' });\n * ```\n */\n createClient(config?: CreateClientConfig): AxiosInstance {\n return createUpgradeClient(config, (instance) => this.upgrade.attachAuth(instance));\n }\n\n /**\n * The assurance bar the session currently holds, read from the `acr` claim of\n * the stored access token.\n *\n * The server scores `acr` and is the only authority; this is the client-side\n * read of what it last said, and it is what lets an `insufficient` outcome\n * report a real `obtained` bar instead of the bar the ceremony was ranked for.\n */\n private readAssuranceBar(): AssuranceBar | undefined {\n const token = this.storageManager.getToken(TokenType.access_token);\n if (!token) {\n return undefined;\n }\n try {\n const claims = parseToken(token) as { acr?: unknown; auth_time?: unknown };\n const acrValues = typeof claims.acr === 'string' ? claims.acr : undefined;\n if (!acrValues) {\n return undefined;\n }\n const level = readAcrValues(acrValues).level;\n return level === undefined ? { acrValues } : { acrValues, level };\n } catch {\n // An unparseable token tells us nothing about the bar, and guessing one\n // would be worse than reporting the ranked approximation.\n return undefined;\n }\n }\n\n /** Every axios client this instance owns, which is every surface a Passflow 401 can arrive on. */\n private axiosClients(): AxiosClient[] {\n return [\n this.authApi['axiosClient'],\n this.appApi['axiosClient'],\n this.userApi['axiosClient'],\n this.settingApi['axiosClient'],\n this.tenantApi['axiosClient'],\n this.invitationApi['axiosClient'],\n this.twoFactorApi['axiosClient'],\n this.upgradeApi['axiosClient'],\n ];\n }\n\n /**\n * Installs (or clears) §4.4's internal seam on every one of the SDK's own\n * clients.\n *\n * Each client gets a handler bound to its OWN `replay`, so a replayed request\n * goes back out through the instance that issued it. The seam is consulted\n * inside the existing error interceptor, which is where it has to live: axios\n * runs response interceptors FIFO and that handler converts the `AxiosError`\n * into a `PassflowError` with no headers and no config, so anything\n * registered afterwards is structurally too late.\n */\n private attachUpgradeSeam(\n factory: ((reissue: (config: unknown) => Promise<unknown>) => (error: unknown) => Promise<unknown>) | undefined,\n ): void {\n for (const client of this.axiosClients()) {\n if (!factory) {\n client.setUpgradeInterceptor(undefined);\n continue;\n }\n const handler = factory((config) => client.replay(config as Parameters<AxiosClient['replay']>[0]));\n client.setUpgradeInterceptor(handler as unknown as AxiosUpgradeHandler);\n }\n }\n\n /**\n * Notifies on a session change, which the facade answers by discarding its\n * controller.\n *\n * `SignIn` and `SignOut` both count: a challenge is server-bound to the\n * session's `sid`, so an elevation abandoned across either boundary must never\n * elevate the NEW session.\n */\n private subscribeToSessionChange(listener: () => void): () => void {\n const subscriber: PassflowSubscriber = {\n onAuthChange: () => listener(),\n };\n this.subscribeStore.subscribe(subscriber, [PassflowEvent.SignIn, PassflowEvent.SignOut]);\n return () => this.subscribeStore.unsubscribe(subscriber);\n }\n\n // Session management\n /**\n * Configure session callbacks and check current session status.\n *\n * **WARNING**: Calling this method multiple times will overwrite previously registered callbacks.\n * Only the most recent `createSession` and `expiredSession` callbacks will be active.\n *\n * @param params - Session configuration parameters\n * @param params.createSession - Callback invoked when a valid session exists or is created\n * @param params.expiredSession - Callback invoked when no valid session exists\n * @param params.doRefresh - Whether to automatically refresh expired tokens (default: false)\n * @returns Promise that resolves when session check is complete\n *\n * @example\n * ```typescript\n * await passflow.session({\n * createSession: async ({ tokens, parsedTokens }) => {\n * console.log('User is authenticated', parsedTokens?.access_token?.sub);\n * // Initialize user session in your app\n * },\n * expiredSession: async () => {\n * console.log('User session expired');\n * // Redirect to login or show login modal\n * },\n * doRefresh: true // Automatically refresh tokens if expired\n * });\n * ```\n */\n session: ({ createSession, expiredSession, doRefresh }: SessionParams) => Promise<void> = async ({\n createSession,\n expiredSession,\n doRefresh = false,\n }) => {\n this.createSessionCallback = createSession;\n this.expiredSessionCallback = expiredSession;\n this.doRefreshTokens = doRefresh;\n\n await this.submitSessionCheck();\n };\n\n private async submitSessionCheck() {\n let tokens;\n let parsedTokens;\n try {\n tokens = await this.authService.getTokens(this.doRefreshTokens);\n parsedTokens = this.tokenCacheService.getParsedTokens();\n } catch (error) {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error || error instanceof PassflowError ? error.message : 'Session check failed',\n originalError: error,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n tokens = undefined;\n }\n\n if (tokens && this.createSessionCallback) {\n await this.createSessionCallback({ tokens, parsedTokens });\n }\n\n if (!tokens && this.expiredSessionCallback) {\n await this.expiredSessionCallback();\n }\n }\n\n // Event subscription\n /**\n * Subscribe to Passflow authentication events.\n *\n * @param subscriber - Subscriber function that receives event type and payload\n * @param events - Optional array of specific events to listen for. If omitted, subscribes to all events.\n *\n * @example\n * ```typescript\n * // Subscribe to all events\n * passflow.subscribe((event, payload) => {\n * console.log('Event:', event, payload);\n * });\n *\n * // Subscribe to specific events only\n * passflow.subscribe(\n * (event, payload) => {\n * if (event === PassflowEvent.SignIn) {\n * console.log('User signed in', payload.tokens);\n * }\n * },\n * [PassflowEvent.SignIn, PassflowEvent.SignOut]\n * );\n * ```\n */\n subscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]) {\n this.subscribeStore.subscribe(subscriber, events);\n\n // Initialize token cache service and token event listener\n this.tokenCacheService.initialize();\n }\n\n /**\n * Unsubscribe from Passflow authentication events.\n *\n * @param subscriber - The subscriber function to remove\n * @param events - Optional array of specific events to unsubscribe from. If omitted, unsubscribes from all events.\n *\n * @example\n * ```typescript\n * const subscriber = (event, payload) => console.log(event, payload);\n *\n * // Subscribe\n * passflow.subscribe(subscriber);\n *\n * // Later, unsubscribe\n * passflow.unsubscribe(subscriber);\n * ```\n */\n unsubscribe(subscriber: PassflowSubscriber, events?: PassflowEvent[]) {\n this.subscribeStore.unsubscribe(subscriber, events);\n }\n\n // Token handling\n /**\n * Handle OAuth redirect callback and extract tokens from URL query parameters.\n * This method should be called on the redirect page after authentication.\n *\n * @returns Tokens object if found in URL, undefined otherwise\n *\n * @example\n * ```typescript\n * // On your redirect page (e.g., /auth/callback)\n * const tokens = passflow.handleTokensRedirect();\n * if (tokens) {\n * console.log('Authentication successful', tokens.access_token);\n * // Tokens are automatically saved to storage\n * }\n * ```\n */\n handleTokensRedirect(): Tokens | undefined {\n return this.checkAndSetTokens();\n }\n\n private checkAndSetTokens(): Tokens | undefined {\n // Cache the URL once to avoid redundant calls (especially for async/expensive adapters)\n const currentUrl = this.platform.getCurrentUrl();\n\n // Check query params first, then fall back to hash\n // React SDK may put tokens in hash (more secure), while some OAuth flows use query params\n let urlParams = new URLSearchParams(currentUrl?.search ?? '');\n let fromHash = false;\n\n // If no access_token in query params, check hash\n if (!urlParams.get('access_token') && currentUrl?.hash) {\n // Hash format: #access_token=xxx&refresh_token=yyy\n const hashParams = new URLSearchParams(currentUrl.hash.substring(1));\n if (hashParams.get('access_token')) {\n urlParams = hashParams;\n fromHash = true;\n }\n }\n\n // A sign-in that owes its second factor lands with `#tfa_token=` and no\n // tokens (a federated GET callback on a non-PKCE app).\n if (!urlParams.get('access_token') && this.adoptTwoFactorTokenFromUrl(currentUrl)) {\n this.error = undefined;\n return undefined;\n }\n\n const access_token = urlParams.get('access_token');\n const refresh_token = urlParams.get('refresh_token');\n const id_token = urlParams.get('id_token');\n const scopes: string[] = urlParams.get('scopes')?.split(',') ?? this.scopes;\n let tokens: Tokens | undefined = undefined;\n\n if (access_token) {\n // Validate JWT format for access_token\n if (!isValidJWTFormat(access_token)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid access token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.cleanupUrlParams(fromHash, currentUrl);\n return undefined;\n }\n\n // Validate optional refresh_token if present\n if (refresh_token && !isValidJWTFormat(refresh_token)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid refresh token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.cleanupUrlParams(fromHash, currentUrl);\n return undefined;\n }\n\n // Validate optional id_token if present\n if (id_token && !isValidJWTFormat(id_token)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid ID token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n this.cleanupUrlParams(fromHash, currentUrl);\n return undefined;\n }\n\n tokens = {\n access_token,\n refresh_token: refresh_token ?? undefined,\n id_token: id_token ?? undefined,\n scopes,\n };\n\n // URL tokens mean we're in JSON mode, not cookie/BFF mode\n // Clear any stored delivery mode to ensure consistent behavior\n this.storageManager.clearDeliveryMode();\n this.storageManager.saveTokens(tokens);\n this.tokenCacheService.setTokensCache(tokens);\n\n this.subscribeStore.notify(PassflowEvent.SignIn, { tokens, parsedTokens: this.getParsedTokens() });\n this.submitSessionCheck();\n this.cleanupUrlParams(fromHash, currentUrl);\n this.error = undefined;\n return tokens;\n } else {\n this.error = this.checkErrorsFromURL();\n }\n return undefined;\n }\n\n /**\n * Adopts a `#tfa_token=` fragment: the 2FA token goes to the TwoFactorService\n * through `TwoFactorRequired`, exactly as a password sign-in's does, and the\n * fragment is stripped from the URL. Nothing is saved as a session.\n *\n * @returns true when the URL carried a 2FA token\n */\n private adoptTwoFactorTokenFromUrl(currentUrl: CurrentUrlInfo | null): boolean {\n if (!currentUrl?.hash) return false;\n const hashParams = new URLSearchParams(currentUrl.hash.substring(1));\n const tfaToken = hashParams.get('tfa_token');\n if (!tfaToken) return false;\n\n hashParams.delete('tfa_token');\n const rest = hashParams.toString();\n this.platform.replaceUrl(currentUrl.pathname + currentUrl.search + (rest ? `#${rest}` : ''));\n\n if (!isValidJWTFormat(tfaToken)) {\n const errorPayload: ErrorPayload = {\n message: 'Invalid 2FA token format received',\n code: 'INVALID_TOKEN_FORMAT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return true;\n }\n\n this.subscribeStore.notify(PassflowEvent.TwoFactorRequired, { email: '', challengeId: '', tfaToken });\n return true;\n }\n\n private checkErrorsFromURL(): Error | undefined {\n const urlParams = new URLSearchParams(this.platform.getCurrentUrl()?.search ?? '');\n const error = urlParams.get('error');\n if (error) {\n // Sanitize error message to prevent XSS\n const sanitized = sanitizeErrorMessage(error);\n return new Error(sanitized);\n }\n return undefined;\n }\n\n private cleanupUrlParams(fromHash = false, currentUrl: CurrentUrlInfo | null = null): void {\n const url = currentUrl ?? this.platform.getCurrentUrl();\n if (!url) return; // no URL context (SSR or unsupported environment)\n\n if (fromHash) {\n // Clear hash completely - tokens should be removed from URL for security\n this.platform.replaceUrl(url.pathname + url.search);\n } else {\n const urlParams = new URLSearchParams(url.search);\n\n // Remove sensitive token parameters\n urlParams.delete('access_token');\n urlParams.delete('refresh_token');\n urlParams.delete('id_token');\n urlParams.delete('client_challenge');\n\n // Use replaceState to fully clear from browser history\n if (urlParams.size > 0) {\n this.platform.replaceUrl(`${url.pathname}?${urlParams.toString()}`);\n } else {\n this.platform.replaceUrl(url.pathname);\n }\n }\n }\n\n private setTokensToCacheFromLocalStorage(): void {\n let tokens = this.storageManager.getTokens();\n\n // If tokens don't have access_token, check for inconsistent state\n // This can happen if delivery_mode is set to BFF but tokens were actually saved in JSON mode\n if (!tokens?.access_token) {\n const deliveryMode = this.storageManager.getDeliveryMode();\n if (deliveryMode) {\n // Delivery mode is set (cookie/BFF mode)\n // Check if this is legitimate cookie mode or stale state\n\n if (tokens?.id_token && this.storageManager.hasCookieModeIdToken()) {\n // Legitimate cookie/BFF mode: we have id_token from cookie mode storage\n // Access token is in HttpOnly cookies, not localStorage - this is valid\n this.tokenCacheService.setTokensCache(tokens);\n return;\n }\n\n // Check if there are JSON mode tokens that should be used instead\n if (this.storageManager.hasJsonModeTokens()) {\n // Stale state: delivery_mode is set but JSON tokens exist\n // Clear delivery mode and use JSON tokens\n this.storageManager.clearDeliveryMode();\n tokens = this.storageManager.getTokens();\n } else {\n // No valid tokens in either mode - clean up completely\n this.storageManager.deleteTokens();\n return;\n }\n }\n }\n\n if (tokens) {\n this.tokenCacheService.setTokensCache(tokens);\n }\n }\n\n /**\n * Get cached tokens from memory without triggering a refresh.\n *\n * @returns Cached tokens or undefined if not cached\n *\n * @example\n * ```typescript\n * const tokens = passflow.getCachedTokens();\n * if (tokens) {\n * console.log('Access token:', tokens.access_token);\n * }\n * ```\n */\n getCachedTokens() {\n return this.tokenCacheService.getTokens();\n }\n\n /**\n * Get cached tokens from memory and automatically refresh if expired.\n *\n * @returns Promise resolving to tokens or undefined\n *\n * @example\n * ```typescript\n * const tokens = await passflow.getTokensWithRefresh();\n * // Tokens are guaranteed to be valid or undefined\n * ```\n */\n getTokensWithRefresh() {\n return this.tokenCacheService.getTokensWithRefresh();\n }\n\n /**\n * Get parsed JWT tokens with decoded claims.\n *\n * @returns Parsed token objects with decoded payloads\n *\n * @example\n * ```typescript\n * const parsed = passflow.getParsedTokens();\n * if (parsed?.access_token) {\n * console.log('User ID:', parsed.access_token.sub);\n * console.log('Expires at:', new Date(parsed.access_token.exp * 1000));\n * }\n * ```\n */\n getParsedTokens() {\n return this.tokenCacheService.getParsedTokens();\n }\n\n /**\n * Check if the cached tokens are expired.\n *\n * @returns True if tokens are expired, false otherwise\n *\n * @example\n * ```typescript\n * if (passflow.areTokensExpired()) {\n * await passflow.refreshToken();\n * }\n * ```\n */\n areTokensExpired() {\n return this.tokenCacheService.isExpired();\n }\n\n // Auth delegation methods\n /**\n * Check if the user is currently authenticated with valid tokens.\n *\n * @returns True if user has valid, non-expired tokens\n *\n * @example\n * ```typescript\n * if (passflow.isAuthenticated()) {\n * console.log('User is logged in');\n * } else {\n * console.log('User needs to sign in');\n * }\n * ```\n */\n isAuthenticated(): boolean {\n const tokens = this.storageManager.getTokens();\n if (!tokens || !tokens.access_token) return false;\n\n // Use cached parsed tokens instead of re-parsing\n const parsedTokens = this.tokenCacheService.getParsedTokens();\n if (!parsedTokens) return false;\n\n return this.authService.isAuthenticated(parsedTokens);\n }\n\n /**\n * Sign in a user with email/username and password.\n *\n * @param payload - Sign-in credentials and options\n * @param payload.email - User's email or username\n * @param payload.password - User's password\n * @param payload.scopes - Optional scopes to request (defaults to SDK scopes)\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If authentication fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.signIn({\n * email: 'user@example.com',\n * password: 'secure-password'\n * });\n * console.log('Signed in successfully', response.access_token);\n * } catch (error) {\n * console.error('Sign in failed', error.message);\n * }\n * ```\n */\n async signIn(payload: PassflowSignInPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.signIn(payload);\n\n return response;\n }\n\n /**\n * Register a new user account with email and password.\n *\n * @param payload - Registration details\n * @param payload.email - User's email address\n * @param payload.password - User's password\n * @param payload.username - Optional username\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If registration fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.signUp({\n * email: 'newuser@example.com',\n * password: 'secure-password',\n * username: 'newuser'\n * });\n * console.log('Account created', response.access_token);\n * } catch (error) {\n * console.error('Sign up failed', error.message);\n * }\n * ```\n */\n async signUp(payload: PassflowSignUpPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.signUp(payload);\n\n return response;\n }\n\n /**\n * Initiate passwordless authentication by sending a magic link or OTP.\n *\n * @param payload - Passwordless sign-in configuration\n * @param payload.email - User's email address\n * @param payload.method - Delivery method ('email' or 'sms')\n * @returns Promise with response indicating if the code was sent\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * // Send magic link via email\n * const response = await passflow.passwordlessSignIn({\n * email: 'user@example.com',\n * method: 'email'\n * });\n * console.log('Magic link sent:', response.success);\n * ```\n */\n passwordlessSignIn(payload: PassflowPasswordlessSignInPayload): Promise<PassflowPasswordlessResponse> {\n return this.authService.passwordlessSignIn(payload);\n }\n\n /**\n * Complete passwordless authentication by verifying the OTP or token.\n *\n * @param payload - Verification payload\n * @param payload.email - User's email address\n * @param payload.code - Verification code from email/SMS\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with validation response containing tokens\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.passwordlessSignInComplete({\n * email: 'user@example.com',\n * code: '123456'\n * });\n * console.log('Passwordless sign-in complete', response.access_token);\n * } catch (error) {\n * console.error('Invalid code', error.message);\n * }\n * ```\n */\n async passwordlessSignInComplete(payload: PassflowPasswordlessSignInCompletePayload): Promise<PassflowValidationResponse> {\n const response = await this.authService.passwordlessSignInComplete(payload);\n\n return response;\n }\n\n /**\n * Centralized error handler for all public methods.\n * Creates proper ErrorPayload, distinguishes PassflowError from generic Error,\n * notifies error event, and re-throws the error.\n *\n * @param error - The error to handle\n * @param context - Context description for the error\n * @throws The original error after handling\n */\n private handleError(error: unknown, context: string): never {\n const errorPayload: ErrorPayload = {\n message: error instanceof Error ? error.message : `${context} failed`,\n originalError: error,\n code: error instanceof PassflowError ? error.id : undefined,\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw error;\n }\n\n /**\n * Sign out the current user and clear all tokens.\n *\n * @returns Promise that resolves when sign-out is complete\n * @throws {PassflowError} If sign-out request fails\n *\n * @example\n * ```typescript\n * try {\n * await passflow.logOut();\n * console.log('User signed out successfully');\n * // Redirect to login page\n * } catch (error) {\n * console.error('Sign out failed', error.message);\n * }\n * ```\n */\n async logOut(): Promise<void> {\n try {\n // Only clear tokens after successful logout API call\n await this.authService.logOut();\n this.storageManager.deleteTokens();\n this.tokenCacheService.setTokensCache(undefined);\n this.twoFactorService.clearPartialAuthState();\n await this.submitSessionCheck();\n this.subscribeStore.notify(PassflowEvent.SignOut, {});\n } catch (error) {\n this.handleError(error, 'Log out');\n }\n }\n\n /**\n * Initiate federated authentication (OAuth) with a popup window.\n *\n * @param payload - Federated authentication configuration\n * @param payload.provider - OAuth provider (e.g., 'google', 'github', 'microsoft')\n * @param payload.scopes - Optional scopes to request\n *\n * @example\n * ```typescript\n * // Sign in with Google using a popup\n * passflow.federatedAuthWithPopup({\n * provider: 'google'\n * });\n *\n * // Listen for the result via subscribe\n * passflow.subscribe((event, payload) => {\n * if (event === PassflowEvent.SignIn) {\n * console.log('OAuth sign-in successful', payload.tokens);\n * }\n * });\n * ```\n */\n federatedAuthWithPopup(payload: PassflowFederatedAuthPayload): void {\n this.authService.federatedAuthWithPopup(payload);\n }\n\n /**\n * Initiate federated authentication (OAuth) with a full-page redirect.\n *\n * @param payload - Federated authentication configuration\n * @param payload.provider - OAuth provider (e.g., 'google', 'github', 'microsoft')\n * @param payload.scopes - Optional scopes to request\n * @param payload.redirectUrl - URL to redirect to after authentication\n *\n * @example\n * ```typescript\n * // Sign in with GitHub using redirect\n * passflow.federatedAuthWithRedirect({\n * provider: 'github',\n * redirectUrl: window.location.origin + '/auth/callback'\n * });\n * ```\n */\n federatedAuthWithRedirect(payload: PassflowFederatedAuthPayload): void {\n this.authService.federatedAuthWithRedirect(payload);\n }\n\n /**\n * Reset the SDK state by clearing all tokens and optionally throwing an error.\n *\n * @param error - Optional error message to throw after reset\n * @throws {Error} If error message is provided\n *\n * @example\n * ```typescript\n * // Clear tokens without error\n * passflow.reset();\n *\n * // Clear tokens and throw error\n * try {\n * passflow.reset('Session expired');\n * } catch (error) {\n * console.error('Reset error:', error.message);\n * }\n * ```\n */\n reset(error?: string) {\n this.storageManager.deleteTokens();\n this.tokenCacheService.setTokensCache(undefined);\n this.subscribeStore.notify(PassflowEvent.SignOut, {});\n if (error) {\n this.error = new Error(error);\n const errorPayload: ErrorPayload = {\n message: error,\n code: 'RESET_ERROR',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n throw this.error;\n }\n }\n\n /**\n * Refresh the access token using the refresh token.\n *\n * @returns Promise with new authorization response containing refreshed tokens\n * @throws {Error} If no refresh token is found\n * @throws {PassflowError} If refresh request fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.refreshToken();\n * console.log('Token refreshed', response.access_token);\n * } catch (error) {\n * console.error('Failed to refresh token', error.message);\n * // Redirect to login\n * }\n * ```\n */\n async refreshToken(): Promise<PassflowAuthorizationResponse> {\n if (!this.tokenCacheService.parsedTokensCache?.refresh_token) {\n throw new Error('No refresh token found');\n }\n\n try {\n const response = await this.authService.refreshToken();\n\n return response;\n } catch (error) {\n if (error instanceof PassflowError) {\n throw error;\n } else {\n this.subscribeStore.notify(PassflowEvent.Error, {\n message: 'Failed to refresh token',\n originalError: error,\n });\n throw error;\n }\n }\n }\n\n /**\n * Send a password reset email to the user.\n *\n * @param payload - Password reset request\n * @param payload.email - User's email address\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * try {\n * await passflow.sendPasswordResetEmail({\n * email: 'user@example.com'\n * });\n * console.log('Password reset email sent');\n * } catch (error) {\n * console.error('Failed to send reset email', error.message);\n * }\n * ```\n */\n sendPasswordResetEmail(payload: PassflowSendPasswordResetEmailPayload): Promise<PassflowSuccessResponse> {\n return this.authService.sendPasswordResetEmail(payload);\n }\n\n /**\n * Reset password using a reset token (typically from URL after clicking email link).\n *\n * @param newPassword - The new password to set\n * @param scopes - Optional scopes to request after reset\n * @returns Promise with authorization response containing new tokens\n * @throws {PassflowError} If reset fails\n *\n * @example\n * ```typescript\n * // On password reset page (e.g., /reset-password?token=xyz)\n * try {\n * const response = await passflow.resetPassword('new-secure-password');\n * console.log('Password reset successful', response.access_token);\n * } catch (error) {\n * console.error('Password reset failed', error.message);\n * }\n * ```\n */\n async resetPassword(newPassword: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.resetPassword(newPassword, scopes);\n\n return response;\n }\n\n // App settings\n /**\n * Get application settings and configuration.\n *\n * @returns Promise with app settings including branding, features, and config\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const settings = await passflow.getAppSettings();\n * console.log('App name:', settings.name);\n * console.log('Passwordless enabled:', settings.passwordless_enabled);\n * ```\n */\n async getAppSettings(): Promise<AppSettings> {\n try {\n return await this.appApi.getAppSettings();\n } catch (error) {\n this.handleError(error, 'Get app settings');\n }\n }\n\n /**\n * Get all Passflow settings including password policy, passkey settings, etc.\n *\n * @returns Promise with all settings\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const settings = await passflow.getSettingsAll();\n * console.log('Password policy:', settings.password_policy);\n * console.log('Passkey settings:', settings.passkey);\n * ```\n */\n async getSettingsAll(): Promise<PassflowSettingsAll> {\n try {\n return await this.settingApi.getSettingsAll();\n } catch (error) {\n this.handleError(error, 'Get all settings');\n }\n }\n\n /**\n * Get password policy settings (min length, complexity requirements, etc.).\n *\n * @returns Promise with password policy configuration\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const policy = await passflow.getPasswordPolicySettings();\n * console.log('Min length:', policy.min_length);\n * console.log('Require uppercase:', policy.require_uppercase);\n * ```\n */\n async getPasswordPolicySettings(): Promise<PassflowPasswordPolicySettings> {\n try {\n return await this.settingApi.getPasswordPolicySettings();\n } catch (error) {\n this.handleError(error, 'Get password policy settings');\n }\n }\n\n /**\n * Get passkey (WebAuthn) configuration settings.\n *\n * @returns Promise with passkey settings\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const passkeySettings = await passflow.getPasskeySettings();\n * console.log('Passkeys enabled:', passkeySettings.enabled);\n * console.log('User verification:', passkeySettings.user_verification);\n * ```\n */\n async getPasskeySettings(): Promise<PassflowPasskeySettings> {\n try {\n return await this.settingApi.getPasskeySettings();\n } catch (error) {\n this.handleError(error, 'Get passkey settings');\n }\n }\n\n // Passkey methods\n /**\n * Register a new user with a passkey (WebAuthn).\n *\n * @param payload - Passkey registration configuration\n * @param payload.email - User's email address\n * @param payload.username - Optional username\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If passkey registration fails\n *\n * @example\n * ```typescript\n * try {\n * const response = await passflow.passkeyRegister({\n * email: 'user@example.com',\n * username: 'myusername'\n * });\n * console.log('Passkey registered', response.access_token);\n * } catch (error) {\n * console.error('Passkey registration failed', error.message);\n * }\n * ```\n */\n async passkeyRegister(payload: PassflowPasskeyRegisterStartPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.passkeyRegister(payload);\n\n return response;\n }\n\n /**\n * Authenticate a user with a passkey (WebAuthn).\n *\n * @param payload - Passkey authentication configuration\n * @param payload.email - Optional user email to pre-fill\n * @param payload.scopes - Optional scopes to request\n * @returns Promise with authorization response containing tokens\n * @throws {PassflowError} If passkey authentication fails\n *\n * @example\n * ```typescript\n * try {\n * // Let user select from available passkeys\n * const response = await passflow.passkeyAuthenticate({});\n * console.log('Passkey sign-in successful', response.access_token);\n * } catch (error) {\n * console.error('Passkey authentication failed', error.message);\n * }\n * ```\n */\n async passkeyAuthenticate(payload: PassflowPasskeyAuthenticateStartPayload): Promise<PassflowAuthorizationResponse> {\n const response = await this.authService.passkeyAuthenticate(payload);\n\n return response;\n }\n\n // Token management\n /**\n * Manually set tokens (useful after custom authentication flows).\n * This will save tokens to storage, update cache, and trigger SignIn event.\n *\n * @param tokensData - Tokens object to set\n * @param tokensData.access_token - JWT access token\n * @param tokensData.refresh_token - Optional refresh token\n * @param tokensData.id_token - Optional ID token\n * @param tokensData.scopes - Token scopes\n *\n * @example\n * ```typescript\n * // Set tokens from a custom auth flow\n * passflow.setTokens({\n * access_token: 'eyJhbGci...',\n * refresh_token: 'eyJhbGci...',\n * id_token: 'eyJhbGci...',\n * scopes: ['id', 'offline', 'email']\n * });\n * ```\n */\n setTokens(tokensData: Tokens): void {\n this.storageManager.saveTokens(tokensData);\n this.tokenCacheService.setTokensCache(tokensData);\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: tokensData,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n }\n\n /**\n * Get current tokens from storage, optionally refreshing if expired.\n *\n * @param doRefresh - If true, automatically refresh expired tokens (default: false)\n * @returns Promise with tokens or undefined if not authenticated\n *\n * @example\n * ```typescript\n * // Get tokens without refresh\n * const tokens = await passflow.getTokens();\n *\n * // Get tokens and auto-refresh if expired\n * const freshTokens = await passflow.getTokens(true);\n * ```\n */\n async getTokens(doRefresh = false): Promise<Tokens | undefined> {\n return await this.authService.getTokens(doRefresh);\n }\n\n /**\n * Get a specific token from storage by type.\n *\n * @param tokenType - Type of token to retrieve ('access_token', 'refresh_token', 'id_token')\n * @returns Token string or undefined if not found\n *\n * @example\n * ```typescript\n * const accessToken = passflow.getToken('access_token');\n * if (accessToken) {\n * // Use token for API calls\n * fetch('/api/data', {\n * headers: { Authorization: `Bearer ${accessToken}` }\n * });\n * }\n * ```\n */\n getToken(tokenType: TokenType): string | undefined {\n return this.storageManager.getToken(tokenType);\n }\n\n // User passkey methods delegated to UserService\n /**\n * Get list of passkeys registered for the current user.\n *\n * @returns Promise with array of user's passkeys\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const passkeys = await passflow.getUserPasskeys();\n * passkeys.forEach(passkey => {\n * console.log('Passkey:', passkey.name, passkey.id);\n * });\n * ```\n */\n async getUserPasskeys() {\n try {\n return await this.userService.getUserPasskeys();\n } catch (error) {\n this.handleError(error, 'Get user passkeys');\n }\n }\n\n /**\n * Rename a user's passkey to a friendly name.\n *\n * @param name - New friendly name for the passkey\n * @param passkeyId - ID of the passkey to rename\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.renameUserPasskey('My MacBook Pro', 'passkey-123');\n * console.log('Passkey renamed successfully');\n * ```\n */\n async renameUserPasskey(name: string, passkeyId: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.userService.renameUserPasskey(name, passkeyId);\n } catch (error) {\n this.handleError(error, 'Rename user passkey');\n }\n }\n\n /**\n * Delete a passkey from the user's account.\n *\n * @param passkeyId - ID of the passkey to delete\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.deleteUserPasskey('passkey-123');\n * console.log('Passkey deleted successfully');\n * ```\n */\n async deleteUserPasskey(passkeyId: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.userService.deleteUserPasskey(passkeyId);\n } catch (error) {\n this.handleError(error, 'Delete user passkey');\n }\n }\n\n /**\n * Add a new passkey to the current user's account (requires active session).\n *\n * @param options - Optional passkey configuration\n * @param options.relyingPartyId - Optional RP ID for the passkey\n * @param options.passkeyUsername - Optional username to associate with passkey\n * @param options.passkeyDisplayName - Optional display name for the passkey\n * @returns Promise that resolves when passkey is added\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * // Add passkey with default settings\n * await passflow.addUserPasskey();\n *\n * // Add passkey with custom display name\n * await passflow.addUserPasskey({\n * passkeyDisplayName: 'My iPhone'\n * });\n * ```\n */\n async addUserPasskey(options?: {\n relyingPartyId?: string;\n passkeyUsername?: string;\n passkeyDisplayName?: string;\n }): Promise<void> {\n try {\n return await this.userService.addUserPasskey(options);\n } catch (error) {\n this.handleError(error, 'Add user passkey');\n }\n }\n\n // Tenant methods delegated to TenantService\n /**\n * Join a tenant invitation\n * @param token The invitation token\n * @param scopes Optional scopes to request\n * @returns Promise with invite response\n */\n async joinInvitation(token: string, scopes?: string[]): Promise<PassflowAuthorizationResponse> {\n try {\n const response = await this.tenant.joinInvitation(token, scopes);\n response.scopes = scopes ?? this.scopes;\n await this.authService.applyTokens(response);\n return response;\n } catch (error) {\n this.handleError(error, 'Join invitation');\n }\n }\n\n /**\n * Create a new tenant\n * @param name The name of the tenant\n * @param refreshToken Whether to refresh the token after creating the tenant\n * @returns Promise with tenant response\n */\n async createTenant(name: string, refreshToken?: boolean): Promise<PassflowTenantResponse> {\n try {\n const response = await this.tenant.createTenant(name);\n if (refreshToken) {\n await this.refreshToken();\n }\n return response;\n } catch (error) {\n this.handleError(error, 'Create tenant');\n }\n }\n\n // Invitation methods delegated to InvitationService\n /**\n * Request an invitation link for a user to join a tenant.\n *\n * @param payload - Invitation request configuration\n * @param payload.email - Email address to send invitation to\n * @param payload.tenantID - Tenant ID for the invitation\n * @param payload.send_to_email - Whether to send email automatically (default: true)\n * @returns Promise with invitation link response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const invitation = await passflow.requestInviteLink({\n * email: 'newuser@example.com',\n * tenantID: 'tenant-123',\n * send_to_email: true\n * });\n * console.log('Invitation link:', invitation.link);\n * ```\n */\n async requestInviteLink(payload: RequestInviteLinkPayload): Promise<InviteLinkResponse> {\n try {\n // default is true\n if (payload.send_to_email === undefined) {\n payload.send_to_email = true;\n }\n return await this.invitationService.requestInviteLink(payload);\n } catch (error) {\n this.handleError(error, 'Request invite link');\n }\n }\n\n /**\n * Gets a list of active invitations\n * @param options Optional parameters for filtering and pagination\n * @returns Promise with paginated list of invitations\n */\n async getInvitations(options: {\n tenantID: string;\n groupID?: string;\n skip?: number | string;\n limit?: number | string;\n }): Promise<InvitationsPaginatedList> {\n try {\n return await this.invitationService.getInvitations(options);\n } catch (error) {\n this.handleError(error, 'Get invitations');\n }\n }\n\n /**\n * Delete an invitation by its token.\n *\n * @param token - Invitation token to delete\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.deleteInvitation('invitation-token-123');\n * console.log('Invitation deleted');\n * ```\n */\n async deleteInvitation(token: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.invitationService.deleteInvitation(token);\n } catch (error) {\n this.handleError(error, 'Delete invitation');\n }\n }\n\n /**\n * Resend an invitation email.\n *\n * @param token - Invitation token to resend\n * @returns Promise with success response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * await passflow.resendInvitation('invitation-token-123');\n * console.log('Invitation email resent');\n * ```\n */\n async resendInvitation(token: string): Promise<PassflowSuccessResponse> {\n try {\n return await this.invitationService.resendInvitation(token);\n } catch (error) {\n this.handleError(error, 'Resend invitation');\n }\n }\n\n /**\n * Get invitation details and link by invitation ID.\n *\n * @param invitationID - Invitation ID to retrieve\n * @returns Promise with invitation link response\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const invitation = await passflow.getInvitationLink('invitation-123');\n * console.log('Invitation link:', invitation.link);\n * ```\n */\n async getInvitationLink(invitationID: string): Promise<InviteLinkResponse> {\n try {\n return await this.invitationService.getInvitationLink(invitationID);\n } catch (error) {\n this.handleError(error, 'Get invitation link');\n }\n }\n\n // Auth redirect helpers\n /**\n * Generate an authentication redirect URL for hosted login.\n *\n * @param options - Redirect URL configuration\n * @param options.url - Optional custom Passflow server URL\n * @param options.redirectUrl - URL to redirect to after authentication\n * @param options.scopes - Optional scopes to request\n * @param options.appId - Optional app ID to use\n * @returns Authentication redirect URL\n *\n * @example\n * ```typescript\n * const authUrl = passflow.authRedirectUrl({\n * redirectUrl: window.location.origin + '/auth/callback',\n * scopes: ['id', 'email', 'offline']\n * });\n * console.log('Auth URL:', authUrl);\n * // Use this URL for custom navigation logic\n * ```\n */\n authRedirectUrl(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): string {\n return this.authService.authRedirectUrl(options);\n }\n\n /**\n * Redirect to the Passflow hosted login page.\n *\n * @param options - Redirect configuration\n * @param options.url - Optional custom Passflow server URL\n * @param options.redirectUrl - URL to redirect to after authentication\n * @param options.scopes - Optional scopes to request\n * @param options.appId - Optional app ID to use\n *\n * @example\n * ```typescript\n * // Redirect to hosted login page\n * passflow.authRedirect({\n * redirectUrl: window.location.origin + '/auth/callback'\n * });\n * ```\n */\n authRedirect(options: { url?: string; redirectUrl?: string; scopes?: string[]; appId?: string } = {}): void {\n this.authService.authRedirect(options);\n }\n\n /**\n * Get the current token delivery mode.\n * Returns the mode determined by the server (json_body, cookie, or mobile).\n *\n * @returns The current token delivery mode\n *\n * @example\n * ```typescript\n * import { TokenDeliveryMode } from '@passflow/core';\n *\n * const mode = passflow.getDeliveryMode();\n * if (mode === TokenDeliveryMode.Cookie) {\n * console.log('Using cookie-based authentication');\n * } else {\n * console.log('Using JSON body authentication');\n * }\n * ```\n */\n getDeliveryMode(): import('./token/delivery-manager').TokenDeliveryMode {\n return this.authService['tokenDeliveryManager'].getMode();\n }\n\n /**\n * Restore and validate session for cookie mode on page load.\n * Only applicable when using cookie-based token delivery.\n * Validates that HttpOnly cookies are still valid with the server.\n *\n * This method is automatically called on SDK initialization for cookie mode,\n * but can be called manually to re-validate the session.\n *\n * @returns Promise resolving to true if session is valid, false otherwise\n *\n * @example\n * ```typescript\n * // Check if session is valid (useful after page reload)\n * const isValid = await passflow.restoreSession();\n * if (isValid) {\n * console.log('Session restored successfully');\n * } else {\n * console.log('Session expired, please sign in again');\n * }\n * ```\n */\n async restoreSession(): Promise<boolean> {\n return await this.authService.restoreSession();\n }\n\n // Two-Factor Authentication methods\n\n /**\n * Get the current 2FA enrollment status for the authenticated user.\n *\n * @returns Promise with 2FA status including enabled state and policy\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const status = await passflow.getTwoFactorStatus();\n * console.log('2FA enabled:', status.enabled);\n * console.log('Recovery codes remaining:', status.recovery_codes_remaining);\n * ```\n */\n async getTwoFactorStatus(): Promise<TwoFactorStatusResponse> {\n try {\n return await this.twoFactorService.getStatus();\n } catch (error) {\n this.handleError(error, 'Get 2FA status');\n }\n }\n\n /**\n * Begin the 2FA setup process for the authenticated user.\n * Returns a secret and QR code to scan with an authenticator app.\n *\n * @returns Promise with setup response containing secret and QR code\n * @throws {PassflowError} If request fails\n *\n * @example\n * ```typescript\n * const setup = await passflow.beginTwoFactorSetup();\n * console.log('Scan this QR code:', setup.qr_code);\n * console.log('Or enter this secret:', setup.secret);\n * ```\n */\n async beginTwoFactorSetup(): Promise<TwoFactorSetupResponse> {\n try {\n return await this.twoFactorService.beginSetup();\n } catch (error) {\n this.handleError(error, 'Begin 2FA setup');\n }\n }\n\n /**\n * Confirm 2FA setup by verifying a TOTP code from the authenticator app.\n * Returns recovery codes that MUST be saved by the user.\n *\n * @param code - 6-digit TOTP code from authenticator app\n * @returns Promise with confirmation response including recovery codes\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * const result = await passflow.confirmTwoFactorSetup('123456');\n * console.log('SAVE THESE RECOVERY CODES:', result.recovery_codes);\n * // Display recovery codes to user for safekeeping\n * ```\n */\n async confirmTwoFactorSetup(code: string): Promise<TwoFactorConfirmResponse> {\n try {\n return await this.twoFactorService.confirmSetup(code);\n } catch (error) {\n this.handleError(error, 'Confirm 2FA setup');\n }\n }\n\n /**\n * Verify a TOTP code during login when 2FA is required.\n * Completes the authentication process and saves tokens.\n *\n * @param code - 6-digit TOTP code from authenticator app\n * @param options - `trustDevice: true` asks the server to trust this device,\n * so later sign-ins from it skip the second factor where the app allows it\n * @returns Promise with verification response containing tokens\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * // After signIn returns requires_2fa: true\n * if (passflow.isTwoFactorVerificationRequired()) {\n * const response = await passflow.verifyTwoFactor('123456', { trustDevice: true });\n * console.log('Authentication complete', response.access_token);\n * }\n * ```\n */\n async verifyTwoFactor(code: string, options?: TwoFactorCompletionOptions): Promise<TwoFactorVerifyResponse> {\n try {\n // The service has applied the tokens in the app's delivery mode.\n const response = await this.twoFactorService.verify(code, options);\n\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n this.handleError(error, 'Verify 2FA');\n }\n }\n\n /**\n * Use a recovery code for authentication when TOTP is unavailable.\n * Completes the authentication process and saves tokens.\n *\n * @param code - Recovery code from the list provided during setup\n * @param options - `trustDevice: true` asks the server to trust this device\n * @returns Promise with recovery response containing tokens and remaining codes count\n * @throws {PassflowError} If recovery code is invalid\n *\n * @example\n * ```typescript\n * // After signIn returns requires_2fa: true\n * const result = await passflow.useTwoFactorRecoveryCode('ABCD-1234');\n * console.log('Authenticated with recovery code');\n * console.log(`${result.remaining_recovery_codes} recovery codes remaining`);\n * ```\n */\n async useTwoFactorRecoveryCode(code: string, options?: TwoFactorCompletionOptions): Promise<TwoFactorRecoveryResponse> {\n try {\n // The service has applied the tokens in the app's delivery mode.\n const response = await this.twoFactorService.useRecoveryCode(code, options);\n\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: response,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n await this.submitSessionCheck();\n return response;\n } catch (error) {\n this.handleError(error, 'Use 2FA recovery code');\n }\n }\n\n /**\n * Disable 2FA for the authenticated user.\n * Requires verification with a current TOTP code.\n *\n * @param code - Current 6-digit TOTP code for verification\n * @returns Promise with success response\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * await passflow.disableTwoFactor('123456');\n * console.log('2FA disabled successfully');\n * ```\n */\n async disableTwoFactor(code: string): Promise<TwoFactorDisableResponse> {\n try {\n return await this.twoFactorService.disable(code);\n } catch (error) {\n this.handleError(error, 'Disable 2FA');\n }\n }\n\n /**\n * Regenerate recovery codes for the authenticated user.\n * Old recovery codes will be invalidated. Requires verification with a current TOTP code.\n *\n * @param code - Current 6-digit TOTP code for verification\n * @returns Promise with response containing new recovery codes\n * @throws {PassflowError} If verification fails\n *\n * @example\n * ```typescript\n * const result = await passflow.regenerateTwoFactorRecoveryCodes('123456');\n * console.log('New recovery codes:', result.recovery_codes);\n * // Display new recovery codes to user for safekeeping\n * ```\n */\n async regenerateTwoFactorRecoveryCodes(code: string): Promise<TwoFactorRegenerateResponse> {\n try {\n return await this.twoFactorService.regenerateRecoveryCodes(code);\n } catch (error) {\n this.handleError(error, 'Regenerate 2FA recovery codes');\n }\n }\n\n /**\n * List the devices the signed-in user trusted to skip the second factor on\n * this app. The device making the request has `current: true`.\n *\n * @returns Promise with the trusted devices\n * @throws {PassflowError} If the request fails\n *\n * @example\n * ```typescript\n * const devices = await passflow.getTrustedDevices();\n * devices.forEach((d) => console.log(d.user_agent, d.current ? '(this device)' : ''));\n * ```\n */\n async getTrustedDevices(): Promise<TwoFactorTrustedDevice[]> {\n try {\n return await this.twoFactorService.getTrustedDevices();\n } catch (error) {\n this.handleError(error, 'Get trusted devices');\n }\n }\n\n /**\n * Revoke one of the signed-in user's trusted devices. Its next sign-in\n * asks for the second factor again.\n *\n * @param id - The trusted device's `id` from `getTrustedDevices()`\n * @throws {PassflowError} If the request fails, including a 404 for an unknown id\n *\n * @example\n * ```typescript\n * await passflow.revokeTrustedDevice(device.id);\n * ```\n */\n async revokeTrustedDevice(id: string): Promise<void> {\n try {\n await this.twoFactorService.revokeTrustedDevice(id);\n } catch (error) {\n this.handleError(error, 'Revoke trusted device');\n }\n }\n\n /**\n * Check if 2FA verification is currently required (local state check).\n * Returns true if user has signed in but needs to complete 2FA verification.\n *\n * @returns True if 2FA verification is pending, false otherwise\n *\n * @example\n * ```typescript\n * if (passflow.isTwoFactorVerificationRequired()) {\n * // Show 2FA code input UI\n * console.log('Please enter your 2FA code');\n * }\n * ```\n */\n isTwoFactorVerificationRequired(): boolean {\n return this.twoFactorService.isVerificationRequired();\n }\n\n /**\n * Get configured TOTP digit count for the current app\n * @returns Number of digits (6 or 8) configured for TOTP codes\n *\n * @example\n * ```typescript\n * const digits = passflow.getTotpDigits();\n * console.log(`TOTP codes should be ${digits} digits`);\n * ```\n */\n getTotpDigits(): 6 | 8 {\n return this.twoFactorService.getTotpDigits();\n }\n\n // Magic Link 2FA Setup Methods\n\n /**\n * Validate a magic link token for 2FA setup.\n * Used when a user clicks on an admin-generated magic link to set up 2FA.\n *\n * The magic link creates a scoped session that can ONLY be used for 2FA setup,\n * not for regular authentication.\n *\n * @param token - Magic link token from URL parameter\n * @returns Promise with validation response containing scoped session or error\n *\n * @example\n * ```typescript\n * // On the magic link landing page (e.g., /2fa-setup/:token)\n * const urlToken = extractTokenFromUrl();\n * const result = await passflow.validateTwoFactorSetupMagicLink(urlToken);\n *\n * if (result.success) {\n * console.log('Magic link validated');\n * console.log('User ID:', result.userId);\n * console.log('Session expires in:', result.expiresIn, 'seconds');\n * // Show 2FA setup form\n * } else {\n * console.error('Validation failed:', result.error?.message);\n * // Show error UI\n * }\n * ```\n */\n async validateTwoFactorSetupMagicLink(\n token: string,\n ): Promise<import('./api/model').TwoFactorSetupMagicLinkValidationResponse> {\n return await this.twoFactorService.validateTwoFactorSetupMagicLink(token);\n }\n\n /**\n * Get the current magic link session (if any).\n * Used by React SDK components to access session data.\n *\n * @returns Active magic link session or null if none/expired\n *\n * @example\n * ```typescript\n * const session = passflow.getMagicLinkSession();\n * if (session) {\n * console.log('Active session for user:', session.userId);\n * console.log('Expires at:', new Date(session.expiresAt));\n * }\n * ```\n */\n getMagicLinkSession(): import('./api/model').TwoFactorSetupMagicLinkSession | null {\n return this.twoFactorService.getMagicLinkSession();\n }\n\n /**\n * Check if a magic link session is currently active.\n *\n * @returns True if magic link session is active and not expired\n *\n * @example\n * ```typescript\n * if (passflow.hasMagicLinkSession()) {\n * // User can proceed with 2FA setup\n * } else {\n * // Redirect to error page or request new link\n * }\n * ```\n */\n hasMagicLinkSession(): boolean {\n return this.twoFactorService.hasMagicLinkSession();\n }\n\n /**\n * Clear the magic link session.\n * Called after successful 2FA setup or on error.\n *\n * @example\n * ```typescript\n * // After 2FA setup is complete\n * passflow.clearMagicLinkSession();\n * // Redirect to sign-in\n * ```\n */\n clearMagicLinkSession(): void {\n this.twoFactorService.clearMagicLinkSession();\n }\n}\n","/**\n * M2M Authentication Error Classes\n *\n * Custom error classes for M2M authentication failures with\n * OAuth 2.0 compliant error codes and detailed information.\n */\n\nimport type { M2MErrorCode, M2MRateLimitInfo } from './types';\n\n/**\n * M2M Authentication Error\n *\n * Thrown when M2M authentication fails. Contains OAuth 2.0 compliant\n * error codes and additional debugging information.\n *\n * @example\n * ```typescript\n * try {\n * const token = await m2m.getToken();\n * } catch (error) {\n * if (error instanceof M2MError) {\n * console.error(`Error: ${error.code} - ${error.message}`);\n * if (error.code === 'rate_limit_exceeded') {\n * console.log(`Retry after: ${error.rateLimitInfo?.reset}`);\n * }\n * }\n * }\n * ```\n */\nexport class M2MError extends Error {\n /** OAuth 2.0 error code */\n readonly code: M2MErrorCode;\n\n /** HTTP status code from the response */\n readonly status: number;\n\n /** URI with more information about the error (if provided) */\n readonly errorUri?: string;\n\n /** Rate limit information (if rate limited) */\n readonly rateLimitInfo?: M2MRateLimitInfo;\n\n /** Response headers from the server */\n readonly headers?: Record<string, string>;\n\n /** Original error (if this wraps another error) */\n readonly cause?: Error;\n\n /** Timestamp when the error occurred */\n readonly timestamp: string;\n\n constructor(options: {\n code: M2MErrorCode;\n message: string;\n status?: number;\n errorUri?: string;\n rateLimitInfo?: M2MRateLimitInfo;\n headers?: Record<string, string>;\n cause?: Error;\n }) {\n super(options.message);\n this.name = 'M2MError';\n this.code = options.code;\n this.status = options.status ?? 400;\n this.errorUri = options.errorUri;\n this.rateLimitInfo = options.rateLimitInfo;\n this.headers = options.headers;\n this.cause = options.cause;\n this.timestamp = new Date().toISOString();\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, M2MError);\n }\n }\n\n /**\n * Create an M2MError from an OAuth 2.0 error response\n */\n static fromOAuthError(\n errorResponse: {\n error: M2MErrorCode;\n error_description?: string;\n error_uri?: string;\n },\n status: number,\n headers?: Record<string, string>,\n ): M2MError {\n const rateLimitInfo = headers ? M2MError.parseRateLimitHeaders(headers) : undefined;\n\n return new M2MError({\n code: errorResponse.error,\n message: errorResponse.error_description ?? M2MError.getDefaultMessage(errorResponse.error),\n status,\n errorUri: errorResponse.error_uri,\n rateLimitInfo,\n headers,\n });\n }\n\n /**\n * Create an M2MError from a network or other error\n */\n static fromError(error: Error, code: M2MErrorCode = 'server_error'): M2MError {\n return new M2MError({\n code,\n message: error.message || 'An unexpected error occurred',\n status: 500,\n cause: error,\n });\n }\n\n /**\n * Parse rate limit headers from response\n */\n static parseRateLimitHeaders(headers: Record<string, string>): M2MRateLimitInfo | undefined {\n const limit = headers['x-ratelimit-limit'];\n const remaining = headers['x-ratelimit-remaining'];\n const reset = headers['x-ratelimit-reset'] || headers['retry-after'];\n\n if (limit && remaining && reset) {\n return {\n limit: parseInt(limit, 10),\n remaining: parseInt(remaining, 10),\n reset: parseInt(reset, 10),\n };\n }\n\n return undefined;\n }\n\n /**\n * Get default error message for an error code\n */\n static getDefaultMessage(code: M2MErrorCode): string {\n const messages: Record<M2MErrorCode, string> = {\n invalid_request: 'The request is missing a required parameter or is otherwise malformed.',\n invalid_client: 'Client authentication failed. Verify your client credentials.',\n invalid_grant: 'The provided authorization grant is invalid or expired.',\n invalid_scope: 'The requested scope is invalid, unknown, or exceeds the allowed scopes.',\n unauthorized_client: 'The client is not authorized to use this grant type.',\n unsupported_grant_type: 'The authorization grant type is not supported.',\n rate_limit_exceeded: 'Too many requests. Please retry after the rate limit window resets.',\n server_error: 'The authorization server encountered an unexpected error.',\n temporarily_unavailable: 'The authorization server is temporarily unavailable. Please try again later.',\n };\n\n return messages[code] || 'An unknown error occurred.';\n }\n\n /**\n * Check if the error is retryable\n */\n isRetryable(): boolean {\n return (\n this.code === 'server_error' ||\n this.code === 'temporarily_unavailable' ||\n this.code === 'rate_limit_exceeded' ||\n this.status >= 500\n );\n }\n\n /**\n * Get suggested wait time before retry (in milliseconds)\n */\n getRetryAfter(): number {\n if (this.rateLimitInfo?.reset) {\n const now = Math.floor(Date.now() / 1000);\n const waitSeconds = this.rateLimitInfo.reset - now;\n return Math.max(waitSeconds * 1000, 1000);\n }\n\n // Default to 1 second for server errors\n return 1000;\n }\n\n /**\n * Convert to JSON-serializable object\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n errorUri: this.errorUri,\n rateLimitInfo: this.rateLimitInfo,\n timestamp: this.timestamp,\n };\n }\n\n /**\n * Create a human-readable string representation\n */\n toString(): string {\n let str = `M2MError [${this.code}]: ${this.message}`;\n if (this.status) {\n str += ` (HTTP ${this.status})`;\n }\n return str;\n }\n}\n\n/**\n * Network error (connection failed, timeout, etc.)\n */\nexport class M2MNetworkError extends M2MError {\n constructor(message: string, cause?: Error) {\n super({\n code: 'temporarily_unavailable',\n message,\n status: 0,\n cause,\n });\n this.name = 'M2MNetworkError';\n }\n}\n\n/**\n * Token parsing error (invalid JWT format)\n */\nexport class M2MTokenParseError extends M2MError {\n constructor(message: string, cause?: Error) {\n super({\n code: 'invalid_request',\n message,\n status: 400,\n cause,\n });\n this.name = 'M2MTokenParseError';\n }\n}\n\n/**\n * Configuration error (missing or invalid config)\n */\nexport class M2MConfigError extends M2MError {\n constructor(message: string) {\n super({\n code: 'invalid_request',\n message,\n status: 400,\n });\n this.name = 'M2MConfigError';\n }\n}\n","/**\n * M2M (Machine-to-Machine) Authentication Types\n *\n * OAuth 2.0 Client Credentials Grant implementation for server-to-server\n * authentication without user involvement.\n */\n\n/**\n * M2M Client configuration options\n */\nexport type M2MClientConfig = {\n /** Passflow server URL */\n url: string;\n\n /** M2M application client ID */\n clientId: string;\n\n /** M2M application client secret */\n clientSecret: string;\n\n /** Scopes to request (default: []) */\n scopes?: string[];\n\n /** Target audiences for the token */\n audience?: string[];\n\n /** Automatically refresh tokens before expiry (default: false) */\n autoRefresh?: boolean;\n\n /** Seconds before expiry to trigger refresh (default: 30) */\n refreshThreshold?: number;\n\n /** Request timeout in milliseconds (default: 10000) */\n timeout?: number;\n\n /** Number of retry attempts on failure (default: 3) */\n retries?: number;\n\n /** Delay between retries in milliseconds (default: 1000) */\n retryDelay?: number;\n\n /** Custom retry strategy */\n retryStrategy?: RetryStrategy;\n\n /** Custom token cache implementation */\n cache?: M2MTokenCache;\n\n /** Callback for token requests (for logging/metrics) */\n onTokenRequest?: (request: M2MTokenRequestInfo) => void;\n\n /** Callback for token responses (for logging/metrics) */\n onTokenResponse?: (response: M2MTokenResponse) => void;\n\n /** Callback for errors (for logging/metrics) */\n onError?: (error: M2MErrorResponse) => void;\n};\n\n/**\n * Token request options (can override defaults per-request)\n */\nexport type M2MTokenRequestOptions = {\n /** Override default scopes for this request */\n scopes?: string[];\n\n /** Override default audience for this request */\n audience?: string[];\n\n /** Force a new token request, ignoring cache */\n forceRefresh?: boolean;\n};\n\n/**\n * Token request info (for logging callbacks)\n */\nexport type M2MTokenRequestInfo = {\n /** Client ID being used */\n clientId: string;\n\n /** Scopes being requested */\n scopes: string[];\n\n /** Audiences being requested */\n audience: string[];\n\n /** Request timestamp */\n timestamp: string;\n};\n\n/**\n * OAuth 2.0 token response from the authorization server\n */\nexport type M2MTokenResponse = {\n /** The access token (JWT) */\n access_token: string;\n\n /** Token type (always \"Bearer\") */\n token_type: 'Bearer';\n\n /** Token lifetime in seconds */\n expires_in: number;\n\n /** Granted scopes (space-separated string) */\n scope?: string;\n\n /** Timestamp when token was issued (added by client) */\n issued_at?: number;\n};\n\n/**\n * OAuth 2.0 error response from the authorization server\n */\nexport type M2MErrorResponse = {\n /** OAuth 2.0 error code */\n error: M2MErrorCode;\n\n /** Human-readable error description */\n error_description?: string;\n\n /** URI with more information about the error */\n error_uri?: string;\n};\n\n/**\n * Parsed M2M JWT token claims\n */\nexport type M2MTokenClaims = {\n /** Issuer (Passflow server URL) */\n iss: string;\n\n /** Subject (client_id) */\n sub: string;\n\n /** Audience (target APIs) */\n aud: string | string[];\n\n /** Issued at timestamp (Unix epoch seconds) */\n iat: number;\n\n /** Expiration timestamp (Unix epoch seconds) */\n exp: number;\n\n /** JWT ID (unique token identifier) */\n jti?: string;\n\n /** Token type (\"m2m\") */\n type: 'm2m';\n\n /** Client ID */\n client_id: string;\n\n /** Tenant ID (for tenant-scoped M2M apps) */\n tenant_id?: string;\n\n /** Granted scopes */\n scopes: string[];\n};\n\n/**\n * M2M error codes (OAuth 2.0 compliant)\n */\nexport type M2MErrorCode =\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'rate_limit_exceeded'\n | 'server_error'\n | 'temporarily_unavailable';\n\n/**\n * M2M error code enum for convenience\n */\nexport const M2MErrorCodes = {\n InvalidRequest: 'invalid_request' as const,\n InvalidClient: 'invalid_client' as const,\n InvalidGrant: 'invalid_grant' as const,\n InvalidScope: 'invalid_scope' as const,\n UnauthorizedClient: 'unauthorized_client' as const,\n UnsupportedGrantType: 'unsupported_grant_type' as const,\n RateLimitExceeded: 'rate_limit_exceeded' as const,\n ServerError: 'server_error' as const,\n TemporarilyUnavailable: 'temporarily_unavailable' as const,\n};\n\n/**\n * Custom token cache interface for external cache implementations\n */\nexport interface M2MTokenCache {\n /**\n * Get cached token by key\n * @param key Cache key (typically clientId)\n * @returns Cached token or null if not found/expired\n */\n get(key: string): Promise<M2MTokenResponse | null>;\n\n /**\n * Cache a token with TTL\n * @param key Cache key (typically clientId)\n * @param token Token to cache\n * @param ttl Time-to-live in seconds\n */\n set(key: string, token: M2MTokenResponse, ttl: number): Promise<void>;\n\n /**\n * Delete cached token\n * @param key Cache key (typically clientId)\n */\n delete(key: string): Promise<void>;\n}\n\n/**\n * Custom retry strategy interface\n */\nexport interface RetryStrategy {\n /**\n * Determine if the request should be retried\n * @param error The error that occurred\n * @param attempt Current attempt number (1-based)\n * @returns true if request should be retried\n */\n shouldRetry(error: { code: M2MErrorCode; status?: number }, attempt: number): boolean;\n\n /**\n * Get delay before next retry\n * @param attempt Current attempt number (1-based)\n * @returns Delay in milliseconds\n */\n getDelay(attempt: number): number;\n}\n\n/**\n * Internal token request payload sent to the authorization server\n */\nexport type M2MTokenRequestPayload = {\n grant_type: 'client_credentials';\n client_id: string;\n client_secret: string;\n scope?: string;\n audience?: string;\n};\n\n/**\n * Rate limit information from response headers\n */\nexport type M2MRateLimitInfo = {\n /** Maximum requests allowed in the window */\n limit: number;\n\n /** Remaining requests in the current window */\n remaining: number;\n\n /** Unix timestamp when the window resets */\n reset: number;\n};\n\n/**\n * Default configuration values\n */\nexport const M2M_DEFAULTS = {\n /** Default token endpoint path */\n TOKEN_ENDPOINT: '/oauth2/token',\n\n /** Default request timeout in milliseconds */\n TIMEOUT: 10000,\n\n /** Default number of retry attempts */\n RETRIES: 3,\n\n /** Default delay between retries in milliseconds */\n RETRY_DELAY: 1000,\n\n /** Default refresh threshold in seconds */\n REFRESH_THRESHOLD: 30,\n\n /** Content-Type for token requests */\n CONTENT_TYPE: 'application/x-www-form-urlencoded',\n} as const;\n","/**\n * M2M (Machine-to-Machine) Authentication Client\n *\n * OAuth 2.0 Client Credentials Grant implementation for server-to-server\n * authentication without user involvement.\n *\n * @example\n * ```typescript\n * const m2m = new M2MClient({\n * url: 'https://auth.yourapp.com',\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * scopes: ['users:read', 'orders:write'],\n * });\n *\n * const token = await m2m.getToken();\n * ```\n */\n\nimport { M2MConfigError, M2MError, M2MNetworkError, M2MTokenParseError } from './errors';\nimport type {\n M2MClientConfig,\n M2MErrorCode,\n M2MTokenCache,\n M2MTokenClaims,\n M2MTokenRequestOptions,\n M2MTokenRequestPayload,\n M2MTokenResponse,\n RetryStrategy,\n} from './types';\nimport { M2M_DEFAULTS } from './types';\n\n/**\n * Default in-memory token cache\n */\nclass InMemoryCache implements M2MTokenCache {\n private cache: Map<string, { token: M2MTokenResponse; expiresAt: number }> = new Map();\n\n get(key: string): Promise<M2MTokenResponse | null> {\n const entry = this.cache.get(key);\n if (!entry) return Promise.resolve(null);\n\n // Check if expired\n if (Date.now() >= entry.expiresAt) {\n this.cache.delete(key);\n return Promise.resolve(null);\n }\n\n return Promise.resolve(entry.token);\n }\n\n set(key: string, token: M2MTokenResponse, ttl: number): Promise<void> {\n this.cache.set(key, {\n token,\n expiresAt: Date.now() + ttl * 1000,\n });\n return Promise.resolve();\n }\n\n delete(key: string): Promise<void> {\n this.cache.delete(key);\n return Promise.resolve();\n }\n}\n\n/**\n * Default retry strategy with exponential backoff\n */\nconst defaultRetryStrategy: RetryStrategy = {\n shouldRetry(error: { code: M2MErrorCode; status?: number }, attempt: number): boolean {\n // Don't retry after max attempts\n if (attempt >= 3) return false;\n\n // Retry on server errors and rate limits\n return (\n error.code === 'server_error' ||\n error.code === 'temporarily_unavailable' ||\n error.code === 'rate_limit_exceeded' ||\n (error.status !== undefined && error.status >= 500)\n );\n },\n getDelay(attempt: number): number {\n // Exponential backoff: 1s, 2s, 4s\n return Math.pow(2, attempt - 1) * 1000;\n },\n};\n\n/**\n * M2M Authentication Client\n *\n * Implements OAuth 2.0 Client Credentials Grant for machine-to-machine\n * authentication. Provides automatic token caching, refresh, and retry logic.\n */\nexport class M2MClient {\n private readonly config: Required<\n Pick<M2MClientConfig, 'url' | 'clientId' | 'clientSecret' | 'timeout' | 'retries' | 'retryDelay' | 'refreshThreshold'>\n > &\n Pick<\n M2MClientConfig,\n 'scopes' | 'audience' | 'autoRefresh' | 'retryStrategy' | 'cache' | 'onTokenRequest' | 'onTokenResponse' | 'onError'\n >;\n\n private readonly cache: M2MTokenCache;\n private readonly retryStrategy: RetryStrategy;\n private readonly tokenEndpoint: string;\n\n /**\n * Create a new M2M client\n *\n * @param config - Client configuration\n * @throws {M2MConfigError} If required configuration is missing\n *\n * @example\n * ```typescript\n * const m2m = new M2MClient({\n * url: 'https://auth.yourapp.com',\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * });\n * ```\n */\n constructor(config: M2MClientConfig) {\n // Validate required config\n if (!config.url) {\n throw new M2MConfigError('M2M client requires a URL');\n }\n if (!config.clientId) {\n throw new M2MConfigError('M2M client requires a clientId');\n }\n if (!config.clientSecret) {\n throw new M2MConfigError('M2M client requires a clientSecret');\n }\n\n // Normalize URL (remove trailing slash)\n const url = config.url.replace(/\\/$/, '');\n\n this.config = {\n url,\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n scopes: config.scopes,\n audience: config.audience,\n autoRefresh: config.autoRefresh ?? false,\n refreshThreshold: config.refreshThreshold ?? M2M_DEFAULTS.REFRESH_THRESHOLD,\n timeout: config.timeout ?? M2M_DEFAULTS.TIMEOUT,\n retries: config.retries ?? M2M_DEFAULTS.RETRIES,\n retryDelay: config.retryDelay ?? M2M_DEFAULTS.RETRY_DELAY,\n retryStrategy: config.retryStrategy,\n cache: config.cache,\n onTokenRequest: config.onTokenRequest,\n onTokenResponse: config.onTokenResponse,\n onError: config.onError,\n };\n\n this.cache = config.cache ?? new InMemoryCache();\n this.retryStrategy = config.retryStrategy ?? defaultRetryStrategy;\n this.tokenEndpoint = `${url}${M2M_DEFAULTS.TOKEN_ENDPOINT}`;\n }\n\n /**\n * Get the cache key for this client\n */\n private getCacheKey(scopes?: string[], audience?: string[]): string {\n const scopeKey = scopes?.sort().join(',') || '';\n const audienceKey = audience?.sort().join(',') || '';\n return `m2m:${this.config.clientId}:${scopeKey}:${audienceKey}`;\n }\n\n /**\n * Request an access token from the authorization server\n *\n * @param options - Optional request overrides\n * @returns Token response\n * @throws {M2MError} On authentication failure\n *\n * @example\n * ```typescript\n * // Basic usage\n * const token = await m2m.getToken();\n *\n * // With options\n * const token = await m2m.getToken({\n * scopes: ['users:read'],\n * forceRefresh: true,\n * });\n * ```\n */\n async getToken(options?: M2MTokenRequestOptions): Promise<M2MTokenResponse> {\n const scopes = options?.scopes ?? this.config.scopes;\n const audience = options?.audience ?? this.config.audience;\n const cacheKey = this.getCacheKey(scopes, audience);\n\n // Check cache first (unless forced refresh)\n if (!options?.forceRefresh) {\n const cached = await this.cache.get(cacheKey);\n if (cached && !this.isTokenExpired(cached)) {\n return cached;\n }\n }\n\n // Request new token\n return this.requestToken(scopes, audience, cacheKey);\n }\n\n /**\n * Get a valid token, automatically refreshing if needed\n *\n * When autoRefresh is enabled, this will proactively refresh tokens\n * that are about to expire (within refreshThreshold seconds).\n *\n * @returns Valid token response\n * @throws {M2MError} On authentication failure\n *\n * @example\n * ```typescript\n * // Always returns a valid, non-expired token\n * const token = await m2m.getValidToken();\n * ```\n */\n async getValidToken(): Promise<M2MTokenResponse> {\n const scopes = this.config.scopes;\n const audience = this.config.audience;\n const cacheKey = this.getCacheKey(scopes, audience);\n\n // Check cache\n const cached = await this.cache.get(cacheKey);\n\n if (cached) {\n // If auto-refresh is enabled and token is about to expire, refresh it\n if (this.config.autoRefresh && this.isTokenExpired(cached, this.config.refreshThreshold)) {\n return this.requestToken(scopes, audience, cacheKey);\n }\n\n // If token is not expired, return it\n if (!this.isTokenExpired(cached)) {\n return cached;\n }\n }\n\n // Request new token\n return this.requestToken(scopes, audience, cacheKey);\n }\n\n /**\n * Request a new token from the authorization server\n */\n private async requestToken(scopes?: string[], audience?: string[], cacheKey?: string): Promise<M2MTokenResponse> {\n // Build request payload\n const payload: M2MTokenRequestPayload = {\n grant_type: 'client_credentials',\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n };\n\n if (scopes && scopes.length > 0) {\n payload.scope = scopes.join(' ');\n }\n\n if (audience && audience.length > 0) {\n payload.audience = audience.join(' ');\n }\n\n // Notify callback\n if (this.config.onTokenRequest) {\n this.config.onTokenRequest({\n clientId: this.config.clientId,\n scopes: scopes ?? [],\n audience: audience ?? [],\n timestamp: new Date().toISOString(),\n });\n }\n\n // Execute request with retries\n const token = await this.executeWithRetry(() => this.doTokenRequest(payload));\n\n // Add issued_at timestamp\n token.issued_at = Math.floor(Date.now() / 1000);\n\n // Cache the token\n if (cacheKey) {\n await this.cache.set(cacheKey, token, token.expires_in);\n }\n\n // Notify callback\n if (this.config.onTokenResponse) {\n this.config.onTokenResponse(token);\n }\n\n return token;\n }\n\n /**\n * Execute the actual HTTP request to the token endpoint\n */\n private async doTokenRequest(payload: M2MTokenRequestPayload): Promise<M2MTokenResponse> {\n // Build form-encoded body\n const body = new URLSearchParams();\n body.append('grant_type', payload.grant_type);\n body.append('client_id', payload.client_id);\n body.append('client_secret', payload.client_secret);\n if (payload.scope) {\n body.append('scope', payload.scope);\n }\n if (payload.audience) {\n body.append('audience', payload.audience);\n }\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(this.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': M2M_DEFAULTS.CONTENT_TYPE,\n Accept: 'application/json',\n },\n body: body.toString(),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n // Parse response body\n const data = await response.json();\n\n // Handle error response\n if (!response.ok) {\n const error = M2MError.fromOAuthError(\n {\n error: data.error || 'server_error',\n error_description: data.error_description || data.message,\n error_uri: data.error_uri,\n },\n response.status,\n headers,\n );\n\n if (this.config.onError) {\n this.config.onError({\n error: error.code,\n error_description: error.message,\n });\n }\n\n throw error;\n }\n\n return data as M2MTokenResponse;\n } catch (error) {\n clearTimeout(timeoutId);\n\n // Handle abort (timeout)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new M2MNetworkError(`Request timed out after ${this.config.timeout}ms`);\n }\n\n // Handle network errors\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw new M2MNetworkError(`Network error: ${error.message}`, error);\n }\n\n // Re-throw M2M errors\n if (error instanceof M2MError) {\n throw error;\n }\n\n // Wrap unknown errors\n throw M2MError.fromError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Execute a request with retry logic\n */\n private async executeWithRetry<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: M2MError | undefined;\n\n for (let attempt = 1; attempt <= this.config.retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n if (!(error instanceof M2MError)) {\n throw error;\n }\n\n lastError = error;\n\n // Check if we should retry\n if (\n attempt < this.config.retries &&\n this.retryStrategy.shouldRetry({ code: error.code, status: error.status }, attempt)\n ) {\n const delay = this.retryStrategy.getDelay(attempt);\n await this.sleep(delay);\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError ?? new M2MError({ code: 'server_error', message: 'Request failed after retries' });\n }\n\n /**\n * Sleep for a given duration\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Get the currently cached token without making a request\n *\n * @returns Cached token or null if not cached\n *\n * @example\n * ```typescript\n * const token = m2m.getCachedToken();\n * if (token && !m2m.isTokenExpired(token)) {\n * console.log('Using cached token');\n * }\n * ```\n */\n getCachedToken(): M2MTokenResponse | null {\n // Note: This is synchronous for the default in-memory cache\n // For async caches, use getToken() instead\n const cache = this.cache as InMemoryCache;\n if ('cache' in cache) {\n const cacheKey = this.getCacheKey(this.config.scopes, this.config.audience);\n const entry = (cache as unknown as { cache: Map<string, { token: M2MTokenResponse; expiresAt: number }> }).cache.get(\n cacheKey,\n );\n return entry?.token ?? null;\n }\n return null;\n }\n\n /**\n * Check if a token is expired or about to expire\n *\n * @param token - Token to check (uses issued_at + expires_in if available)\n * @param threshold - Seconds before actual expiry to consider expired (default: 0)\n * @returns true if expired or about to expire\n *\n * @example\n * ```typescript\n * if (m2m.isTokenExpired(token)) {\n * console.log('Token is expired');\n * }\n *\n * // Check if expiring within 5 minutes\n * if (m2m.isTokenExpired(token, 300)) {\n * console.log('Token expires soon');\n * }\n * ```\n */\n isTokenExpired(token?: M2MTokenResponse | null, threshold = 0): boolean {\n if (!token) return true;\n\n const now = Math.floor(Date.now() / 1000);\n const issuedAt = token.issued_at ?? now - token.expires_in;\n const expiresAt = issuedAt + token.expires_in;\n\n return now >= expiresAt - threshold;\n }\n\n /**\n * Parse token claims from a JWT access token\n *\n * @param token - JWT access token string\n * @returns Decoded token claims\n * @throws {M2MTokenParseError} If token format is invalid\n *\n * @example\n * ```typescript\n * const token = await m2m.getToken();\n * const claims = m2m.parseToken(token.access_token);\n * console.log('Client ID:', claims.client_id);\n * console.log('Scopes:', claims.scopes);\n * ```\n */\n parseToken(token: string): M2MTokenClaims {\n try {\n // JWT format: header.payload.signature\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new M2MTokenParseError('Invalid JWT format: expected 3 parts');\n }\n\n // Decode the payload (second part)\n const payload = parts[1];\n if (!payload) {\n throw new M2MTokenParseError('Invalid JWT format: missing payload');\n }\n const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));\n const claims = JSON.parse(decoded);\n\n // Ensure scopes is an array\n if (claims.scopes && typeof claims.scopes === 'string') {\n claims.scopes = claims.scopes.split(' ');\n } else if (!claims.scopes) {\n claims.scopes = [];\n }\n\n return claims as M2MTokenClaims;\n } catch (error) {\n if (error instanceof M2MTokenParseError) {\n throw error;\n }\n throw new M2MTokenParseError(`Failed to parse token: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Clear the token cache, forcing a new request on next getToken()\n *\n * @example\n * ```typescript\n * m2m.clearCache();\n * // Next getToken() will request a new token\n * const token = await m2m.getToken();\n * ```\n */\n clearCache(): void {\n const cacheKey = this.getCacheKey(this.config.scopes, this.config.audience);\n this.cache.delete(cacheKey);\n }\n\n /**\n * Revoke the current token\n *\n * Note: Requires the server to support token revocation (RFC 7009).\n * Not all Passflow deployments may support this endpoint.\n *\n * @throws {M2MError} If revocation fails\n *\n * @example\n * ```typescript\n * await m2m.revokeToken();\n * console.log('Token revoked');\n * ```\n */\n async revokeToken(): Promise<void> {\n const cached = this.getCachedToken();\n if (!cached) {\n return; // No token to revoke\n }\n\n const revokeEndpoint = `${this.config.url}/oauth2/revoke`;\n\n const body = new URLSearchParams();\n body.append('token', cached.access_token);\n body.append('client_id', this.config.clientId);\n body.append('client_secret', this.config.clientSecret);\n\n try {\n const response = await fetch(revokeEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': M2M_DEFAULTS.CONTENT_TYPE,\n },\n body: body.toString(),\n });\n\n // RFC 7009: Server should return 200 even if token was already revoked\n if (!response.ok && response.status !== 200) {\n const data = await response.json().catch(() => ({}));\n throw M2MError.fromOAuthError(\n {\n error: data.error || 'server_error',\n error_description: data.error_description || 'Token revocation failed',\n },\n response.status,\n );\n }\n\n // Clear cache\n this.clearCache();\n } catch (error) {\n if (error instanceof M2MError) {\n throw error;\n }\n throw M2MError.fromError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n /**\n * Get the configured URL\n */\n get url(): string {\n return this.config.url;\n }\n\n /**\n * Get the configured client ID\n */\n get clientId(): string {\n return this.config.clientId;\n }\n\n /**\n * Get the configured scopes\n */\n get scopes(): string[] | undefined {\n return this.config.scopes;\n }\n\n /**\n * Get the configured audience\n */\n get audience(): string[] | undefined {\n return this.config.audience;\n }\n}\n"],"names":["APP_ID_HEADER_KEY","AUTHORIZATION_HEADER_KEY","DEVICE_ID_HEADER_KEY","DEVICE_TYPE_HEADER_KEY","SDK_VERSION","packageJson","MINIMAL_DEFAULT_SCOPES","DEFAULT_SCOPES","PASSFLOW_CLOUD_URL","DEFAULT_GROUP_NAME","POPUP_WIDTH","POPUP_HEIGHT","POPUP_POLL_INTERVAL_MS","POPUP_TIMEOUT_MS","TOKEN_EXPIRY_BUFFER_SECONDS","USERNAME_MIN_LENGTH","USERNAME_MAX_LENGTH","ERROR_MESSAGE_MAX_LENGTH","parseMembership","raw","tenants","k","v","tnt","gk","roles","g","decodeBase64","base64","TokenService","storageManager","ttype","tokenString","token","parseToken","isTokenExpired","tokenType","bufferSeconds","base64Url","padded","decoded","jsonPayload","c","parsedToken","TokenType","TokenDeliveryMode","SessionState","TokenDeliveryManager","mode","persistedMode","persistedState","StorageManager","storage","prefix","tokens","deliveryMode","id_token","access_token","refresh_token","scopes","key","idToken","access","deviceId","url","DeviceService","newDeviceId","uuidv4","PopupClosedError","message","WebAdapter","options","startRegistration","startAuthentication","callback","handler","width","height","expectedReturnOrigin","popupWindow","resolve","reject","startTime","checkInterval","callbackSearch","callbackHash","str","enabled","webAdapter","PARTICIPATING_TOKEN","ABSENT","parseAcrToken","readAcrValues","levels","level","bar","requiredAcrLevel","INSUFFICIENT_USER_AUTHENTICATION","UPGRADE_REQUIRED","AUTH_PARAM","unescapeQuoted","value","parseAuthParams","header","params","match","name","quoted","bare","parseMaxAge","n","maxAgeValue","reading","parseChallengeHeader","status","parseChallengeBody","body","data","readChannel","channel","challenge","rawMaxAge","rawHeaderParams","readUpgradeChallenge","input","headerOnly","rawHeader","headerChallenge","bodyChallenge","diagnostics","report","reason","detail","channels","rawOf","contributing","requiredLevel","acrValues","maxAge","first","second","acrDisagrees","freshnessDisagrees","parseUpgradeChallenge","getHeader","UPGRADE_TRANSACTION_MARKER","UPGRADE_REPLAYED_MARKER","readMarker","config","markUpgradeTransaction","hasUpgradeTransactionMarker","markUpgradeReplayed","hasUpgradeReplayedMarker","RequestMethod","PassflowEndpointPaths","PassflowAdminEndpointPaths","resolveUpgradeDomain","parsed","PassflowError","error","Providers","OS","getOSFromDeviceType","deviceType","pathWithParams","template","result","TwoFactorPolicy","MAX_RETRIES","INITIAL_RETRY_DELAY_MS","axiosHeaderReader","response","readUpgradeChallengeFrom","isUpgradeChallengeResponse","AxiosClient","deviceService","appId","keyStoragePrefix","axios","axiosConfig","csrfToken","controller","e","upgradeError","endpoint","payload","method","retryCount","delayMs","retryAfter","retryAfterNum","retryDate","errorData","path","AppAPI","SIGN_IN_REQUEST_CONFIG","AuthAPI","refreshToken","accessToken","upgrade","os","defaultPayload","create_tenant","anonymous","code","verifier","isAdmin","newPassword","resetToken","codeChallenge","passkeyData","challengeId","otp","headers","CLIAuthAPI","sessionId","request","InvitationAPI","invitationID","SettingAPI","TenantAPI","tenantId","groupId","userId","role","roleId","limit","skip","inviteId","email","bearer","tfaToken","credentialed","TwoFactorApiClient","tfa_token","trust_device","recovery_code","backendResponse","methodId","rest","UpgradeAPI","UserAPI","passkeyId","relyingPartyId","passkeyDisplayName","passkeyUsername","BASE64URL_ALPHABET","VERIFIER_BYTES","base64UrlEncode","bytes","out","i","a","b","webCrypto","crypto","s256Challenge","digest","createPkcePair","PassflowEvent","PassflowStore","subscriber","events","eventSet","subscribedEvents","event","eventType","UpgradeErrorIds","RECOVERABLE_COMMIT_ERROR_IDS","SESSION_INVALIDATING_COMMIT_ERROR_IDS","RETRYABLE_KINDS","isRetryableUpgradeKind","kind","DEFAULT_MESSAGES","UpgradeTransportError","init","UPGRADE_ERROR_CODES","isUpgradeSdkError","isUpgradeTransportError","readErrorFacts","candidate","envelopeId","kindFromErrorId","errorId","kindFromStatus","classifyUpgradeError","facts","isRecoverableCommitFailure","invalidatesSessionOnCommit","UpgradeInsufficientError","obtained","required","UpgradeUnavailableError","UpgradeSessionChangedError","readSessionPin","claims","sessionPinMatches","pin","current","isValidJWTFormat","parts","base64UrlPattern","part","sanitizeErrorMessage","isValidEmail","trimmed","isValidPhoneNumber","phone","isValidUsername","username","isValidTOTPCode","digits","normalizeRecoveryCode","normalized","ConsoleLogger","args","getDefaultLogger","PENDING_PKCE_STORAGE_KEY","PENDING_PKCE_TIMEOUT_MS","AuthService","authApi","subscribeStore","tokenCacheService","createTenantForNewUser","sessionCallbacks","tokenExchangeConfig","platform","readPkceEnabled","pkce","stored","pending","errorPayload","answer","oldScopes","refresh","is4xxError","sscopes","exchanged","challenge_id","publicKey","webauthn","responseRegisterComplete","responseAuthenticateComplete","passflowPathWithProvider","queryParams","passflowURL","hashParams","urlParams","tokensData","redirectUrl","externalUrl","parsedTokens","hasIdToken","sessionValid","sessionUnknown","doRefresh","InvitationService","invitationApi","TenantUserMembership","users","groups","memberships","r","uig","u","m","membership","TenantService","tenantApi","logger","context","responseData","passflowError","TokenCacheService","TwoFactorService","twoFactorApi","signInCompleter","eventSubscriber","tfPayload","errorWithId","completed","normalizedCode","codesCopy","storedState","UserService","userAPI","UpgradeFactorError","factor","cancelled","requireCode","totpFactor","recoveryCodesFactor","passkeyFactor","prepared","passkeys","credential","DEFAULT_UPGRADE_FACTORS","factorRegistry","factors","f","AAL3","CEREMONY_ORDER","rank","demanded","ceremony","needsAAL3","reachesAAL3Unaided","selectFactors","available","UpgradeCancelledError","cause","UpgradeUnsatisfiableError","DEFAULT_MAX_ATTEMPTS","DEFAULT_SETTLE_TIMEOUT_MS","deferred","res","rej","barOf","barMeets","mergeChallenges","incoming","isStrictlyStronger","acrRose","freshnessTightened","UpgradeController","waiter","registration","settled","statedBar","stronger","usable","settle","proof","summary","deadline","waiters","strongest","previousError","attempt","terminal","assertion","RELATIVE_BASE","resolutionBase","scope","baseUri","href","normaliseUrl","base","sentinelBase","baseOrigin","resolved","headerEntry","captureHeaders","captured","iterable","entry","pair","flat","createRequestSnapshot","normalised","snapshot","swapAuthorization","swap","changed","next","headersToObject","bag","ONE_SHOT_METHODS","toPrepared","wire","isAlreadySettled","createUpgradeTransport","deps","permitted","resolveInterceptorOptions","source","parsePattern","pattern","pathMatches","inPassflowDomain","domain","inDeclaredBackend","patterns","patternUrl","isInterceptionScope","shouldIntercept","warn","diagnostic","reporterFor","ctx","sink","forwardParseDiagnostics","JSON_CONTENT_TYPE","readChallenge","contentType","mayReadBody","readOutOfScopeChallenge","outOfScopeMessage","classifyFailure","classifyReplay","replayed","raceAbort","work","signal","onAbort","aborted","handleChallenge","before","elevating","race","elevation","after","outcome","coreOutcomeToError","settleFetchOutcome","original","tagUpgradeOutcome","settleAxiosOutcome","originalError","exhausted","insufficient","unsatisfiable","unavailable","sessionChanged","transportFailure","isUpgradeCancelled","isUpgradeExhausted","isUpgradeInsufficient","isUpgradeUnsatisfiable","isUpgradeUnavailable","isUpgradeSessionChanged","isUpgradeTransportFailure","IDEMPOTENT_METHODS","isRequest","urlOf","methodOf","classifyInitBody","knownBodyBytes","sized","policyPermits","policy","info","bodyBytes","refuse","bodyKind","forwardInput","forwardInit","replayHeaders","hints","initWithoutHeaders","_headers","effectiveHeaders","snapshotBody","copy","requestWithSnapshotHeaders","planReplay","snapshotFor","spare","isRecord","headerReader","bodyKindOf","replayConfig","absolute","contextFor","reissue","inherited","effective","failure","createAxiosUpgradeHandler","typed","settlement","attachUpgradeAuth","host","own","send","id","ejected","dispatchingLayers","abortViewOf","type","listener","plan","markers","abort","upgradeFetch","dispatch","nestedInput","nestedInit","createUpgradeClient","attachAuth","instance","FETCH_PRIORITY_OIDC","FETCH_PRIORITY_UPGRADE","FetchMiddlewareContractError","middlewareName","entries","nextSeq","installed","capturedFetch","ourWrapper","byPriority","globalScope","chain","index","target","delegated","nextInput","nextInit","install","native","wrapper","uninstall","registerFetchMiddleware","middleware","opts","removed","fetchRegistryState","declaredBackends","backend","createUpgradeFacade","installation","isCurrent","owner","transportFor","assertCurrent","buildInstallation","generation","box","built","elevateFor","interceptorOptions","teardown","stated","requested","acr","disposers","attach","torn","dispose","global","_Passflow","createSession","expiredSession","factory","read","settings","client","currentUrl","fromHash","sanitized","Passflow","M2MError","errorResponse","rateLimitInfo","remaining","reset","now","waitSeconds","M2MNetworkError","M2MTokenParseError","M2MConfigError","M2MErrorCodes","M2M_DEFAULTS","InMemoryCache","ttl","defaultRetryStrategy","M2MClient","audience","scopeKey","audienceKey","cacheKey","cached","timeoutId","fn","lastError","delay","ms","cache","threshold","expiresAt","revokeEndpoint"],"mappings":"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"}
|