UNPKG

@authava/client

Version:

Client library for seamless integration with Authava's white-label authentication service

1,010 lines (1,005 loc) 31.7 kB
// src/api/base.ts var BaseApiClient = class { config; constructor(config) { this.config = config; } /** * Make a request to the Authava API */ async request(endpoint, method, data, options = {}) { const protocol = this.config.secure ? "https" : "http"; const url = `${protocol}://${this.config.resolverDomain}${endpoint}`; const defaultHeaders = { "Content-Type": "application/json", Accept: "application/json", host: this.config.domain }; const configCustomHeaders = typeof this.config.customHeaders === "function" ? this.config.customHeaders() : this.config.customHeaders; const headers = { ...defaultHeaders, ...configCustomHeaders, ...options.headers }; const hasSessionCookie = typeof document !== "undefined" && document.cookie.includes("session="); const tokenFromStorage = typeof localStorage !== "undefined" ? localStorage.getItem("session_token") : null; if (!hasSessionCookie && tokenFromStorage && !headers["Authorization"]) { headers["Authorization"] = `Bearer ${tokenFromStorage}`; } const requestOptions = { method, headers, credentials: options.credentials || "include" }; if (method !== "GET" && data !== void 0) { requestOptions.body = JSON.stringify(data); } try { const response = await fetch(url, requestOptions); const contentType = response.headers.get("content-type"); const isJson = contentType && contentType.includes("application/json"); if (response.status === 204 || response.status === 200 && response.headers.get("content-length") === "0") { return {}; } const responseData = isJson ? await response.json() : await response.text(); if (!response.ok) { const errorResponse = responseData; this.logError(`API error (${response.status})`, errorResponse.error); return errorResponse; } return responseData; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; this.logError("API request failed", error); return { error: errorMessage }; } } /** * Log an error if debug mode is enabled */ logError(message, error) { if (this.config.debug) { console.error(`[AuthavaClient] ${message}:`, error); } } /** * Log a message if debug mode is enabled */ log(...args) { if (this.config.debug) { console.log("[AuthavaClient]", ...args); } } }; // src/api/auth.ts var AuthApi = class extends BaseApiClient { /** * Login with email and password */ async login(data) { const res = await this.request("/login", "POST", data); return toAuthavaResult(res); } /** * Register a new user */ async register(data) { const res = await this.request("/register", "POST", data); return toAuthavaResult(res); } /** * Get the current session */ async getSession() { const res = await this.request("/session", "GET"); return toAuthavaResult(res); } /** * Logout the current user */ async logout() { const res = await this.request("/logout", "POST"); return toAuthavaResult(res); } /** * Request a password reset */ async forgotPassword(data) { const res = await this.request("/forgot-password", "POST", data); return toAuthavaResult(res); } /** * Reset password with token */ async resetPassword(data) { const res = await this.request("/reset-password", "POST", data); return toAuthavaResult(res); } /** * Verify MFA code */ async verifyMfa(data) { const res = await this.request("/verify-mfa", "POST", data); return toAuthavaResult(res); } /** * Send MFA verification email */ async sendMfaEmail(data) { const res = await this.request("/send-mfa-email", "POST", data); return toAuthavaResult(res); } }; // src/api/profile.ts var ProfileApi = class extends BaseApiClient { /** * Get user profile information */ async getProfile() { return this.request("/v1/profile", "GET"); } /** * Change email address */ async changeEmail(data) { return this.request("/v1/profile/email", "PUT", data); } /** * Change password */ async changePassword(data) { return this.request("/v1/profile/password", "PUT", data); } /** * Update notification preferences */ async updateNotificationPreferences(data) { return this.request("/v1/profile/notifications", "PUT", data); } /** * Remove an MFA method */ async removeMfaMethod(methodId) { return this.request(`/v1/profile/mfa/${methodId}`, "DELETE"); } }; // src/api/mfa.ts var MfaApi = class extends BaseApiClient { /** * Initialize Email MFA setup */ async setupEmailMfa(data) { const res = await this.request("/v1/profile/mfa/email/setup", "POST", data); return toAuthavaResult(res); } /** * Verify and activate Email MFA */ async verifyEmailMfa(data) { const res = await this.request( "/v1/profile/mfa/email/verify", "POST", data ); return toAuthavaResult(res); } /** * Initialize TOTP MFA setup */ async setupTotp(data) { const res = await this.request("/v1/profile/mfa/totp/setup", "POST", data); return toAuthavaResult(res); } /** * Verify and activate TOTP MFA */ async verifyTotp(data) { const res = await this.request("/v1/profile/mfa/totp/verify", "POST", data); return toAuthavaResult(res); } }; // src/client.ts function toAuthavaResult(response) { return response && typeof response === "object" && "error" in response ? { success: false, error: response.error.toString() } : { success: true, data: response }; } var AuthavaClient = class { config; refreshTimer; refreshing = false; currentState; subscribers = /* @__PURE__ */ new Set(); broadcastChannel; autoRefreshConfig; // API clients authApi; profileApi; mfaApi; constructor(config) { const resolverDomain = config.resolverDomain || config.domain; this.config = { domain: config.domain, resolverDomain, secure: config.secure ?? true, autoRefresh: config.autoRefresh ?? true, refreshBuffer: config.refreshBuffer ?? 5, customHeaders: config.customHeaders ?? {}, debug: config.debug ?? false }; this.autoRefreshConfig = { enabled: this.config.autoRefresh, refreshBuffer: this.config.refreshBuffer, maxRetries: 3, retryDelay: 1e3 }; this.currentState = { status: "expired", user: null }; this.authApi = new AuthApi(this.config); this.profileApi = new ProfileApi(this.config); this.mfaApi = new MfaApi(this.config); if (typeof BroadcastChannel !== "undefined") { this.broadcastChannel = new BroadcastChannel("authava-session"); this.broadcastChannel.onmessage = (event) => { this.handleSessionBroadcast(event.data); }; } this.log("Initialized with config:", this.config); this.checkSession(); } hasRoles(roles) { if (!this.currentState.user || !Array.isArray(this.currentState.user.roles)) return false; const required = Array.isArray(roles) ? roles : [roles]; return required.every((r) => this.currentState.user.roles.includes(r)); } hasPermissions(permissions) { if (!this.currentState.user || !Array.isArray(this.currentState.user.permissions)) return false; const required = Array.isArray(permissions) ? permissions : [permissions]; return required.every((p) => this.currentState.user.permissions.includes(p)); } async hasScopes({ resource_type, resource_id, actions, resolver }) { const user = this.currentState.user; if (!user || !Array.isArray(user.teams)) return false; for (const team of user.teams) { if (!Array.isArray(team.scopes)) continue; const results = await Promise.all( actions.map( (action) => this.teamHasMatchingScope( team.scopes, resource_type, resource_id, [action], team, resolver ) ) ); if (results.every(Boolean)) { return true; } } return false; } async teamHasMatchingScope(scopes, resource_type, resource_id, actions, team, resolver) { for (const scope of scopes) { if (scope.resource_type !== resource_type) continue; if (!actions.includes(scope.action)) continue; if (scope.resource_id === resource_id) return true; if (scope.resource_id !== "*") continue; if (!resolver) continue; try { const allowed = await resolver(resource_type, team); if (allowed.includes(resource_id)) return true; } catch { continue; } } return false; } encodeBase64Url(input) { return btoa(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } setLogoutReason(reason) { if (typeof document === "undefined") return; document.cookie = `logout_reason=${encodeURIComponent(reason)}; path=/; SameSite=None; Secure`; } // Private method for logging log(...args) { if (this.config.debug) { console.log("[AuthavaClient]", ...args); } } /** * Log the user out. * Clears the session cookie, updates the state, and redirects the user. */ async logout(reason) { this.log("Logging out user."); await this.authApi.logout(); if (typeof document !== "undefined") { document.cookie = "session=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; } this.updateState({ status: "expired", user: null }); if (typeof window === "undefined") { this.log("Skipping redirect, window is not defined (SSR or backend)."); return; } const defaultRedirect = `${this.config.secure ? "https" : "http"}://${this.config.domain}`; const redirectUrl = localStorage.getItem("redirect_after_logout") || defaultRedirect; localStorage.removeItem("redirect_after_logout"); const reasonParam = reason ? `reason=${encodeURIComponent(reason)}` : ""; const currentUrl = window?.location?.href || ""; const nextParam = currentUrl ? `next=${this.encodeBase64Url(currentUrl)}` : ""; const queryParams = [reasonParam, nextParam].filter(Boolean).join("&"); const finalRedirect = queryParams ? `${redirectUrl}?${queryParams}` : redirectUrl; window.location.href = finalRedirect; } getLogoutReason() { if (typeof document === "undefined") return null; const match = document.cookie.match(/(?:^|; )logout_reason=([^;]*)/); return match ? decodeURIComponent(match[1]) : null; } /** * Get the current session if it exists. * If a session error occurs (including CORS errors), logs out the user. */ fetchFailureCount = 0; getTokenFromStorage() { if (typeof localStorage === "undefined") return null; const stored = localStorage.getItem("session_token"); if (stored) return stored; if (typeof window !== "undefined" && window.location?.href) { try { const url = new URL(window.location.href); const urlToken = url.searchParams.get("token"); if (urlToken) { localStorage.setItem("session_token", urlToken); return urlToken; } } catch (e) { } } return null; } async getSession(extraHeaders) { try { const protocol = this.config.secure ? "https" : "http"; const defaultHeaders = { Accept: "application/json", host: this.config.domain }; const tokenFromCookie = this.getTokenFromCookie(); const tokenFromStorage = this.getTokenFromStorage(); if (!tokenFromCookie && tokenFromStorage) { defaultHeaders.Authorization = `Bearer ${tokenFromStorage}`; } const configCustom = (typeof this.config.customHeaders === "function" ? this.config.customHeaders() : this.config.customHeaders) || {}; const headers = { ...defaultHeaders, ...configCustom, ...extraHeaders }; const url = `${protocol}://${this.config.resolverDomain}/session`; this.log("getSession request to:", url, "with headers:", headers); const response = await fetch(url, { method: "POST", credentials: "include", headers }); if (!response.ok) { if (response.status === 401) { this.log("Session expired or unauthorized"); this.setLogoutReason("unauthorized"); await this.logout("unauthorized"); return null; } throw new Error(`Session check failed: ${response.statusText}`); } const session = await response.json(); if (!session || !session.user || !session.user.id || !session.user.email) { throw new Error("Invalid session data received"); } this.fetchFailureCount = 0; this.updateState({ status: "valid", user: session.user }); this.log("Session retrieved successfully:", session); return session; } catch (error) { this.log("getSession error:", error); const isNetworkError = error instanceof TypeError && error.message.includes("Failed to fetch"); if (isNetworkError) { this.fetchFailureCount++; this.log(`\u274C Network error #${this.fetchFailureCount}:`, error); if (this.fetchFailureCount >= 3) { this.log("\u{1F6A8} Logging out due to repeated fetch failures."); this.setLogoutReason("cors"); await this.logout("cors"); } else { this.updateState({ status: "error", error }); } } else { this.updateState({ status: "error", error }); } return null; } } /** * Subscribe to session changes. * Returns an unsubscribe function. */ onSessionChange(callback) { this.subscribers.add(callback); callback(this.currentState); return () => { this.subscribers.delete(callback); }; } async checkSession() { const session = await this.getSession(); if (session && this.autoRefreshConfig.enabled) { const token = this.getTokenFromCookie(); if (token) { const expiresAt = this.getTokenExpiration(token); if (expiresAt) { this.startAutoRefresh(expiresAt); } } } } getTokenFromCookie() { if (typeof document === "undefined") return null; const cookieMatch = document.cookie.match(/(?:^|; )session=([^;]*)/); return cookieMatch ? decodeURIComponent(cookieMatch[1]) : null; } getTokenExpiration(token) { try { const payload = JSON.parse(atob(token.split(".")[1])); return new Date(payload.exp * 1e3); } catch { return null; } } async startAutoRefresh(expiresAt) { if (!this.autoRefreshConfig.enabled) return; const buffer = this.autoRefreshConfig.refreshBuffer * 60 * 1e3; const refreshTime = new Date(expiresAt.getTime() - buffer); if (this.refreshTimer) { clearTimeout(this.refreshTimer); } const delay = Math.max(0, refreshTime.getTime() - Date.now()); this.refreshTimer = setTimeout(() => this.refreshSession(), delay); } async refreshSession(retryCount = 0) { if (this.refreshing) return; try { this.refreshing = true; this.updateState({ status: "refreshing" }); const session = await this.getSession(); if (!session) { await this.logout(); return; } const token = this.getTokenFromCookie(); if (!token) { return; } const expiresAt = this.getTokenExpiration(token); if (expiresAt) { this.updateState({ status: "valid", user: session.user, expiresAt }); this.startAutoRefresh(expiresAt); } } catch (error) { if (retryCount < this.autoRefreshConfig.maxRetries) { const delay = this.autoRefreshConfig.retryDelay * Math.pow(2, retryCount); await new Promise((resolve) => setTimeout(resolve, delay)); return this.refreshSession(retryCount + 1); } this.updateState({ status: "error", error }); } finally { this.refreshing = false; } } updateState(newState) { this.currentState = { ...this.currentState, ...newState }; this.notifySubscribers(); if (this.broadcastChannel) { this.broadcastChannel.postMessage(this.currentState); } } notifySubscribers() { for (const callback of this.subscribers) { try { callback(this.currentState); } catch (error) { console.error("Error in session change callback:", error); } } } handleSessionBroadcast(state) { if (JSON.stringify(state) !== JSON.stringify(this.currentState)) { this.currentState = state; this.notifySubscribers(); } } // ===== Authentication Methods ===== /** * Login with email and password */ async login(data) { const response = await this.authApi.login(data); if (!response.success) { return response; } this.updateState({ status: "valid", user: response.data.user }); const token = this.getTokenFromCookie(); if (token && this.autoRefreshConfig.enabled) { const expiresAt = this.getTokenExpiration(token); if (expiresAt) { this.startAutoRefresh(expiresAt); } } return response; } /** * Register a new user */ async register(data) { return this.authApi.register(data); } /** * Request a password reset */ async forgotPassword(data) { return this.authApi.forgotPassword(data); } /** * Reset password with token */ async resetPassword(data) { return this.authApi.resetPassword(data); } /** * Verify MFA code */ async verifyMfa(data) { const response = await this.authApi.verifyMfa(data); if (response.success) { this.updateState({ status: "valid", user: response.data.user }); } return response; } /** * Send MFA verification email */ async sendMfaEmail(data) { return this.authApi.sendMfaEmail(data); } // ===== Profile Methods ===== /** * Get user profile information */ async getProfile() { const res = await this.profileApi.getProfile(); return toAuthavaResult(res); } /** * Change email address */ async changeEmail(data) { const res = await this.profileApi.changeEmail(data); return toAuthavaResult(res); } /** * Change password */ async changePassword(data) { const res = await this.profileApi.changePassword(data); return toAuthavaResult(res); } /** * Update notification preferences */ async updateNotificationPreferences(data) { const res = await this.profileApi.updateNotificationPreferences(data); return toAuthavaResult(res); } /** * Remove an MFA method */ async removeMfaMethod(methodId) { const res = await this.profileApi.removeMfaMethod(methodId); return toAuthavaResult(res); } // ===== MFA Methods ===== /** * Initialize Email MFA setup */ async setupEmailMfa(data) { return this.mfaApi.setupEmailMfa(data); } /** * Verify and activate Email MFA */ async verifyEmailMfa(data) { return this.mfaApi.verifyEmailMfa(data); } /** * Initialize TOTP MFA setup */ async setupTotp(data) { return this.mfaApi.setupTotp(data); } /** * Verify and activate TOTP MFA */ async verifyTotp(data) { return this.mfaApi.verifyTotp(data); } }; // src/mock.ts var MockAuthavaClient = class { currentState; subscribers = /* @__PURE__ */ new Set(); config; mockMfaMethods = []; mockUser = null; constructor(config) { this.config = config || {}; this.mockUser = config?.mockUser || null; this.currentState = this.mockUser ? { status: "valid", user: this.mockUser } : { status: "expired", user: null }; if (this.mockUser) { this.log("Initialized with mock user:", this.mockUser); } if (config?.debug) { console.log("[MockAuthavaClient] Initialized"); } } // Helper method to create a successful API response createSuccessResponse(data) { return { data, success: true }; } // Helper method for void responses createVoidSuccessResponse() { return { success: true, data: void 0 }; } // Helper method to create an error API response createErrorResponse(message, code = "400", status = 400) { return { success: false, error: message, code }; } // Helper method to log debug messages log(...args) { if (this.config.debug) { console.log("[MockAuthavaClient]", ...args); } } // ===== Session Management ===== async getSession() { return this.currentState.status === "valid" ? { user: this.currentState.user, redirect_url: "https://dashboard.example.com", authority: "auth.example.com", tenant_id: "mock-tenant-id" } : null; } setSession(session) { this.currentState = { status: "valid", user: session.user }; this.notifySubscribers(); } expireSession() { this.currentState = { status: "expired", user: null }; this.notifySubscribers(); } onSessionChange(callback) { this.subscribers.add(callback); callback(this.currentState); return () => { this.subscribers.delete(callback); }; } notifySubscribers() { for (const callback of this.subscribers) { try { callback(this.currentState); } catch (error) { console.error("MockAuthavaClient callback error:", error); } } } async logout() { this.expireSession(); const domain = this.config.domain || "example.com"; const secure = this.config.secure !== false; const defaultRedirect = `${secure ? "https" : "http"}://${domain}`; const redirectUrl = defaultRedirect; if (typeof window !== "undefined") { window.location.href = redirectUrl; } else if (this.config.debug) { this.log(`Would redirect to: ${redirectUrl}`); } return this.createVoidSuccessResponse(); } // ===== Authentication Methods ===== async login(data) { this.log("Mock login with:", data); if (!data.email || !data.password) { return this.createErrorResponse("Email and password are required"); } const user = { id: "mock-user-id", email: data.email, roles: [], permissions: [], teams: [], last_login_at: (/* @__PURE__ */ new Date()).toISOString() }; const response = { user, redirect_url: "https://dashboard.example.com", authority: "auth.example.com", tenant_id: "mock-tenant-id" }; this.setSession({ user, redirect_url: response.redirect_url, authority: "auth.example.com", tenant_id: "mock-tenant-id" }); return this.createSuccessResponse(response); } async register(data) { this.log("Mock register with:", data); if (!data.email || !data.password) { return this.createErrorResponse("Email and password are required"); } return this.createVoidSuccessResponse(); } async forgotPassword(data) { this.log("Mock forgot password with:", data); if (!data.email) { return this.createErrorResponse("Email is required"); } return this.createVoidSuccessResponse(); } async resetPassword(data) { this.log("Mock reset password with:", data); if (!data.token || !data.password) { return this.createErrorResponse("Token and password are required"); } return this.createVoidSuccessResponse(); } async verifyMfa(data) { this.log("Mock verify MFA with:", data); if (!data.session_id || !data.method_id || !data.code) { return this.createErrorResponse("Session ID, method ID, and code are required"); } const user = { id: "mock-user-id", email: "mock@example.com", roles: [], permissions: [], teams: [], last_login_at: (/* @__PURE__ */ new Date()).toISOString() }; const response = { user, redirect_url: "https://dashboard.example.com", authority: "auth.example.com", tenant_id: "mock-tenant-id" }; this.setSession({ user, redirect_url: response.redirect_url, authority: "auth.example.com", tenant_id: "mock-tenant-id" }); return this.createSuccessResponse(response); } async sendMfaEmail(data) { this.log("Mock send MFA email with:", data); if (!data.session_id || !data.method_id) { return this.createErrorResponse("Session ID and method ID are required"); } return this.createVoidSuccessResponse(); } // ===== Profile Methods ===== async getProfile() { this.log("Mock get profile"); if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } if (!this.mockUser) { this.mockUser = { id: this.currentState.user.id, email: this.currentState.user.email, status: "active", email_verified: true, roles: [], permissions: [], teams: [], created_at: new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString(), // 30 days ago updated_at: (/* @__PURE__ */ new Date()).toISOString(), last_login_at: (/* @__PURE__ */ new Date()).toISOString() }; } const response = { user: this.mockUser, mfa_methods: this.mockMfaMethods, notification_preferences: { user_id: this.mockUser.id, security_alerts: true, account_activity: true, product_updates: false, marketing_emails: false, created_at: this.mockUser.created_at, updated_at: this.mockUser.updated_at }, recent_security_events: [ { id: "mock-event-1", user_id: this.mockUser.id, event_type: "login", created_at: (/* @__PURE__ */ new Date()).toISOString(), ip_address: "127.0.0.1", user_agent: "Mock Browser" } ] }; return this.createSuccessResponse(response); } async changeEmail(data) { this.log("Mock change email with:", data); if (!data.new_email || !data.password) { return this.createErrorResponse("New email and password are required"); } if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } if (this.mockUser) { this.mockUser.email = data.new_email; this.mockUser.updated_at = (/* @__PURE__ */ new Date()).toISOString(); } const updatedUser = { ...this.currentState.user, email: data.new_email }; this.setSession({ user: updatedUser, redirect_url: "https://dashboard.example.com", authority: "auth.example.com", tenant_id: "mock-tenant-id" }); return this.createVoidSuccessResponse(); } async changePassword(data) { this.log("Mock change password with:", data); if (!data.current_password || !data.new_password) { return this.createErrorResponse("Current password and new password are required"); } if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } if (this.mockUser) { this.mockUser.updated_at = (/* @__PURE__ */ new Date()).toISOString(); } return this.createVoidSuccessResponse(); } async updateNotificationPreferences(data) { this.log("Mock update notification preferences with:", data); if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } const response = { user_id: this.currentState.user.id, security_alerts: data.security_alerts, account_activity: data.account_activity, product_updates: data.product_updates, marketing_emails: data.marketing_emails, created_at: this.mockUser?.created_at || new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }; return this.createSuccessResponse(response); } async removeMfaMethod(methodId) { this.log("Mock remove MFA method:", methodId); if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } this.mockMfaMethods = this.mockMfaMethods.filter((method) => method.id !== methodId); return this.createVoidSuccessResponse(); } // ===== MFA Methods ===== async setupEmailMfa(data) { this.log("Mock setup email MFA with:", data); if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } const methodId = `mock-email-mfa-${Date.now()}`; this.mockMfaMethods.push({ id: methodId, mfa_type: "email", verified: false, name: data.name || "Email MFA", created_at: (/* @__PURE__ */ new Date()).toISOString() }); return this.createVoidSuccessResponse(); } async verifyEmailMfa(data) { this.log("Mock verify email MFA with:", data); if (!data.code) { return this.createErrorResponse("Verification code is required"); } if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } const emailMethod = this.mockMfaMethods.find((m) => m.mfa_type === "email" && !m.verified); if (emailMethod) { emailMethod.verified = true; emailMethod.last_used_at = (/* @__PURE__ */ new Date()).toISOString(); } const backupCodes = Array.from( { length: 10 }, (_, i) => `MOCK-${i.toString().padStart(4, "0")}` ); return this.createSuccessResponse({ codes: backupCodes }); } async setupTotp(data) { this.log("Mock setup TOTP with:", data); if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } const methodId = `mock-totp-mfa-${Date.now()}`; this.mockMfaMethods.push({ id: methodId, mfa_type: "totp", verified: false, name: data.name || "TOTP MFA", created_at: (/* @__PURE__ */ new Date()).toISOString() }); const response = { secret: "MOCKSECRETKEY123456", qr_code: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", method_id: methodId }; return this.createSuccessResponse(response); } async verifyTotp(data) { this.log("Mock verify TOTP with:", data); if (!data.method_id || !data.code) { return this.createErrorResponse("Method ID and code are required"); } if (this.currentState.status !== "valid" || !this.currentState.user) { return this.createErrorResponse("Not authenticated", "401", 401); } const method = this.mockMfaMethods.find((m) => m.id === data.method_id); if (method) { method.verified = true; method.last_used_at = (/* @__PURE__ */ new Date()).toISOString(); } else { return this.createErrorResponse("MFA method not found", "404", 404); } const backupCodes = Array.from( { length: 10 }, (_, i) => `MOCK-${i.toString().padStart(4, "0")}` ); return this.createSuccessResponse({ codes: backupCodes }); } }; export { AuthavaClient, MockAuthavaClient }; //# sourceMappingURL=index.js.map