@passflow/core 0.2.10 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +177 -175
- package/dist/index.mjs.map +1 -1
- package/dist/lib/services/auth-service.d.ts.map +1 -1
- package/dist/lib/services/token-cache-service.d.ts.map +1 -1
- package/dist/lib/store.d.ts +4 -0
- package/dist/lib/store.d.ts.map +1 -1
- package/package.json +4 -2
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/api/model.ts","../lib/api/axios-client.ts","../lib/api/app.ts","../lib/api/auth.ts","../lib/api/invitation.ts","../lib/api/setting.ts","../lib/api/tenant.ts","../lib/api/two-factor.ts","../lib/api/user.ts","../lib/store.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/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 RegistrationResponseJSON,\n} from '@simplewebauthn/types';\nimport type { AxiosRequestConfig } from 'axios';\n\nimport type { Tokens } from '../types';\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 TwoFactorMethodSetupBegin = '/v2/user/2fa/methods/:method/setup/begin',\n TwoFactorMethodSetupConfirm = '/v2/user/2fa/methods/:method/setup/confirm',\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}\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\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}\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","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 { StorageManager } from '../storage';\nimport { TokenService, isTokenExpired, parseToken } from '../token';\nimport { TokenDeliveryManager } from '../token/delivery-manager';\n\nimport {\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowError,\n PassflowResponseError,\n RequestMethod,\n RequestOptions,\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\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\n tokenService: TokenService;\n\n origin = typeof window !== 'undefined' ? window.location.origin : '';\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 private readonly protectedEndpoints = ['logout', 'refresh'];\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n const { url, appId, keyStoragePrefix } = config;\n\n this.url = url || PASSFLOW_CLOUD_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]: 'web',\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 session as invalid on 401\n if (e.response?.status === HttpStatuses.unauthorized) {\n this.tokenDeliveryManager.setSessionInvalid();\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 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 * Falls back to JSON mode if cookies are blocked\n */\n private detectCookieSupport(): void {\n // Only run in browser environment\n if (typeof document === 'undefined') {\n return;\n }\n\n try {\n // Test if cookies are enabled\n document.cookie = 'passflow_test=1; SameSite=Lax';\n const cookiesEnabled = document.cookie.indexOf('passflow_test=1') !== -1;\n document.cookie = 'passflow_test=; expires=Thu, 01 Jan 1970 00:00:00 UTC';\n\n if (!cookiesEnabled && this.tokenDeliveryManager.isCookieMode()) {\n // Cookies disabled but cookie mode requested - server will handle fallback\n // No action needed as server sends token_delivery header to switch modes\n }\n } catch (_error) {\n // Cookie detection failed (likely SSR or restrictive environment)\n // Silent fail - will attempt cookie mode anyway if server requests it\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 { APP_ID_HEADER_KEY, AUTHORIZATION_HEADER_KEY } from '../constants';\n\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\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} 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 refreshToken(refreshToken: string, scopes: string[], accessToken?: string): Promise<PassflowAuthorizationResponse> {\n const payload = {\n access: accessToken,\n scopes,\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, typeof payload>(PassflowEndpointPaths.refresh, payload, {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${refreshToken}`,\n },\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 { 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 * Begin 2FA method setup\n * POST /v2/user/2fa/methods/:method/setup/begin\n */\n beginMethodSetup(method: TwoFactorMethod): Promise<unknown> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorMethodSetupBegin, { method });\n return this.axiosClient.post<unknown, {}>(endpoint, {});\n }\n\n /**\n * Confirm 2FA method setup\n * POST /v2/user/2fa/methods/:method/setup/confirm\n */\n confirmMethodSetup(method: TwoFactorMethod, payload: unknown): Promise<unknown> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorMethodSetupConfirm, { method });\n return this.axiosClient.post<unknown, unknown>(endpoint, payload);\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","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 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.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","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 { startAuthentication, startRegistration } from '@simplewebauthn/browser';\nimport axios from 'axios';\nimport {\n AuthAPI,\n OS,\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} from '../api';\nimport { POPUP_HEIGHT, POPUP_POLL_INTERVAL_MS, POPUP_TIMEOUT_MS, POPUP_WIDTH } from '../constants';\nimport { DeviceService } from '../device';\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 { 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 origin: string,\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 ) {\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 = OS.web;\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 = OS.web;\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 async refreshToken(): Promise<PassflowAuthorizationResponse> {\n this.subscribeStore.notify(PassflowEvent.RefreshStart, {});\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 = await this.authApi.refreshToken('', this.scopes);\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 this.tokenDeliveryManager.setSessionInvalid();\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 const response = await this.authApi.refreshToken(tokens?.refresh_token ?? '', oldScopes, tokens?.access_token);\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 if (error instanceof PassflowError) {\n throw error;\n } else if (axios.isAxiosError(error) && error.response && error.response?.status >= 400 && error.response?.status < 500) {\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(window.location.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 this.subscribeStore.notify(PassflowEvent.RegisterStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = OS.web;\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 = btoa(publicKey.user.id);\n const webauthn = await startRegistration({\n optionsJSON: publicKey,\n });\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 this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = OS.web;\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 startAuthentication({\n optionsJSON: publicKey,\n });\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.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 federatedAuthWithPopup(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\n const popupWindow = window.open(passflowURL, '_blank', `width=${POPUP_WIDTH},height=${POPUP_HEIGHT}`);\n\n if (!popupWindow) {\n this.federatedAuthWithRedirect(payload);\n return;\n }\n\n const startTime = Date.now();\n\n const checkInterval = setInterval(() => {\n // Check if popup was closed by user\n if (popupWindow.closed) {\n clearInterval(checkInterval);\n const errorPayload: ErrorPayload = {\n message: 'Authentication popup was closed',\n code: 'POPUP_CLOSED',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return;\n }\n\n // Check for timeout\n if (Date.now() - startTime > POPUP_TIMEOUT_MS) {\n clearInterval(checkInterval);\n popupWindow.close();\n const errorPayload: ErrorPayload = {\n message: 'Authentication popup timed out',\n code: 'POPUP_TIMEOUT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return;\n }\n\n // Try to check popup URL (may throw cross-origin error)\n try {\n if (popupWindow.location.href.startsWith(this.origin)) {\n const urlParams = new URLSearchParams(popupWindow.location.search);\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 // Process response (may forward to BFF in BFF mode)\n this.processAuthResponse(tokensData, sscopes).then(() => {\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: tokensData,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n window.location.href = `${this.origin}`;\n });\n\n clearInterval(checkInterval);\n popupWindow.close();\n }\n } catch (_error) {\n // Expected cross-origin error - popup still on auth provider domain\n // Continue polling\n }\n }, POPUP_POLL_INTERVAL_MS);\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 window.location.href = 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 ?? window.location.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 window.location.href = 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 { 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 visibilityChangeHandler: (() => 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 ) {\n this.storageManager = storageManager;\n this.authApi = authApi;\n this.setupPageUnloadHandler();\n }\n\n initialize() {\n try {\n const tokens = this.storageManager.getTokens();\n if (!tokens) {\n this.startTokenCheck();\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 this.setTokensCache(undefined);\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 Page Visibility API listener\n this.setupVisibilityListener();\n\n this.checkInterval = setInterval(() => {\n // Skip check if page is hidden\n if (typeof document !== 'undefined' && document.hidden) {\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 setupVisibilityListener() {\n if (typeof document === 'undefined') return;\n\n // Remove previous listener if exists\n if (this.visibilityChangeHandler) {\n document.removeEventListener('visibilitychange', this.visibilityChangeHandler);\n }\n\n this.visibilityChangeHandler = () => {\n if (!document.hidden && this.checkInterval) {\n // Page became visible, do immediate check\n if (!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\n document.addEventListener('visibilitychange', this.visibilityChangeHandler);\n }\n\n private setupPageUnloadHandler() {\n if (typeof window === 'undefined') return;\n\n window.addEventListener('beforeunload', () => {\n this.destroy();\n });\n }\n\n private stopTokenCheck() {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n\n if (this.visibilityChangeHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityChangeHandler);\n this.visibilityChangeHandler = 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 }\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 * Begin 2FA method setup\n */\n async beginMethodSetup(method: TwoFactorMethod): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.beginMethodSetup(method);\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupStarted, { secret: '', method });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Begin 2FA method setup');\n }\n }\n\n /**\n * Confirm 2FA method setup\n */\n async confirmMethodSetup(method: TwoFactorMethod, payload: unknown): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.confirmMethodSetup(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, 'Confirm 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 { startRegistration } from '@simplewebauthn/browser';\nimport { OS, PassflowSuccessResponse, UserAPI } from '../api';\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 ) {}\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 const deviceId = this.deviceService.getDeviceId();\n const os = OS.web;\n const { challenge_id, publicKey } = await this.userAPI.addUserPasskeyStart({\n relyingPartyId: relyingPartyId || window?.location?.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 = btoa(publicKey.user.id);\n const webauthn = await startRegistration({ optionsJSON: publicKey });\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","import {\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 UserAPI,\n} from './api';\nimport { DEFAULT_SCOPES, PASSFLOW_CLOUD_URL, SDK_VERSION } from './constants';\nimport { DeviceService } from './device';\nimport { AuthService, InvitationService, TenantService, TokenCacheService, TwoFactorService, UserService } from './services';\nimport { StorageManager } from './storage';\nimport { type ErrorPayload, PassflowEvent, PassflowStore, type PassflowSubscriber } from './store';\nimport { type TokenType } from './token';\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\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 // 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 origin = window.location.origin;\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\n // Initialize single StorageManager instance\n this.storageManager = new StorageManager({\n prefix: config.keyStoragePrefix ?? '',\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\n // Initialize PassflowStore\n this.subscribeStore = new PassflowStore();\n\n this.tokenCacheService = new TokenCacheService(this.storageManager, this.authApi, this.subscribeStore);\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.origin,\n this.url,\n {\n createSession: this.createSessionCallback,\n expiredSession: this.expiredSessionCallback,\n },\n this.appId ?? '',\n config.tokenExchange,\n );\n\n this.userService = new UserService(this.userApi, this.deviceService);\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 // 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\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 // 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 // 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(window.location.search);\n let fromHash = false;\n\n // If no access_token in query params, check hash\n if (!urlParams.get('access_token') && window.location.hash) {\n // Hash format: #access_token=xxx&refresh_token=yyy\n const hashParams = new URLSearchParams(window.location.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);\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);\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);\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);\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(window.location.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): void {\n if (fromHash) {\n // Clear hash completely - tokens should be removed from URL for security\n window.history.replaceState({}, document.title, window.location.pathname + window.location.search);\n } else {\n const urlParams = new URLSearchParams(window.location.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 window.history.replaceState({}, document.title, `${window.location.pathname}?${urlParams.toString()}`);\n } else {\n window.history.replaceState({}, document.title, window.location.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","RequestMethod","PassflowEndpointPaths","PassflowAdminEndpointPaths","PassflowError","error","Providers","OS","pathWithParams","template","params","result","value","TwoFactorPolicy","MAX_RETRIES","INITIAL_RETRY_DELAY_MS","AxiosClient","config","deviceService","appId","keyStoragePrefix","axios","axiosConfig","csrfToken","controller","response","e","endpoint","cookiesEnabled","payload","method","retryCount","delayMs","retryAfter","retryAfterNum","retryDate","resolve","status","errorData","path","options","data","AppAPI","AuthAPI","refreshToken","accessToken","os","defaultPayload","create_tenant","anonymous","isAdmin","newPassword","resetToken","passkeyData","challengeId","otp","headers","InvitationAPI","invitationID","SettingAPI","TenantAPI","name","tenantId","groupId","userId","role","roleId","limit","skip","inviteId","email","TwoFactorApiClient","tfa_token","code","recovery_code","backendResponse","methodId","UserAPI","passkeyId","relyingPartyId","passkeyDisplayName","passkeyUsername","PassflowEvent","PassflowStore","subscriber","events","eventSet","subscribedEvents","event","eventType","isValidJWTFormat","parts","base64UrlPattern","part","sanitizeErrorMessage","message","isValidEmail","trimmed","isValidPhoneNumber","phone","isValidUsername","username","isValidTOTPCode","digits","normalizeRecoveryCode","normalized","AuthService","authApi","subscribeStore","tokenCacheService","createTenantForNewUser","origin","sessionCallbacks","tokenExchangeConfig","errorPayload","oldScopes","sscopes","challenge_id","publicKey","webauthn","startRegistration","responseRegisterComplete","startAuthentication","responseAuthenticateComplete","passflowPathWithProvider","queryParams","passflowURL","popupWindow","startTime","checkInterval","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","parsed","UserService","userAPI","_Passflow","createSession","expiredSession","fromHash","hashParams","sanitized","Passflow","M2MError","errorResponse","rateLimitInfo","remaining","reset","now","waitSeconds","str","M2MNetworkError","cause","M2MTokenParseError","M2MConfigError","M2MErrorCodes","M2M_DEFAULTS","InMemoryCache","entry","ttl","defaultRetryStrategy","attempt","M2MClient","audience","scopeKey","audienceKey","cacheKey","cached","body","timeoutId","fn","lastError","delay","ms","cache","threshold","expiresAt","claims","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/api/model.ts","../lib/api/axios-client.ts","../lib/api/app.ts","../lib/api/auth.ts","../lib/api/invitation.ts","../lib/api/setting.ts","../lib/api/tenant.ts","../lib/api/two-factor.ts","../lib/api/user.ts","../lib/store.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/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 RegistrationResponseJSON,\n} from '@simplewebauthn/types';\nimport type { AxiosRequestConfig } from 'axios';\n\nimport type { Tokens } from '../types';\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 TwoFactorMethodSetupBegin = '/v2/user/2fa/methods/:method/setup/begin',\n TwoFactorMethodSetupConfirm = '/v2/user/2fa/methods/:method/setup/confirm',\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}\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\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}\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","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 { StorageManager } from '../storage';\nimport { TokenService, isTokenExpired, parseToken } from '../token';\nimport { TokenDeliveryManager } from '../token/delivery-manager';\n\nimport {\n PassflowAuthorizationResponse,\n PassflowConfig,\n PassflowEndpointPaths,\n PassflowError,\n PassflowResponseError,\n RequestMethod,\n RequestOptions,\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\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\n tokenService: TokenService;\n\n origin = typeof window !== 'undefined' ? window.location.origin : '';\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 private readonly protectedEndpoints = ['logout', 'refresh'];\n\n constructor(config: PassflowConfig, storageManager?: StorageManager, deviceService?: DeviceService) {\n const { url, appId, keyStoragePrefix } = config;\n\n this.url = url || PASSFLOW_CLOUD_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]: 'web',\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 session as invalid on 401\n if (e.response?.status === HttpStatuses.unauthorized) {\n this.tokenDeliveryManager.setSessionInvalid();\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 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 * Falls back to JSON mode if cookies are blocked\n */\n private detectCookieSupport(): void {\n // Only run in browser environment\n if (typeof document === 'undefined') {\n return;\n }\n\n try {\n // Test if cookies are enabled\n document.cookie = 'passflow_test=1; SameSite=Lax';\n const cookiesEnabled = document.cookie.indexOf('passflow_test=1') !== -1;\n document.cookie = 'passflow_test=; expires=Thu, 01 Jan 1970 00:00:00 UTC';\n\n if (!cookiesEnabled && this.tokenDeliveryManager.isCookieMode()) {\n // Cookies disabled but cookie mode requested - server will handle fallback\n // No action needed as server sends token_delivery header to switch modes\n }\n } catch (_error) {\n // Cookie detection failed (likely SSR or restrictive environment)\n // Silent fail - will attempt cookie mode anyway if server requests it\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 { APP_ID_HEADER_KEY, AUTHORIZATION_HEADER_KEY } from '../constants';\n\nimport { DeviceService } from '../device';\nimport { StorageManager } from '../storage';\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} 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 refreshToken(refreshToken: string, scopes: string[], accessToken?: string): Promise<PassflowAuthorizationResponse> {\n const payload = {\n access: accessToken,\n scopes,\n };\n\n return this.axiosClient.post<PassflowAuthorizationResponse, typeof payload>(PassflowEndpointPaths.refresh, payload, {\n headers: {\n [AUTHORIZATION_HEADER_KEY]: `Bearer ${refreshToken}`,\n },\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 { 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 * Begin 2FA method setup\n * POST /v2/user/2fa/methods/:method/setup/begin\n */\n beginMethodSetup(method: TwoFactorMethod): Promise<unknown> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorMethodSetupBegin, { method });\n return this.axiosClient.post<unknown, {}>(endpoint, {});\n }\n\n /**\n * Confirm 2FA method setup\n * POST /v2/user/2fa/methods/:method/setup/confirm\n */\n confirmMethodSetup(method: TwoFactorMethod, payload: unknown): Promise<unknown> {\n const endpoint = pathWithParams(PassflowEndpointPaths.TwoFactorMethodSetupConfirm, { method });\n return this.axiosClient.post<unknown, unknown>(endpoint, payload);\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","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","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 { startAuthentication, startRegistration } from '@simplewebauthn/browser';\nimport axios from 'axios';\nimport {\n AuthAPI,\n OS,\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} from '../api';\nimport { POPUP_HEIGHT, POPUP_POLL_INTERVAL_MS, POPUP_TIMEOUT_MS, POPUP_WIDTH } from '../constants';\nimport { DeviceService } from '../device';\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 { 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 origin: string,\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 ) {\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 = OS.web;\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 = OS.web;\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 async refreshToken(): Promise<PassflowAuthorizationResponse> {\n this.subscribeStore.notify(PassflowEvent.RefreshStart, {});\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 = await this.authApi.refreshToken('', this.scopes);\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 this.tokenDeliveryManager.setSessionInvalid();\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 const response = await this.authApi.refreshToken(tokens?.refresh_token ?? '', oldScopes, tokens?.access_token);\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 = axios.isAxiosError(error) && error.response?.status && error.response.status >= 400 && error.response.status < 500;\n\n if (is4xxError) {\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) {\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(window.location.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 this.subscribeStore.notify(PassflowEvent.RegisterStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = OS.web;\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 = btoa(publicKey.user.id);\n const webauthn = await startRegistration({\n optionsJSON: publicKey,\n });\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 this.subscribeStore.notify(PassflowEvent.SignInStart, {});\n const deviceId = this.deviceService.getDeviceId();\n const os = OS.web;\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 startAuthentication({\n optionsJSON: publicKey,\n });\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.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 federatedAuthWithPopup(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\n const popupWindow = window.open(passflowURL, '_blank', `width=${POPUP_WIDTH},height=${POPUP_HEIGHT}`);\n\n if (!popupWindow) {\n this.federatedAuthWithRedirect(payload);\n return;\n }\n\n const startTime = Date.now();\n\n const checkInterval = setInterval(() => {\n // Check if popup was closed by user\n if (popupWindow.closed) {\n clearInterval(checkInterval);\n const errorPayload: ErrorPayload = {\n message: 'Authentication popup was closed',\n code: 'POPUP_CLOSED',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return;\n }\n\n // Check for timeout\n if (Date.now() - startTime > POPUP_TIMEOUT_MS) {\n clearInterval(checkInterval);\n popupWindow.close();\n const errorPayload: ErrorPayload = {\n message: 'Authentication popup timed out',\n code: 'POPUP_TIMEOUT',\n };\n this.subscribeStore.notify(PassflowEvent.Error, errorPayload);\n return;\n }\n\n // Try to check popup URL (may throw cross-origin error)\n try {\n if (popupWindow.location.href.startsWith(this.origin)) {\n const urlParams = new URLSearchParams(popupWindow.location.search);\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 // Process response (may forward to BFF in BFF mode)\n this.processAuthResponse(tokensData, sscopes).then(() => {\n this.subscribeStore.notify(PassflowEvent.SignIn, {\n tokens: tokensData,\n parsedTokens: this.tokenCacheService.getParsedTokens(),\n });\n window.location.href = `${this.origin}`;\n });\n\n clearInterval(checkInterval);\n popupWindow.close();\n }\n } catch (_error) {\n // Expected cross-origin error - popup still on auth provider domain\n // Continue polling\n }\n }, POPUP_POLL_INTERVAL_MS);\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 window.location.href = 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 ?? window.location.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 window.location.href = 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 { 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 visibilityChangeHandler: (() => 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 ) {\n this.storageManager = storageManager;\n this.authApi = authApi;\n this.setupPageUnloadHandler();\n }\n\n initialize() {\n try {\n const tokens = this.storageManager.getTokens();\n if (!tokens) {\n this.startTokenCheck();\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 Page Visibility API listener\n this.setupVisibilityListener();\n\n this.checkInterval = setInterval(() => {\n // Skip check if page is hidden\n if (typeof document !== 'undefined' && document.hidden) {\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 setupVisibilityListener() {\n if (typeof document === 'undefined') return;\n\n // Remove previous listener if exists\n if (this.visibilityChangeHandler) {\n document.removeEventListener('visibilitychange', this.visibilityChangeHandler);\n }\n\n this.visibilityChangeHandler = () => {\n if (!document.hidden && this.checkInterval) {\n // Page became visible, do immediate check\n if (!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\n document.addEventListener('visibilitychange', this.visibilityChangeHandler);\n }\n\n private setupPageUnloadHandler() {\n if (typeof window === 'undefined') return;\n\n window.addEventListener('beforeunload', () => {\n this.destroy();\n });\n }\n\n private stopTokenCheck() {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n\n if (this.visibilityChangeHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityChangeHandler);\n this.visibilityChangeHandler = 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 }\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 * Begin 2FA method setup\n */\n async beginMethodSetup(method: TwoFactorMethod): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.beginMethodSetup(method);\n this.subscribeStore.notify(PassflowEvent.TwoFactorSetupStarted, { secret: '', method });\n return response;\n } catch (error) {\n this.emitErrorAndThrow(error, 'Begin 2FA method setup');\n }\n }\n\n /**\n * Confirm 2FA method setup\n */\n async confirmMethodSetup(method: TwoFactorMethod, payload: unknown): Promise<unknown> {\n try {\n const response = await this.twoFactorApi.confirmMethodSetup(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, 'Confirm 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 { startRegistration } from '@simplewebauthn/browser';\nimport { OS, PassflowSuccessResponse, UserAPI } from '../api';\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 ) {}\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 const deviceId = this.deviceService.getDeviceId();\n const os = OS.web;\n const { challenge_id, publicKey } = await this.userAPI.addUserPasskeyStart({\n relyingPartyId: relyingPartyId || window?.location?.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 = btoa(publicKey.user.id);\n const webauthn = await startRegistration({ optionsJSON: publicKey });\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","import {\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 UserAPI,\n} from './api';\nimport { DEFAULT_SCOPES, PASSFLOW_CLOUD_URL, SDK_VERSION } from './constants';\nimport { DeviceService } from './device';\nimport { AuthService, InvitationService, TenantService, TokenCacheService, TwoFactorService, UserService } from './services';\nimport { StorageManager } from './storage';\nimport { type ErrorPayload, PassflowEvent, PassflowStore, type PassflowSubscriber } from './store';\nimport { type TokenType } from './token';\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\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 // 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 origin = window.location.origin;\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\n // Initialize single StorageManager instance\n this.storageManager = new StorageManager({\n prefix: config.keyStoragePrefix ?? '',\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\n // Initialize PassflowStore\n this.subscribeStore = new PassflowStore();\n\n this.tokenCacheService = new TokenCacheService(this.storageManager, this.authApi, this.subscribeStore);\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.origin,\n this.url,\n {\n createSession: this.createSessionCallback,\n expiredSession: this.expiredSessionCallback,\n },\n this.appId ?? '',\n config.tokenExchange,\n );\n\n this.userService = new UserService(this.userApi, this.deviceService);\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 // 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\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 // 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 // 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(window.location.search);\n let fromHash = false;\n\n // If no access_token in query params, check hash\n if (!urlParams.get('access_token') && window.location.hash) {\n // Hash format: #access_token=xxx&refresh_token=yyy\n const hashParams = new URLSearchParams(window.location.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);\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);\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);\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);\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(window.location.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): void {\n if (fromHash) {\n // Clear hash completely - tokens should be removed from URL for security\n window.history.replaceState({}, document.title, window.location.pathname + window.location.search);\n } else {\n const urlParams = new URLSearchParams(window.location.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 window.history.replaceState({}, document.title, `${window.location.pathname}?${urlParams.toString()}`);\n } else {\n window.history.replaceState({}, document.title, window.location.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","RequestMethod","PassflowEndpointPaths","PassflowAdminEndpointPaths","PassflowError","error","Providers","OS","pathWithParams","template","params","result","value","TwoFactorPolicy","MAX_RETRIES","INITIAL_RETRY_DELAY_MS","AxiosClient","config","deviceService","appId","keyStoragePrefix","axios","axiosConfig","csrfToken","controller","response","e","endpoint","cookiesEnabled","payload","method","retryCount","delayMs","retryAfter","retryAfterNum","retryDate","resolve","status","errorData","path","options","data","AppAPI","AuthAPI","refreshToken","accessToken","os","defaultPayload","create_tenant","anonymous","isAdmin","newPassword","resetToken","passkeyData","challengeId","otp","headers","InvitationAPI","invitationID","SettingAPI","TenantAPI","name","tenantId","groupId","userId","role","roleId","limit","skip","inviteId","email","TwoFactorApiClient","tfa_token","code","recovery_code","backendResponse","methodId","UserAPI","passkeyId","relyingPartyId","passkeyDisplayName","passkeyUsername","PassflowEvent","PassflowStore","subscriber","events","eventSet","subscribedEvents","event","eventType","isValidJWTFormat","parts","base64UrlPattern","part","sanitizeErrorMessage","message","isValidEmail","trimmed","isValidPhoneNumber","phone","isValidUsername","username","isValidTOTPCode","digits","normalizeRecoveryCode","normalized","AuthService","authApi","subscribeStore","tokenCacheService","createTenantForNewUser","origin","sessionCallbacks","tokenExchangeConfig","errorPayload","oldScopes","is4xxError","sscopes","challenge_id","publicKey","webauthn","startRegistration","responseRegisterComplete","startAuthentication","responseAuthenticateComplete","passflowPathWithProvider","queryParams","passflowURL","popupWindow","startTime","checkInterval","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","parsed","UserService","userAPI","_Passflow","createSession","expiredSession","fromHash","hashParams","sanitized","Passflow","M2MError","errorResponse","rateLimitInfo","remaining","reset","now","waitSeconds","str","M2MNetworkError","cause","M2MTokenParseError","M2MConfigError","M2MErrorCodes","M2M_DEFAULTS","InMemoryCache","entry","ttl","defaultRetryStrategy","attempt","M2MClient","audience","scopeKey","audienceKey","cacheKey","cached","body","timeoutId","fn","lastError","delay","ms","cache","threshold","expiresAt","claims","revokeEndpoint"],"mappings":"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"}
|