@nauth-toolkit/client 0.1.123 → 0.1.125
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/auth.types.ts","../src/types/error.types.ts","../src/types/audit.types.ts","../src/core/config.ts","../src/core/errors.ts","../src/core/refresh.ts","../src/storage/browser.ts","../src/storage/memory.ts","../src/core/events.ts","../src/adapters/fetch-adapter.ts","../src/core/challenge-router.ts","../src/core/admin-operations.ts","../src/core/client.ts","../src/core/challenge-helpers.ts"],"sourcesContent":["export * from './types';\nexport * from './core/client';\nexport * from './core/config';\nexport * from './core/errors';\nexport * from './core/events';\nexport * from './core/http-adapter';\nexport * from './core/challenge-helpers';\nexport * from './core/challenge-router';\nexport * from './core/admin-operations';\nexport * from './storage/interface';\nexport * from './storage/browser';\nexport * from './storage/memory';\nexport * from './adapters/fetch-adapter';\n","/**\n * Authentication challenge types returned by the backend.\n */\nexport enum AuthChallenge {\n VERIFY_EMAIL = 'VERIFY_EMAIL',\n VERIFY_PHONE = 'VERIFY_PHONE',\n MFA_REQUIRED = 'MFA_REQUIRED',\n MFA_SETUP_REQUIRED = 'MFA_SETUP_REQUIRED',\n FORCE_CHANGE_PASSWORD = 'FORCE_CHANGE_PASSWORD',\n}\n\n/**\n * Auth response returned by backend for all auth operations.\n * Either contains tokens/user or a challenge to complete.\n */\nimport { MFAMethod, MFAChallengeMethod } from './mfa.types';\n\nexport interface AuthResponse {\n user?: AuthUserSummary;\n accessToken?: string;\n refreshToken?: string;\n accessTokenExpiresAt?: number;\n refreshTokenExpiresAt?: number;\n /**\n * Authentication method used to create the current session.\n *\n * Examples:\n * - `password`\n * - `google`\n * - `apple`\n * - `facebook`\n */\n authMethod?: string;\n trusted?: boolean;\n deviceToken?: string;\n challengeName?: AuthChallenge;\n session?: string;\n challengeParameters?: Record<string, unknown>;\n sub?: string;\n}\n\n/**\n * Minimal user information returned inside auth responses.\n */\nexport interface AuthUserSummary {\n sub: string;\n email: string;\n firstName?: string | null;\n lastName?: string | null;\n phone?: string | null;\n isEmailVerified: boolean;\n isPhoneVerified?: boolean;\n socialProviders?: string[] | null;\n hasPasswordHash?: boolean;\n}\n\n/**\n * Token response returned by refresh endpoint.\n */\nexport interface TokenResponse {\n accessToken: string;\n refreshToken: string;\n accessTokenExpiresAt: number;\n refreshTokenExpiresAt: number;\n}\n\n/**\n * Signup request payload.\n */\nexport interface SignupRequest {\n email: string;\n password: string;\n firstName?: string;\n lastName?: string;\n phone?: string;\n metadata?: Record<string, unknown>;\n /**\n * Optional reCAPTCHA token for bot protection.\n * - v2: Token from visible checkbox challenge\n * - v3: Token from invisible/automatic challenge\n * - If required by server but not provided, request will fail with RECAPTCHA_REQUIRED error\n */\n recaptchaToken?: string;\n}\n\n/**\n * Login request payload.\n */\nexport interface LoginRequest {\n identifier: string;\n password: string;\n /**\n * Optional reCAPTCHA token for bot protection.\n * - v2: Token from visible checkbox challenge\n * - v3: Token from invisible/automatic challenge\n * - If required by server but not provided, request will fail with RECAPTCHA_REQUIRED error\n */\n recaptchaToken?: string;\n}\n\n/**\n * Logout request payload.\n */\nexport interface LogoutRequest {\n forgetMe?: boolean;\n}\n\n/**\n * Logout all sessions request payload.\n */\nexport interface LogoutAllRequest {\n /**\n * Whether to also revoke all trusted devices\n * Default: false (devices remain trusted)\n */\n forgetDevices?: boolean;\n}\n\n/**\n * Resend code request payload.\n */\nexport interface ResendCodeRequest {\n session: string;\n}\n\n/**\n * MFA setup data request payload during challenge flows.\n */\nexport interface GetSetupDataRequest {\n session: string;\n method: MFAMethod;\n}\n\n/**\n * Challenge data request payload (e.g., passkey options).\n */\nexport interface GetChallengeDataRequest {\n session: string;\n method: MFAChallengeMethod;\n}\n\n/**\n * Unified challenge response discriminated union.\n */\nexport type ChallengeResponse =\n | VerifyEmailResponse\n | VerifyPhoneCollectResponse\n | VerifyPhoneCodeResponse\n | MFACodeResponse\n | MFAPasskeyResponse\n | MFASetupResponse\n | ForceChangePasswordResponse;\n\n/**\n * Base challenge response shape.\n */\nexport interface BaseChallengeResponse {\n session: string;\n}\n\nexport interface VerifyEmailResponse extends BaseChallengeResponse {\n type: AuthChallenge.VERIFY_EMAIL;\n code: string;\n}\n\nexport interface VerifyPhoneCollectResponse extends BaseChallengeResponse {\n type: AuthChallenge.VERIFY_PHONE;\n phone: string;\n}\n\nexport interface VerifyPhoneCodeResponse extends BaseChallengeResponse {\n type: AuthChallenge.VERIFY_PHONE;\n code: string;\n}\n\nexport interface MFACodeResponse extends BaseChallengeResponse {\n type: AuthChallenge.MFA_REQUIRED;\n method: MFAMethod;\n code: string;\n deviceId?: number;\n}\n\nexport interface MFAPasskeyResponse extends BaseChallengeResponse {\n type: AuthChallenge.MFA_REQUIRED;\n method: 'passkey';\n credential: Record<string, unknown>;\n deviceId?: number;\n}\n\nexport interface MFASetupResponse extends BaseChallengeResponse {\n type: AuthChallenge.MFA_SETUP_REQUIRED;\n method: MFAMethod;\n setupData: Record<string, unknown>;\n}\n\nexport interface ForceChangePasswordResponse extends BaseChallengeResponse {\n type: AuthChallenge.FORCE_CHANGE_PASSWORD;\n newPassword: string;\n}\n\n// MFAMethod and MFAChallengeMethod are imported from mfa.types\n","/**\n * Standardized error codes mirroring backend AuthErrorCode.\n */\nexport enum NAuthErrorCode {\n // Authentication Errors\n AUTH_INVALID_CREDENTIALS = 'AUTH_INVALID_CREDENTIALS',\n AUTH_ACCOUNT_LOCKED = 'AUTH_ACCOUNT_LOCKED',\n AUTH_ACCOUNT_INACTIVE = 'AUTH_ACCOUNT_INACTIVE',\n AUTH_TOKEN_EXPIRED = 'AUTH_TOKEN_EXPIRED',\n AUTH_TOKEN_INVALID = 'AUTH_TOKEN_INVALID',\n AUTH_BEARER_NOT_ALLOWED = 'AUTH_BEARER_NOT_ALLOWED',\n AUTH_COOKIES_NOT_ALLOWED = 'AUTH_COOKIES_NOT_ALLOWED',\n AUTH_CSRF_TOKEN_INVALID = 'AUTH_CSRF_TOKEN_INVALID',\n AUTH_CSRF_TOKEN_MISSING = 'AUTH_CSRF_TOKEN_MISSING',\n AUTH_TOKEN_REUSE_DETECTED = 'AUTH_TOKEN_REUSE_DETECTED',\n AUTH_SESSION_NOT_FOUND = 'AUTH_SESSION_NOT_FOUND',\n AUTH_SESSION_EXPIRED = 'AUTH_SESSION_EXPIRED',\n\n // Signup Errors\n SIGNUP_DISABLED = 'SIGNUP_DISABLED',\n SIGNUP_EMAIL_EXISTS = 'SIGNUP_EMAIL_EXISTS',\n SIGNUP_USERNAME_EXISTS = 'SIGNUP_USERNAME_EXISTS',\n SIGNUP_PHONE_EXISTS = 'SIGNUP_PHONE_EXISTS',\n SIGNUP_WEAK_PASSWORD = 'SIGNUP_WEAK_PASSWORD',\n SIGNUP_PHONE_REQUIRED = 'SIGNUP_PHONE_REQUIRED',\n SIGNUP_NOT_ALLOWED = 'SIGNUP_NOT_ALLOWED',\n\n // Verification Errors\n VERIFY_CODE_INVALID = 'VERIFY_CODE_INVALID',\n VERIFY_CODE_EXPIRED = 'VERIFY_CODE_EXPIRED',\n VERIFY_TOO_MANY_ATTEMPTS = 'VERIFY_TOO_MANY_ATTEMPTS',\n VERIFY_ALREADY_VERIFIED = 'VERIFY_ALREADY_VERIFIED',\n\n // MFA Errors\n MFA_SETUP_REQUIRED = 'MFA_SETUP_REQUIRED',\n\n // Rate Limit Errors\n RATE_LIMIT_SMS = 'RATE_LIMIT_SMS',\n RATE_LIMIT_EMAIL = 'RATE_LIMIT_EMAIL',\n RATE_LIMIT_LOGIN = 'RATE_LIMIT_LOGIN',\n RATE_LIMIT_RESEND = 'RATE_LIMIT_RESEND',\n\n // Social Auth Errors\n SOCIAL_TOKEN_INVALID = 'SOCIAL_TOKEN_INVALID',\n SOCIAL_ACCOUNT_LINKED = 'SOCIAL_ACCOUNT_LINKED',\n SOCIAL_CONFIG_MISSING = 'SOCIAL_CONFIG_MISSING',\n SOCIAL_EMAIL_REQUIRED = 'SOCIAL_EMAIL_REQUIRED',\n SOCIAL_ACCOUNT_NOT_FOUND = 'SOCIAL_ACCOUNT_NOT_FOUND',\n\n // Challenge Errors\n CHALLENGE_EXPIRED = 'CHALLENGE_EXPIRED',\n CHALLENGE_INVALID = 'CHALLENGE_INVALID',\n CHALLENGE_TYPE_MISMATCH = 'CHALLENGE_TYPE_MISMATCH',\n CHALLENGE_MAX_ATTEMPTS = 'CHALLENGE_MAX_ATTEMPTS',\n CHALLENGE_ALREADY_COMPLETED = 'CHALLENGE_ALREADY_COMPLETED',\n\n // Validation Errors\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n VALIDATION_INVALID_PHONE = 'VALIDATION_INVALID_PHONE',\n VALIDATION_INVALID_EMAIL = 'VALIDATION_INVALID_EMAIL',\n VALIDATION_INVALID_PASSWORD = 'VALIDATION_INVALID_PASSWORD',\n\n // Password Errors\n PASSWORD_INCORRECT = 'PASSWORD_INCORRECT',\n PASSWORD_REUSED = 'PASSWORD_REUSED',\n PASSWORD_CHANGE_NOT_ALLOWED = 'PASSWORD_CHANGE_NOT_ALLOWED',\n WEAK_PASSWORD = 'SIGNUP_WEAK_PASSWORD',\n PASSWORD_RESET_CODE_INVALID = 'PASSWORD_RESET_CODE_INVALID',\n PASSWORD_RESET_CODE_EXPIRED = 'PASSWORD_RESET_CODE_EXPIRED',\n PASSWORD_RESET_MAX_ATTEMPTS = 'PASSWORD_RESET_MAX_ATTEMPTS',\n RATE_LIMIT_PASSWORD_RESET = 'RATE_LIMIT_PASSWORD_RESET',\n\n // Adaptive MFA\n SIGNIN_BLOCKED_HIGH_RISK = 'SIGNIN_BLOCKED_HIGH_RISK',\n\n // General Errors\n RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',\n FORBIDDEN = 'FORBIDDEN',\n INTERNAL_ERROR = 'INTERNAL_ERROR',\n SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE',\n}\n\n/**\n * Structured client error.\n */\nexport interface NAuthError {\n code: NAuthErrorCode;\n message: string;\n details?: Record<string, unknown>;\n timestamp?: string;\n statusCode?: number;\n}\n","/**\n * Audit event types.\n */\nexport enum AuthAuditEventType {\n // Authentication events\n LOGIN_SUCCESS = 'LOGIN_SUCCESS',\n LOGIN_FAILED = 'LOGIN_FAILED',\n LOGOUT = 'LOGOUT',\n TOKEN_REFRESH = 'TOKEN_REFRESH',\n TOKEN_REVOKED = 'TOKEN_REVOKED',\n\n // Registration events\n SIGNUP_SUCCESS = 'SIGNUP_SUCCESS',\n SIGNUP_FAILED = 'SIGNUP_FAILED',\n\n // Verification events\n EMAIL_VERIFIED = 'EMAIL_VERIFIED',\n PHONE_VERIFIED = 'PHONE_VERIFIED',\n VERIFICATION_FAILED = 'VERIFICATION_FAILED',\n\n // MFA events\n MFA_ENABLED = 'MFA_ENABLED',\n MFA_DISABLED = 'MFA_DISABLED',\n MFA_VERIFIED = 'MFA_VERIFIED',\n MFA_FAILED = 'MFA_FAILED',\n MFA_BACKUP_CODE_USED = 'MFA_BACKUP_CODE_USED',\n\n // Password events\n PASSWORD_CHANGED = 'PASSWORD_CHANGED',\n PASSWORD_RESET_REQUESTED = 'PASSWORD_RESET_REQUESTED',\n PASSWORD_RESET_COMPLETED = 'PASSWORD_RESET_COMPLETED',\n\n // Security events\n ACCOUNT_LOCKED = 'ACCOUNT_LOCKED',\n ACCOUNT_UNLOCKED = 'ACCOUNT_UNLOCKED',\n SUSPICIOUS_ACTIVITY = 'SUSPICIOUS_ACTIVITY',\n ADAPTIVE_MFA_TRIGGERED = 'ADAPTIVE_MFA_TRIGGERED',\n\n // Social auth events\n SOCIAL_LINK_SUCCESS = 'SOCIAL_LINK_SUCCESS',\n SOCIAL_LINK_FAILED = 'SOCIAL_LINK_FAILED',\n SOCIAL_UNLINK = 'SOCIAL_UNLINK',\n}\n\n/**\n * Audit event status.\n */\nexport type AuthAuditEventStatus = 'SUCCESS' | 'FAILURE' | 'INFO' | 'SUSPICIOUS';\n\n/**\n * Individual audit event record.\n */\nexport interface AuthAuditEvent {\n id: number;\n userId: number;\n eventType: AuthAuditEventType;\n eventStatus: AuthAuditEventStatus;\n riskFactor?: number | null;\n riskFactors?: string[] | null;\n adaptiveMfaTriggered?: boolean | null;\n ipAddress?: string | null;\n ipCountry?: string | null;\n ipCity?: string | null;\n ipLatitude?: number | null;\n ipLongitude?: number | null;\n userAgent?: string | null;\n platform?: string | null;\n browser?: string | null;\n deviceId?: string | null;\n deviceName?: string | null;\n deviceType?: string | null;\n sessionId?: number | null;\n challengeSessionId?: number | null;\n authMethod?: string | null;\n performedBy?: string | null;\n reason?: string | null;\n description?: string | null;\n metadata?: Record<string, unknown> | null;\n createdAt: string | Date;\n}\n\n/**\n * Paginated audit history response.\n */\nexport interface AuditHistoryResponse {\n data: AuthAuditEvent[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n","import {\n NAuthClientConfig,\n NAuthEndpoints,\n NAuthAdminEndpoints,\n NAuthStorageAdapter,\n TokenDeliveryMode,\n HttpAdapter,\n} from '../types/config.types';\n\n/**\n * Fully resolved configuration with all defaults applied.\n */\nexport type ResolvedNAuthClientConfig = Omit<\n NAuthClientConfig,\n 'endpoints' | 'storage' | 'tokenDelivery' | 'httpAdapter'\n> & {\n tokenDelivery: TokenDeliveryMode;\n endpoints: NAuthEndpoints;\n storage: NAuthStorageAdapter;\n httpAdapter: HttpAdapter;\n csrf: { cookieName: string; headerName: string };\n deviceTrust: { headerName: string; storageKey: string };\n headers: Record<string, string>;\n timeout: number;\n admin?: {\n pathPrefix: string;\n endpoints: NAuthAdminEndpoints;\n headers: Record<string, string>;\n };\n};\n\n/**\n * Default endpoint paths matching backend controller.\n */\nexport const defaultEndpoints: NAuthEndpoints = {\n login: '/login',\n signup: '/signup',\n logout: '/logout',\n logoutAll: '/logout/all',\n refresh: '/refresh',\n respondChallenge: '/respond-challenge',\n resendCode: '/challenge/resend',\n getSetupData: '/challenge/setup-data',\n getChallengeData: '/challenge/challenge-data',\n profile: '/profile',\n changePassword: '/change-password',\n forgotPassword: '/forgot-password',\n confirmForgotPassword: '/forgot-password/confirm',\n confirmAdminResetPassword: '/reset-password/confirm',\n mfaStatus: '/mfa/status',\n mfaDevices: '/mfa/devices',\n mfaSetupData: '/mfa/setup-data',\n mfaVerifySetup: '/mfa/verify-setup',\n mfaPreferred: '/mfa/devices/:deviceId/preferred',\n mfaBackupCodes: '/mfa/backup-codes/generate',\n socialLinked: '/social/linked',\n socialLink: '/social/link',\n socialUnlink: '/social/unlink',\n socialVerify: '/social/:provider/verify',\n socialRedirectStart: '/social/:provider/redirect',\n socialExchange: '/social/exchange',\n trustDevice: '/trust-device',\n isTrustedDevice: '/is-trusted-device',\n auditHistory: '/audit/history',\n updateProfile: '/profile',\n};\n\n/**\n * Default admin endpoint paths matching backend admin controller.\n */\nexport const defaultAdminEndpoints: NAuthAdminEndpoints = {\n signup: '/signup',\n signupSocial: '/signup-social',\n getUsers: '/users',\n getUser: '/users/:sub',\n deleteUser: '/users/:sub',\n disableUser: '/users/:sub/disable',\n enableUser: '/users/:sub/enable',\n forcePasswordChange: '/users/:sub/force-password-change',\n setPassword: '/set-password',\n resetPasswordInitiate: '/reset-password/initiate',\n getUserSessions: '/users/:sub/sessions',\n logoutAll: '/users/:sub/logout-all',\n getMfaStatus: '/users/:sub/mfa/status',\n getMfaDevices: '/users/:sub/mfa/devices',\n removeMfaDeviceById: '/mfa/devices/:deviceId',\n setPreferredMfaDevice: '/users/:sub/mfa/devices/:deviceId/preferred',\n setMfaExemption: '/mfa/exemption',\n getAuditHistory: '/audit/history',\n};\n\n/**\n * Normalize user config with defaults.\n *\n * @param config - User supplied config\n * @param defaultAdapter - Default HTTP adapter (FetchAdapter for vanilla, AngularHttpAdapter for Angular)\n * @returns Resolved config with defaults applied\n */\nexport const resolveConfig = (config: NAuthClientConfig, defaultAdapter: HttpAdapter): ResolvedNAuthClientConfig => {\n const resolvedEndpoints: NAuthEndpoints = {\n ...defaultEndpoints,\n ...(config.endpoints ?? {}),\n };\n\n // Resolve admin config if provided\n let resolvedAdmin: ResolvedNAuthClientConfig['admin'];\n if (config.admin) {\n const resolvedAdminEndpoints: NAuthAdminEndpoints = {\n ...defaultAdminEndpoints,\n ...(config.admin.endpoints ?? {}),\n };\n\n resolvedAdmin = {\n pathPrefix: config.admin.pathPrefix ?? '/admin',\n endpoints: resolvedAdminEndpoints,\n headers: {\n ...config.headers,\n ...(config.admin.headers ?? {}),\n },\n };\n }\n\n return {\n ...config,\n csrf: {\n cookieName: config.csrf?.cookieName ?? 'nauth_csrf_token',\n headerName: config.csrf?.headerName ?? 'x-csrf-token',\n },\n deviceTrust: {\n headerName: config.deviceTrust?.headerName ?? 'X-Device-Token',\n storageKey: config.deviceTrust?.storageKey ?? 'nauth_device_token',\n },\n headers: config.headers ?? {},\n timeout: config.timeout ?? 30000,\n endpoints: resolvedEndpoints,\n storage: config.storage as NAuthStorageAdapter,\n httpAdapter: config.httpAdapter ?? defaultAdapter,\n admin: resolvedAdmin,\n };\n};\n","import { NAuthError, NAuthErrorCode } from '../types/error.types';\n\n/**\n * Client-side error wrapper for SDK operations.\n *\n * Mirrors the backend NAuthException structure for consistent error handling.\n *\n * @example\n * ```typescript\n * try {\n * await client.login({ identifier: 'user@example.com', password: 'wrong' });\n * } catch (error) {\n * if (error instanceof NAuthClientError) {\n * console.log(error.code); // 'AUTH_INVALID_CREDENTIALS'\n * console.log(error.message); // 'Invalid credentials'\n * console.log(error.timestamp); // '2025-12-06T...'\n *\n * // Check specific error code\n * if (error.isCode(NAuthErrorCode.RATE_LIMIT_LOGIN)) {\n * const retryAfter = error.details?.retryAfter as number;\n * console.log(`Rate limited. Retry in ${retryAfter}s`);\n * }\n * }\n * }\n * ```\n */\nexport class NAuthClientError extends Error implements NAuthError {\n public readonly code: NAuthErrorCode;\n public readonly details?: Record<string, unknown>;\n public readonly statusCode?: number;\n public readonly timestamp: string;\n public readonly isNetworkError: boolean;\n\n /**\n * Create a new client error.\n *\n * @param code - Error code from NAuthErrorCode enum\n * @param message - Human-readable error message\n * @param options - Optional metadata including details, statusCode, timestamp, and network error flag\n */\n constructor(\n code: NAuthErrorCode,\n message: string,\n options?: {\n details?: Record<string, unknown>;\n statusCode?: number;\n timestamp?: string;\n isNetworkError?: boolean;\n },\n ) {\n super(message);\n this.code = code;\n this.details = options?.details;\n this.statusCode = options?.statusCode;\n this.timestamp = options?.timestamp || new Date().toISOString();\n this.isNetworkError = options?.isNetworkError ?? false;\n this.name = 'NAuthClientError';\n Object.setPrototypeOf(this, NAuthClientError.prototype);\n }\n\n /**\n * Check if error matches a specific error code.\n *\n * @param code - Error code to check against\n * @returns True if the error code matches\n *\n * @example\n * ```typescript\n * if (error.isCode(NAuthErrorCode.RATE_LIMIT_SMS)) {\n * // Handle SMS rate limit\n * }\n * ```\n */\n isCode(code: NAuthErrorCode): boolean {\n return this.code === code;\n }\n\n /**\n * Get error details/metadata.\n *\n * @returns Error details object or undefined\n *\n * @example\n * ```typescript\n * const details = error.getDetails();\n * if (details?.retryAfter) {\n * console.log(`Retry after ${details.retryAfter} seconds`);\n * }\n * ```\n */\n getDetails(): Record<string, unknown> | undefined {\n return this.details;\n }\n\n /**\n * Get the error code.\n *\n * @returns The error code enum value\n */\n getCode(): NAuthErrorCode {\n return this.code;\n }\n\n /**\n * Serialize error to JSON object.\n *\n * @returns Plain object representation\n *\n * @example\n * ```typescript\n * const errorJson = error.toJSON();\n * // { code: 'AUTH_INVALID_CREDENTIALS', message: '...', timestamp: '...', details: {...} }\n * ```\n */\n toJSON(): {\n code: string;\n message: string;\n timestamp: string;\n details?: Record<string, unknown>;\n statusCode?: number;\n } {\n return {\n code: this.code,\n message: this.message,\n timestamp: this.timestamp,\n details: this.details,\n statusCode: this.statusCode,\n };\n }\n}\n","import { TokenResponse } from '../types/auth.types';\nimport { NAuthStorageAdapter } from '../types/config.types';\nimport { NAuthClientError } from './errors';\nimport { NAuthErrorCode } from '../types/error.types';\n\nconst ACCESS_TOKEN_KEY = 'nauth_access_token';\nconst REFRESH_TOKEN_KEY = 'nauth_refresh_token';\nconst ACCESS_EXPIRES_AT_KEY = 'nauth_access_token_expires_at';\nconst REFRESH_EXPIRES_AT_KEY = 'nauth_refresh_token_expires_at';\nconst USER_KEY = 'nauth_user';\nconst CHALLENGE_KEY = 'nauth_challenge_session';\n\n/**\n * Token state persisted in storage.\n */\nexport interface TokenState {\n accessToken: string | null;\n refreshToken: string | null;\n accessTokenExpiresAt?: number | null;\n refreshTokenExpiresAt?: number | null;\n}\n\n/**\n * Manage token persistence and refresh queuing.\n */\nexport class TokenManager {\n private readonly storage: NAuthStorageAdapter;\n private refreshPromise: Promise<TokenResponse> | null = null;\n private readonly isBrowser = typeof window !== 'undefined';\n\n /**\n * @param storage - storage adapter\n */\n constructor(storage: NAuthStorageAdapter) {\n this.storage = storage;\n }\n\n /**\n * Load tokens from storage.\n */\n async getTokens(): Promise<TokenState> {\n const [accessToken, refreshToken, accessExpRaw, refreshExpRaw] = await Promise.all([\n this.storage.getItem(ACCESS_TOKEN_KEY),\n this.storage.getItem(REFRESH_TOKEN_KEY),\n this.storage.getItem(ACCESS_EXPIRES_AT_KEY),\n this.storage.getItem(REFRESH_EXPIRES_AT_KEY),\n ]);\n return {\n accessToken,\n refreshToken,\n accessTokenExpiresAt: accessExpRaw ? Number(accessExpRaw) : null,\n refreshTokenExpiresAt: refreshExpRaw ? Number(refreshExpRaw) : null,\n };\n }\n\n /**\n * Persist tokens.\n *\n * @param tokens - new token pair\n */\n async setTokens(tokens: TokenResponse): Promise<void> {\n await Promise.all([\n this.storage.setItem(ACCESS_TOKEN_KEY, tokens.accessToken),\n this.storage.setItem(REFRESH_TOKEN_KEY, tokens.refreshToken),\n this.storage.setItem(ACCESS_EXPIRES_AT_KEY, tokens.accessTokenExpiresAt.toString()),\n this.storage.setItem(REFRESH_EXPIRES_AT_KEY, tokens.refreshTokenExpiresAt.toString()),\n ]);\n this.broadcastStorage();\n }\n\n /**\n * Clear tokens and related auth state.\n */\n async clearTokens(): Promise<void> {\n await Promise.all([\n this.storage.removeItem(ACCESS_TOKEN_KEY),\n this.storage.removeItem(REFRESH_TOKEN_KEY),\n this.storage.removeItem(ACCESS_EXPIRES_AT_KEY),\n this.storage.removeItem(REFRESH_EXPIRES_AT_KEY),\n this.storage.removeItem(USER_KEY),\n this.storage.removeItem(CHALLENGE_KEY),\n ]);\n this.broadcastStorage();\n }\n\n /**\n * Ensure only one refresh in flight.\n *\n * @param refreshFn - function performing refresh request\n */\n async refreshOnce(\n refreshFn: () => Promise<TokenResponse>,\n options?: {\n /**\n * Whether to persist returned tokens to storage.\n *\n * WHY:\n * - JSON mode needs persisted tokens for Authorization headers.\n * - Cookies mode uses httpOnly cookies; persisting tokens would be a security footgun.\n */\n persist?: boolean;\n },\n ): Promise<TokenResponse> {\n const shouldPersist = options?.persist !== false;\n if (!this.refreshPromise) {\n this.refreshPromise = refreshFn()\n .then(async (tokens) => {\n if (shouldPersist) {\n await this.setTokens(tokens);\n }\n return tokens;\n })\n .catch((error) => {\n throw error;\n })\n .finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n\n /**\n * Validate that a refresh token exists before attempting refresh.\n */\n async assertHasRefreshToken(): Promise<void> {\n const state = await this.getTokens();\n if (!state.refreshToken) {\n throw new NAuthClientError(NAuthErrorCode.AUTH_SESSION_NOT_FOUND, 'No refresh token available');\n }\n }\n\n /**\n * Broadcast a no-op write to trigger storage listeners in other tabs.\n */\n private broadcastStorage(): void {\n if (!this.isBrowser) return;\n try {\n window.localStorage.setItem('nauth_sync', Date.now().toString());\n } catch {\n // Best-effort; ignore if storage unavailable\n }\n }\n}\n","import { NAuthStorageAdapter } from './interface';\n\n/**\n * Browser storage adapter wrapping localStorage or sessionStorage.\n */\nexport class BrowserStorage implements NAuthStorageAdapter {\n private readonly storage: Storage;\n\n /**\n * Create a browser storage adapter.\n *\n * @param storage - Storage implementation (localStorage by default)\n */\n constructor(storage: Storage = window.localStorage) {\n this.storage = storage;\n }\n\n async getItem(key: string): Promise<string | null> {\n return this.storage.getItem(key);\n }\n\n async setItem(key: string, value: string): Promise<void> {\n this.storage.setItem(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n this.storage.removeItem(key);\n }\n\n async clear(): Promise<void> {\n this.storage.clear();\n }\n}\n","import { NAuthStorageAdapter } from './interface';\n\n/**\n * In-memory storage adapter for SSR, tests, or environments without Web Storage.\n */\nexport class InMemoryStorage implements NAuthStorageAdapter {\n private readonly store = new Map<string, string>();\n\n async getItem(key: string): Promise<string | null> {\n return this.store.has(key) ? (this.store.get(key) as string) : null;\n }\n\n async setItem(key: string, value: string): Promise<void> {\n this.store.set(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n}\n","import type { AuthResponse } from '../types/auth.types';\nimport type { NAuthClientError } from './errors';\n\n/**\n * Authentication event types emitted by NAuthClient\n *\n * Events are emitted throughout the authentication lifecycle,\n * allowing applications to react to auth state changes.\n */\nexport type AuthEventType =\n | 'auth:login'\n | 'auth:signup'\n | 'auth:success'\n | 'auth:challenge'\n | 'auth:error'\n | 'auth:logout'\n | 'auth:refresh'\n | 'auth:session_expired'\n | 'oauth:started'\n | 'oauth:callback'\n | 'oauth:completed'\n | 'oauth:error';\n\n/**\n * Discriminated union of all authentication events with type-safe payloads\n *\n * Each event has a specific payload type for better type safety.\n */\nexport type AuthEvent =\n | AuthLoginEvent\n | AuthSignupEvent\n | AuthSuccessEvent\n | AuthChallengeEvent\n | AuthErrorEvent\n | AuthLogoutEvent\n | AuthRefreshEvent\n | AuthSessionExpiredEvent\n | OAuthStartedEvent\n | OAuthCallbackEvent\n | OAuthCompletedEvent\n | OAuthErrorEvent;\n\n/**\n * Login initiated event\n */\nexport interface AuthLoginEvent {\n type: 'auth:login';\n data: {\n identifier: string;\n };\n timestamp: number;\n}\n\n/**\n * Signup initiated event\n */\nexport interface AuthSignupEvent {\n type: 'auth:signup';\n data: {\n email: string;\n };\n timestamp: number;\n}\n\n/**\n * Successful authentication event\n */\nexport interface AuthSuccessEvent {\n type: 'auth:success';\n data: AuthResponse;\n timestamp: number;\n}\n\n/**\n * Challenge required event\n */\nexport interface AuthChallengeEvent {\n type: 'auth:challenge';\n data: AuthResponse;\n timestamp: number;\n}\n\n/**\n * Authentication error event\n */\nexport interface AuthErrorEvent {\n type: 'auth:error';\n data: NAuthClientError;\n timestamp: number;\n}\n\n/**\n * User logged out event\n */\nexport interface AuthLogoutEvent {\n type: 'auth:logout';\n data: {\n forgetDevice?: boolean;\n global?: boolean;\n };\n timestamp: number;\n}\n\n/**\n * Token refreshed event\n */\nexport interface AuthRefreshEvent {\n type: 'auth:refresh';\n data: {\n success: boolean;\n };\n timestamp: number;\n}\n\n/**\n * Session expired event (refresh token expired or invalid)\n *\n * Emitted when token refresh fails with 401, indicating the session\n * has expired and the user needs to re-authenticate.\n */\nexport interface AuthSessionExpiredEvent {\n type: 'auth:session_expired';\n data: Record<string, never>;\n timestamp: number;\n}\n\n/**\n * OAuth flow started event\n */\nexport interface OAuthStartedEvent {\n type: 'oauth:started';\n data: {\n provider: string;\n };\n timestamp: number;\n}\n\n/**\n * OAuth callback received event\n */\nexport interface OAuthCallbackEvent {\n type: 'oauth:callback';\n data: {\n provider: string;\n };\n timestamp: number;\n}\n\n/**\n * OAuth flow completed event\n */\nexport interface OAuthCompletedEvent {\n type: 'oauth:completed';\n data: AuthResponse;\n timestamp: number;\n}\n\n/**\n * OAuth error event\n */\nexport interface OAuthErrorEvent {\n type: 'oauth:error';\n data: NAuthClientError;\n timestamp: number;\n}\n\n/**\n * Event listener callback function\n */\nexport type AuthEventListener = (event: AuthEvent) => void;\n\n/**\n * Internal event emitter for authentication events\n *\n * Provides pub/sub functionality for auth lifecycle events.\n *\n * @internal\n */\nexport class EventEmitter {\n private listeners = new Map<AuthEventType | '*', Set<AuthEventListener>>();\n\n /**\n * Subscribe to an authentication event\n *\n * @param event - Event type or '*' for all events\n * @param listener - Callback function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = emitter.on('auth:success', (event) => {\n * console.log('User logged in:', event.data);\n * });\n *\n * // Later\n * unsubscribe();\n * ```\n */\n on(event: AuthEventType | '*', listener: AuthEventListener): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n\n // Return unsubscribe function\n return () => this.off(event, listener);\n }\n\n /**\n * Unsubscribe from an authentication event\n *\n * @param event - Event type or '*'\n * @param listener - Callback function to remove\n */\n off(event: AuthEventType | '*', listener: AuthEventListener): void {\n this.listeners.get(event)?.delete(listener);\n }\n\n /**\n * Emit an authentication event\n *\n * Notifies all listeners for the specific event type and wildcard listeners.\n *\n * @param event - Event to emit\n * @internal\n */\n emit(event: AuthEvent): void {\n const specificListeners = this.listeners.get(event.type);\n const wildcardListeners = this.listeners.get('*');\n\n // Emit to specific event listeners\n specificListeners?.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n console.error(`Error in ${event.type} event listener:`, error);\n }\n });\n\n // Emit to wildcard listeners\n wildcardListeners?.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n console.error(`Error in wildcard event listener:`, error);\n }\n });\n }\n\n /**\n * Remove all listeners\n *\n * @internal\n */\n clear(): void {\n this.listeners.clear();\n }\n}\n","import { HttpAdapter, HttpRequest, HttpResponse } from '../core/http-adapter';\nimport { NAuthClientError } from '../core/errors';\nimport { NAuthErrorCode } from '../types/error.types';\n\n/**\n * HTTP adapter using native fetch API.\n *\n * Suitable for:\n * - Vanilla JavaScript/TypeScript\n * - Node.js (with fetch polyfill or Node 18+)\n * - Environments without framework-specific HTTP clients\n *\n * @example\n * ```typescript\n * import { NAuthClient } from '@nauth-toolkit/client';\n * import { FetchAdapter } from '@nauth-toolkit/client/adapters/fetch';\n *\n * const client = new NAuthClient({\n * baseUrl: 'https://api.example.com/auth',\n * httpAdapter: new FetchAdapter(),\n * });\n * ```\n */\nexport class FetchAdapter implements HttpAdapter {\n /**\n * Execute HTTP request using native fetch.\n *\n * @param config - Request configuration\n * @returns Response with parsed data\n * @throws NAuthClientError if request fails\n */\n async request<T>(config: HttpRequest): Promise<HttpResponse<T>> {\n const fetchOptions: RequestInit = {\n method: config.method,\n headers: config.headers,\n signal: config.signal,\n credentials: config.credentials,\n };\n\n if (config.body !== undefined) {\n fetchOptions.body = JSON.stringify(config.body);\n }\n\n let response: Response;\n try {\n response = await fetch(config.url, fetchOptions);\n } catch (error) {\n throw new NAuthClientError(NAuthErrorCode.INTERNAL_ERROR, 'Network request failed', {\n isNetworkError: true,\n details: { url: config.url, message: (error as Error).message },\n });\n }\n\n const status = response.status;\n let data: unknown = null;\n const text = await response.text();\n if (text) {\n try {\n data = JSON.parse(text);\n } catch {\n data = text;\n }\n }\n\n // Extract headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n if (!response.ok) {\n const errorData = typeof data === 'object' && data !== null ? (data as Record<string, unknown>) : {};\n const code =\n typeof errorData['code'] === 'string' ? (errorData['code'] as NAuthErrorCode) : NAuthErrorCode.INTERNAL_ERROR;\n const message =\n typeof errorData['message'] === 'string'\n ? (errorData['message'] as string)\n : `Request failed with status ${status}`;\n const timestamp = typeof errorData['timestamp'] === 'string' ? (errorData['timestamp'] as string) : undefined;\n const details = errorData['details'] as Record<string, unknown> | undefined;\n\n throw new NAuthClientError(code, message, {\n statusCode: status,\n timestamp,\n details,\n });\n }\n\n return { data: data as T, status, headers };\n }\n}\n","import { AuthResponse, AuthChallenge } from '../types/auth.types';\nimport { ResolvedNAuthClientConfig } from './config';\nimport { AuthResponseContext, type NAuthStorageAdapter } from '../types/config.types';\n\nconst OAUTH_STATE_KEY = 'nauth_oauth_state';\n\n/**\n * Challenge router - handles automatic navigation after auth operations.\n *\n * This is internal to the SDK. Consumer apps interact via config options:\n * - `onAuthResponse` callback for full control (dialogs, etc.)\n * - `navigationHandler` for custom navigation\n * - `redirects.challengeRoutes` for custom route mapping\n * - `redirects.useSingleChallengeRoute` for query param mode\n *\n * @example Dialog-based app\n * ```typescript\n * {\n * onAuthResponse: (response, context) => {\n * if (response.challengeName) {\n * dialog.open(ChallengeComponent, { data: response });\n * }\n * }\n * }\n * ```\n *\n * @example Custom routes\n * ```typescript\n * {\n * redirects: {\n * challengeRoutes: {\n * [AuthChallenge.MFA_REQUIRED]: '/auth/mfa',\n * }\n * }\n * }\n * ```\n *\n * @example Single route with query param\n * ```typescript\n * {\n * redirects: {\n * useSingleChallengeRoute: true\n * }\n * }\n * ```\n */\nexport class ChallengeRouter {\n constructor(\n private config: ResolvedNAuthClientConfig,\n private oauthStorage: NAuthStorageAdapter,\n ) {}\n\n /**\n * Handle auth response - either call callback or auto-navigate.\n *\n * @param response - Auth response from backend\n * @param context - Context about the auth operation\n */\n async handleAuthResponse(response: AuthResponse, context: AuthResponseContext): Promise<void> {\n // If custom handler provided, delegate to app\n if (this.config.onAuthResponse) {\n await this.config.onAuthResponse(response, context);\n return;\n }\n\n // Auto-navigate based on response\n if (response.challengeName) {\n await this.navigateToChallenge(response);\n } else {\n // Retrieve stored appState for social redirect flows (when not already in context)\n const queryParams = context.appState ? { appState: context.appState } : await this.getStoredOauthState();\n await this.navigateToSuccess(queryParams, context);\n }\n }\n\n /**\n * Resolve the configured success URL based on context and explicit override keys.\n *\n * IMPORTANT:\n * - `null` explicitly disables auto-navigation (caller should rely on framework events / custom routing).\n * - `undefined` / missing keys fall back to other configured values or defaults.\n * - Falls back to legacy `redirects.success` when new keys are not set.\n *\n * @param context - Auth response context\n * @returns URL string, or null when auto-navigation is disabled\n */\n private resolveSuccessUrl(context?: AuthResponseContext): string | null {\n const redirects = this.config.redirects;\n if (!redirects) {\n return '/';\n }\n\n const isSignup = context?.source === 'signup';\n\n if (isSignup) {\n if (redirects.signupSuccess === null) return null;\n if (typeof redirects.signupSuccess === 'string') return redirects.signupSuccess;\n }\n\n if (!isSignup) {\n if (redirects.loginSuccess === null) return null;\n if (typeof redirects.loginSuccess === 'string') return redirects.loginSuccess;\n }\n\n // Backward-compatible fallback (legacy API)\n if (redirects.success === null) return null;\n if (typeof redirects.success === 'string') return redirects.success;\n\n return '/';\n }\n\n /**\n * Navigate to appropriate challenge route.\n *\n * @param response - Auth response containing challenge info\n */\n async navigateToChallenge(response: AuthResponse): Promise<void> {\n const url = this.buildChallengeUrl(response);\n await this.navigate(url);\n }\n\n /**\n * Navigate to success URL.\n *\n * @param queryParams - Optional query parameters to append to the success URL\n * @param context - Optional auth context for selecting the success route\n *\n * @example\n * ```typescript\n * await router.navigateToSuccess({ appState: 'invite-code-123' });\n * ```\n */\n async navigateToSuccess(\n queryParams?: Record<string, string | null | undefined>,\n context?: AuthResponseContext,\n ): Promise<void> {\n const resolved = this.resolveSuccessUrl(context);\n if (resolved === null) {\n return;\n }\n\n let url = resolved;\n\n // Append query parameters if provided\n // Build path with query string (not full URL) to work with both\n // Angular Router (navigateByUrl) and window.location\n if (queryParams && Object.keys(queryParams).length > 0) {\n const searchParams = new URLSearchParams();\n Object.entries(queryParams).forEach(([key, value]) => {\n if (value) {\n searchParams.set(key, value);\n }\n });\n const queryString = searchParams.toString();\n url = queryString ? `${url}${url.includes('?') ? '&' : '?'}${queryString}` : url;\n }\n\n await this.navigate(url);\n }\n\n /**\n * Retrieve stored OAuth appState from sessionStorage.\n *\n * NOTE: This method does NOT clear the storage. Only the public getLastOauthState() API clears it.\n * This allows the SDK to read appState for auto-navigation while still making it available to\n * consumers via the public API.\n *\n * @returns Query params object with appState if present, undefined otherwise\n */\n private async getStoredOauthState(): Promise<Record<string, string> | undefined> {\n try {\n const stored = await this.oauthStorage.getItem(OAUTH_STATE_KEY);\n if (stored) {\n // Do NOT clear - consumer may want to retrieve it via getLastOauthState()\n return { appState: stored };\n }\n } catch {\n // Ignore storage errors\n }\n return undefined;\n }\n\n /**\n * Navigate to error URL.\n *\n * @param type - Type of error (oauth or session)\n */\n async navigateToError(type: 'oauth' | 'session'): Promise<void> {\n const redirects = this.config.redirects;\n const raw = type === 'oauth' ? redirects?.oauthError : redirects?.sessionExpired;\n if (raw === null) {\n return;\n }\n const url = raw ?? '/login';\n await this.navigate(url);\n }\n\n /**\n * Build challenge URL based on configuration.\n *\n * Priority:\n * 1. Custom route mapping (challengeRoutes)\n * 2. Single route with query param (useSingleChallengeRoute)\n * 3. MFA-specific routes (mfaRoutes) - for MFA_REQUIRED challenge only\n * 4. Default separate routes (challengeBase + kebab-case)\n *\n * @param response - Auth response containing challenge info\n * @returns URL to navigate to\n */\n buildChallengeUrl(response: AuthResponse): string {\n const challengeName = response.challengeName!;\n\n // Priority 1: Custom route mapping\n if (this.config.redirects?.challengeRoutes?.[challengeName]) {\n return this.config.redirects.challengeRoutes[challengeName]!;\n }\n\n const base = this.config.redirects?.challengeBase || '/auth/challenge';\n\n // Priority 2: Single route with query param\n if (this.config.redirects?.useSingleChallengeRoute) {\n return `${base}?challenge=${challengeName}`;\n }\n\n // Priority 3: MFA-specific routes (only for MFA_REQUIRED)\n if (challengeName === AuthChallenge.MFA_REQUIRED) {\n const mfaUrl = this.buildMFAUrl(response);\n if (mfaUrl) {\n return mfaUrl;\n }\n }\n\n // Priority 4: Default separate routes\n const route = this.buildDefaultRouteSegment(challengeName, response);\n return `${base}/${route}`;\n }\n\n /**\n * Build MFA-specific URL if custom mfaRoutes are configured.\n *\n * @param response - Auth response with MFA challenge parameters\n * @returns Custom MFA URL if configured, null otherwise\n */\n private buildMFAUrl(response: AuthResponse): string | null {\n const params = response.challengeParameters;\n const method = params?.['preferredMethod'] || params?.['method'];\n const mfaRoutes = this.config.redirects?.mfaRoutes;\n\n if (!mfaRoutes) {\n return null;\n }\n\n // Passkey verification\n if (method === 'passkey' && mfaRoutes.passkey) {\n return mfaRoutes.passkey;\n }\n\n // MFA method selector (multiple methods available)\n if (\n !method &&\n params?.['availableMethods'] &&\n Array.isArray(params['availableMethods']) &&\n params['availableMethods'].length > 1\n ) {\n if (mfaRoutes.selector) {\n return mfaRoutes.selector;\n }\n }\n\n // Default MFA verification (sms, email, totp)\n if (mfaRoutes.default) {\n return mfaRoutes.default;\n }\n\n return null;\n }\n\n /**\n * Build default route segment for a challenge.\n *\n * @param challengeName - Challenge type\n * @param response - Auth response for extracting challenge parameters (needed for MFA)\n * @returns Route segment (e.g., 'mfa-required/passkey', 'verify-email')\n */\n private buildDefaultRouteSegment(challengeName: AuthChallenge, response?: AuthResponse): string {\n // MFA_REQUIRED needs special handling for backward compatibility\n // with existing apps that have passkey and selector routes\n if (challengeName === AuthChallenge.MFA_REQUIRED && response) {\n const params = response.challengeParameters;\n const method = params?.['preferredMethod'] || params?.['method'];\n\n // Passkey verification\n if (method === 'passkey') {\n return 'mfa-required/passkey';\n }\n\n // MFA method selector (multiple methods available)\n if (\n !method &&\n params?.['availableMethods'] &&\n Array.isArray(params['availableMethods']) &&\n params['availableMethods'].length > 1\n ) {\n return 'mfa-selector';\n }\n\n // Default MFA verification (sms, email, totp)\n return 'mfa-required';\n }\n\n // Generic kebab-case route for other challenges\n return challengeName.toLowerCase().replace(/_/g, '-');\n }\n\n /**\n * Execute navigation using configured handler or default.\n *\n * @param url - URL to navigate to\n */\n private async navigate(url: string): Promise<void> {\n if (this.config.navigationHandler) {\n await this.config.navigationHandler(url);\n } else {\n // Default: use window.location.replace (works in guards and browsers)\n if (typeof window !== 'undefined') {\n window.location.replace(url);\n }\n }\n }\n\n /**\n * Expose URL builder for guards/components that need it.\n *\n * @param response - Auth response containing challenge info\n * @returns URL for the challenge\n */\n getChallengeUrl(response: AuthResponse): string {\n return this.buildChallengeUrl(response);\n }\n\n /**\n * Check if the error redirect is disabled.\n *\n * WHY: Guards need to know if they should return true (activate route) or false\n * (prevent activation) when auto-redirect is disabled via null redirect URLs.\n *\n * @param type - Type of error (oauth or session)\n * @returns True if the error redirect is explicitly null, false otherwise\n */\n isErrorRedirectDisabled(type: 'oauth' | 'session'): boolean {\n const redirects = this.config.redirects;\n if (!redirects) {\n return false;\n }\n\n const redirectUrl = type === 'oauth' ? redirects.oauthError : redirects.sessionExpired;\n return redirectUrl === null;\n }\n\n /**\n * Check if the success redirect is disabled for a given context.\n *\n * WHY: Guards need to know if they should return true (activate route) or false\n * (prevent activation) when auto-redirect is disabled via null redirect URLs.\n *\n * @param context - Optional auth context to determine which success redirect to check\n * @returns True if the success redirect is explicitly null, false otherwise\n */\n isSuccessRedirectDisabled(context?: AuthResponseContext): boolean {\n const resolved = this.resolveSuccessUrl(context);\n return resolved === null;\n }\n}\n","import { ResolvedNAuthClientConfig } from './config';\nimport { NAuthAdminEndpoints } from '../types/config.types';\nimport { NAuthClientError } from './errors';\nimport { NAuthErrorCode } from '../types/error.types';\nimport type {\n AdminSignupRequest,\n AdminSignupResponse,\n AdminSignupSocialRequest,\n AdminSignupSocialResponse,\n GetUsersRequest,\n GetUsersResponse,\n DeleteUserResponse,\n DisableUserResponse,\n EnableUserResponse,\n AdminResetPasswordRequest,\n AdminResetPasswordResponse,\n GetUserSessionsResponse,\n AdminAuditHistoryRequest,\n} from '../types/admin.types';\nimport type { AuthUser } from '../types/user.types';\nimport type { MFAStatus, RemoveMFADeviceResponse, GetMFADevicesResponse } from '../types/mfa.types';\nimport type { AuditHistoryResponse } from '../types/audit.types';\n\nconst hasWindow = (): boolean =>\n typeof globalThis !== 'undefined' && typeof (globalThis as { window?: unknown }).window !== 'undefined';\n\n/**\n * Admin operations for user and system management.\n * Accessed via client.admin.*\n *\n * Provides admin-level operations including:\n * - User CRUD operations\n * - Password management (set, reset)\n * - Session management\n * - MFA management\n * - Audit history\n *\n * @example\n * ```typescript\n * const client = new NAuthClient({\n * baseUrl: 'https://api.example.com/auth',\n * tokenDelivery: 'cookies',\n * admin: {\n * pathPrefix: '/admin'\n * }\n * });\n *\n * // User management\n * const user = await client.admin.createUser({\n * email: 'user@example.com',\n * password: 'SecurePass123!',\n * isEmailVerified: true,\n * });\n *\n * // Get users with filters\n * const users = await client.admin.getUsers({\n * page: 1,\n * limit: 20,\n * mfaEnabled: false\n * });\n *\n * // Delete user (handles :sub path param)\n * await client.admin.deleteUser('user-uuid');\n * ```\n */\nexport class AdminOperations {\n private readonly config: ResolvedNAuthClientConfig;\n private readonly adminEndpoints: NAuthAdminEndpoints;\n private readonly adminPathPrefix: string;\n private readonly adminHeaders: Record<string, string>;\n\n /**\n * Create admin operations instance.\n *\n * @param config - Resolved client configuration\n */\n constructor(config: ResolvedNAuthClientConfig) {\n if (!config.admin) {\n throw new Error('Admin operations require admin configuration');\n }\n\n this.config = config;\n this.adminEndpoints = config.admin.endpoints;\n this.adminPathPrefix = config.admin.pathPrefix;\n this.adminHeaders = config.admin.headers;\n }\n\n // ============================================================================\n // User Management\n // ============================================================================\n\n /**\n * Create a new user (admin operation)\n *\n * Allows creating users with:\n * - Pre-verified email/phone\n * - Auto-generated passwords\n * - Force password change flag\n *\n * @param request - User creation request\n * @returns Created user and optional generated password\n * @throws {NAuthClientError} If creation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.createUser({\n * email: 'user@example.com',\n * password: 'SecurePass123!',\n * isEmailVerified: true,\n * });\n *\n * // With auto-generated password\n * const result = await client.admin.createUser({\n * email: 'user@example.com',\n * generatePassword: true,\n * mustChangePassword: true,\n * });\n * console.log('Generated password:', result.generatedPassword);\n * ```\n */\n async createUser(request: AdminSignupRequest): Promise<AdminSignupResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.signup);\n return this.post<AdminSignupResponse>(path, request);\n }\n\n /**\n * Import social user (admin operation)\n *\n * Imports existing social users from external platforms (e.g., Cognito, Auth0)\n * with social account linkage.\n *\n * @param request - Social user import request\n * @returns Created user and social account info\n * @throws {NAuthClientError} If import fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.importSocialUser({\n * email: 'user@example.com',\n * provider: 'google',\n * providerId: 'google_12345',\n * providerEmail: 'user@gmail.com',\n * });\n * ```\n */\n async importSocialUser(request: AdminSignupSocialRequest): Promise<AdminSignupSocialResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.signupSocial);\n return this.post<AdminSignupSocialResponse>(path, request);\n }\n\n /**\n * Get users with filters and pagination\n *\n * @param params - Filter and pagination params\n * @returns Paginated user list\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.getUsers({\n * page: 1,\n * limit: 20,\n * isEmailVerified: true,\n * mfaEnabled: false,\n * sortBy: 'createdAt',\n * sortOrder: 'DESC',\n * });\n * ```\n */\n async getUsers(params: GetUsersRequest = {}): Promise<GetUsersResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getUsers);\n const queryString = this.buildQueryString(params as unknown as Record<string, unknown>);\n return this.get<GetUsersResponse>(`${path}${queryString}`);\n }\n\n /**\n * Get user by sub (UUID)\n *\n * @param sub - User UUID\n * @returns User object\n * @throws {NAuthClientError} If user not found\n *\n * @example\n * ```typescript\n * const user = await client.admin.getUser('a21b654c-2746-4168-acee-c175083a65cd');\n * ```\n */\n async getUser(sub: string): Promise<AuthUser> {\n const path = this.buildAdminUrl(this.adminEndpoints.getUser, { sub });\n return this.get<AuthUser>(path);\n }\n\n /**\n * Delete user with cascade cleanup\n *\n * @param sub - User UUID\n * @returns Deletion confirmation with cascade counts\n * @throws {NAuthClientError} If deletion fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.deleteUser('user-uuid');\n * console.log('Deleted records:', result.deletedRecords);\n * ```\n */\n async deleteUser(sub: string): Promise<DeleteUserResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.deleteUser, { sub });\n return this.delete<DeleteUserResponse>(path);\n }\n\n /**\n * Disable user account (permanent lock)\n *\n * @param sub - User UUID\n * @param reason - Optional reason for disabling\n * @returns Disable confirmation with revoked session count\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.disableUser(\n * 'user-uuid',\n * 'Account compromised'\n * );\n * console.log('Revoked sessions:', result.revokedSessions);\n * ```\n */\n async disableUser(sub: string, reason?: string): Promise<DisableUserResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.disableUser, { sub });\n return this.post<DisableUserResponse>(path, { reason });\n }\n\n /**\n * Enable (unlock) user account\n *\n * @param sub - User UUID\n * @returns Enable confirmation with updated user\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.enableUser('user-uuid');\n * console.log('User enabled:', result.user);\n * ```\n */\n async enableUser(sub: string): Promise<EnableUserResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.enableUser, { sub });\n return this.post<EnableUserResponse>(path, {});\n }\n\n /**\n * Force password change on next login\n *\n * @param sub - User UUID\n * @returns Success confirmation\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.forcePasswordChange('user-uuid');\n * ```\n */\n async forcePasswordChange(sub: string): Promise<{ success: boolean }> {\n const path = this.buildAdminUrl(this.adminEndpoints.forcePasswordChange, { sub });\n return this.post<{ success: boolean }>(path, {});\n }\n\n // ============================================================================\n // Password Management\n // ============================================================================\n\n /**\n * Set password for any user (admin operation)\n *\n * @param identifier - User email, username, or phone\n * @param newPassword - New password\n * @returns Success confirmation\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.setPassword('user@example.com', 'NewSecurePass123!');\n * ```\n */\n async setPassword(identifier: string, newPassword: string): Promise<{ success: boolean }> {\n const path = this.buildAdminUrl(this.adminEndpoints.setPassword);\n return this.post<{ success: boolean }>(path, { identifier, newPassword });\n }\n\n /**\n * Initiate password reset workflow (sends code/link to user)\n *\n * @param request - Password reset request\n * @returns Reset confirmation with delivery details\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.initiatePasswordReset({\n * sub: 'user-uuid',\n * deliveryMethod: 'email',\n * baseUrl: 'https://myapp.com/reset-password',\n * reason: 'User requested password reset',\n * });\n * console.log('Code sent to:', result.destination);\n * ```\n */\n async initiatePasswordReset(request: AdminResetPasswordRequest): Promise<AdminResetPasswordResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.resetPasswordInitiate);\n return this.post<AdminResetPasswordResponse>(path, request);\n }\n\n // ============================================================================\n // Session Management\n // ============================================================================\n\n /**\n * Get all sessions for a user\n *\n * @param sub - User UUID\n * @returns User sessions\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.getUserSessions('user-uuid');\n * console.log('Active sessions:', result.sessions);\n * ```\n */\n async getUserSessions(sub: string): Promise<GetUserSessionsResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getUserSessions, { sub });\n return this.get<GetUserSessionsResponse>(path);\n }\n\n /**\n * Logout all sessions for a user (admin-initiated)\n *\n * @param sub - User UUID\n * @param forgetDevices - If true, also revokes all trusted devices\n * @returns Number of sessions revoked\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.logoutAllSessions('user-uuid', true);\n * console.log(`Revoked ${result.revokedCount} sessions`);\n * ```\n */\n async logoutAllSessions(sub: string, forgetDevices = false): Promise<{ revokedCount: number }> {\n const path = this.buildAdminUrl(this.adminEndpoints.logoutAll, { sub });\n return this.post<{ revokedCount: number }>(path, { forgetDevices });\n }\n\n // ============================================================================\n // MFA Management\n // ============================================================================\n\n /**\n * Get MFA status for a user\n *\n * @param sub - User UUID\n * @returns MFA status\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const status = await client.admin.getMfaStatus('user-uuid');\n * console.log('MFA enabled:', status.enabled);\n * ```\n */\n async getMfaStatus(sub: string): Promise<MFAStatus> {\n const path = this.buildAdminUrl(this.adminEndpoints.getMfaStatus, { sub });\n return this.get<MFAStatus>(path);\n }\n\n /**\n * Get MFA devices for a user\n *\n * Returns all active MFA devices for a user including device id, name, type, and isPreferred status.\n *\n * @param sub - User UUID\n * @returns Response containing array of MFA devices\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.getMfaDevices('user-uuid');\n * console.log('Devices:', result.devices);\n * // [{ id: 1, name: 'My Authenticator', type: 'totp', isPreferred: true, ... }]\n * ```\n */\n async getMfaDevices(sub: string): Promise<GetMFADevicesResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getMfaDevices, { sub });\n return this.get<GetMFADevicesResponse>(path);\n }\n\n /**\n * Remove a single MFA device by device ID (admin).\n *\n * @param deviceId - MFA device ID\n * @returns Removal result\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.removeMfaDeviceById(123);\n * console.log(result.removedDeviceId);\n * ```\n */\n async removeMfaDeviceById(deviceId: number): Promise<RemoveMFADeviceResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.removeMfaDeviceById, { deviceId: String(deviceId) });\n return this.delete<RemoveMFADeviceResponse>(path);\n }\n\n /**\n * Set preferred MFA device for a user (admin operation).\n *\n * @param sub - User identifier\n * @param deviceId - Device ID to set as preferred\n * @returns Success message\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.setPreferredMfaDevice('user-uuid', 123);\n * ```\n */\n async setPreferredMfaDevice(sub: string, deviceId: number): Promise<{ message: string }> {\n const path = this.buildAdminUrl(this.adminEndpoints.setPreferredMfaDevice, { sub, deviceId: String(deviceId) });\n return this.post<{ message: string }>(path, {});\n }\n\n /**\n * Grant or revoke MFA exemption for a user\n *\n * @param sub - User UUID\n * @param exempt - True to exempt from MFA, false to require\n * @param reason - Optional reason for exemption\n * @returns Success message\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.setMfaExemption('user-uuid', true, 'Service account');\n * ```\n */\n async setMfaExemption(sub: string, exempt: boolean, reason?: string): Promise<{ message: string }> {\n const path = this.buildAdminUrl(this.adminEndpoints.setMfaExemption);\n return this.post<{ message: string }>(path, { sub, exempt, reason });\n }\n\n // ============================================================================\n // Audit\n // ============================================================================\n\n /**\n * Get audit history for a user\n *\n * @param params - Audit history request params\n * @returns Paginated audit events\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const history = await client.admin.getAuditHistory({\n * sub: 'user-uuid',\n * page: 1,\n * limit: 50,\n * eventType: 'LOGIN_SUCCESS',\n * });\n * ```\n */\n async getAuditHistory(params: AdminAuditHistoryRequest): Promise<AuditHistoryResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getAuditHistory);\n const queryString = this.buildQueryString(params as unknown as Record<string, unknown>);\n return this.get<AuditHistoryResponse>(`${path}${queryString}`);\n }\n\n // ============================================================================\n // Private Helper Methods\n // ============================================================================\n\n /**\n * Build admin endpoint URL with path parameter replacement\n *\n * Path construction order:\n * 1. Start with endpoint: '/users/:sub'\n * 2. Replace params: '/users/uuid-123'\n * 3. Apply adminPathPrefix: '/admin/users/uuid-123'\n * 4. Apply authPathPrefix if exists: '/auth/admin/users/uuid-123'\n * 5. Combine with baseUrl: 'https://api.example.com/auth/admin/users/uuid-123'\n *\n * @param endpointPath - Endpoint path (may contain :sub, :provider, etc.)\n * @param pathParams - Path parameters to replace\n * @returns Full URL\n * @private\n */\n private buildAdminUrl(endpointPath: string, pathParams?: Record<string, string>): string {\n let path = endpointPath;\n\n // Replace path parameters (e.g., :sub, :provider)\n if (pathParams) {\n Object.entries(pathParams).forEach(([key, value]) => {\n path = path.replace(`:${key}`, encodeURIComponent(value));\n });\n }\n\n // Apply admin path prefix\n const prefix = this.adminPathPrefix.startsWith('/') ? this.adminPathPrefix : `/${this.adminPathPrefix}`;\n path = `${prefix}${path.startsWith('/') ? '' : '/'}${path}`;\n\n // Apply authPathPrefix if configured (e.g., '/auth')\n if (this.config.authPathPrefix) {\n const authPrefix = this.config.authPathPrefix.startsWith('/')\n ? this.config.authPathPrefix\n : `/${this.config.authPathPrefix}`;\n path = `${authPrefix}${path}`;\n }\n\n // Combine with baseUrl\n const normalizedBase = this.config.baseUrl.endsWith('/') ? this.config.baseUrl.slice(0, -1) : this.config.baseUrl;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n\n return `${normalizedBase}${normalizedPath}`;\n }\n\n /**\n * Build query string from params object\n *\n * Handles:\n * - Simple values (string, number, boolean)\n * - Arrays (multiple values for same key)\n * - Nested objects (e.g., createdAt[operator], createdAt[value])\n * - Dates (converted to ISO string)\n *\n * @param params - Query parameters\n * @returns Query string (e.g., '?page=1&limit=20')\n * @private\n */\n private buildQueryString(params: Record<string, unknown>): string {\n const searchParams = new URLSearchParams();\n\n for (const [key, rawValue] of Object.entries(params)) {\n if (rawValue === undefined || rawValue === null) {\n continue;\n }\n\n // Handle arrays\n if (Array.isArray(rawValue)) {\n for (const item of rawValue) {\n searchParams.append(key, String(item));\n }\n continue;\n }\n\n // Handle nested objects (e.g., createdAt: { operator: 'gte', value: Date })\n if (typeof rawValue === 'object' && rawValue !== null && !(rawValue instanceof Date)) {\n const nestedObj = rawValue as Record<string, unknown>;\n for (const [nestedKey, nestedValue] of Object.entries(nestedObj)) {\n const nestedParamKey = `${key}[${nestedKey}]`;\n const valueToAppend = nestedValue instanceof Date ? nestedValue.toISOString() : String(nestedValue);\n searchParams.append(nestedParamKey, valueToAppend);\n }\n continue;\n }\n\n // Handle dates\n if (rawValue instanceof Date) {\n searchParams.append(key, rawValue.toISOString());\n continue;\n }\n\n // Handle simple values\n searchParams.append(key, String(rawValue));\n }\n\n const query = searchParams.toString();\n return query ? `?${query}` : '';\n }\n\n /**\n * Build request headers for authentication\n *\n * @param auth - Whether to include authentication headers\n * @param method - HTTP method\n * @returns Headers object\n * @private\n */\n private async buildHeaders(\n auth: boolean,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' = 'GET',\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n ...this.config.headers,\n ...this.adminHeaders,\n };\n\n // Set Content-Type for mutating requests\n if (method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n // Add access token in JSON mode\n // In cookies mode, tokens are sent automatically via httpOnly cookies\n if (auth && this.config.tokenDelivery === 'json') {\n try {\n const ACCESS_TOKEN_KEY = 'nauth_access_token';\n const token = await this.config.storage.getItem(ACCESS_TOKEN_KEY);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n } catch {\n // Non-fatal: storage can fail in restricted environments\n }\n }\n\n // Add device token header in JSON mode\n // In cookies mode, device token is sent automatically via httpOnly cookie\n if (this.config.tokenDelivery === 'json') {\n try {\n const deviceToken = await this.config.storage.getItem(this.config.deviceTrust.storageKey);\n if (deviceToken) {\n headers[this.config.deviceTrust.headerName] = deviceToken;\n }\n } catch {\n // Non-fatal: storage can fail in restricted environments\n }\n }\n\n // Add CSRF header in cookies mode for mutating requests\n const mutatingMethods: readonly ('POST' | 'PUT' | 'PATCH' | 'DELETE')[] = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (\n this.config.tokenDelivery === 'cookies' &&\n hasWindow() &&\n (mutatingMethods as readonly string[]).includes(method)\n ) {\n const csrfToken = this.getCsrfToken();\n if (csrfToken) {\n headers[this.config.csrf.headerName] = csrfToken;\n }\n }\n\n return headers;\n }\n\n /**\n * Get CSRF token from cookie (browser only)\n *\n * @returns CSRF token or null\n * @private\n */\n private getCsrfToken(): string | null {\n if (!hasWindow() || typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`));\n return match ? decodeURIComponent(match[2]) : null;\n }\n\n /**\n * Execute GET request\n *\n * @param path - Full URL path\n * @returns Response data\n * @private\n */\n private async get<T>(path: string): Promise<T> {\n const headers = await this.buildHeaders(true, 'GET');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n try {\n const response = await this.config.httpAdapter.request<T>({\n method: 'GET',\n url: path,\n headers,\n credentials,\n });\n return response.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Execute POST request\n *\n * @param path - Full URL path\n * @param body - Request body\n * @returns Response data\n * @private\n */\n private async post<T>(path: string, body: unknown): Promise<T> {\n const headers = await this.buildHeaders(true, 'POST');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n try {\n const response = await this.config.httpAdapter.request<T>({\n method: 'POST',\n url: path,\n headers,\n body,\n credentials,\n });\n return response.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Execute DELETE request\n *\n * @param path - Full URL path\n * @returns Response data\n * @private\n */\n private async delete<T>(path: string): Promise<T> {\n const headers = await this.buildHeaders(true, 'DELETE');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n try {\n const response = await this.config.httpAdapter.request<T>({\n method: 'DELETE',\n url: path,\n headers,\n credentials,\n });\n return response.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Handle HTTP errors and convert to NAuthClientError\n *\n * @param error - Error from HTTP adapter\n * @returns NAuthClientError\n * @private\n */\n private handleError(error: unknown): NAuthClientError {\n if (error instanceof NAuthClientError) {\n return error;\n }\n\n // Try to extract error info from adapter response\n if (error && typeof error === 'object' && 'response' in error) {\n const httpError = error as { response?: { status?: number; data?: unknown } };\n const status = httpError.response?.status ?? 500;\n const errorData =\n typeof httpError.response?.data === 'object' && httpError.response.data !== null\n ? (httpError.response.data as Record<string, unknown>)\n : {};\n\n const code =\n typeof errorData['code'] === 'string' ? (errorData['code'] as NAuthErrorCode) : NAuthErrorCode.INTERNAL_ERROR;\n const message =\n typeof errorData['message'] === 'string'\n ? (errorData['message'] as string)\n : `Request failed with status ${status}`;\n\n return new NAuthClientError(code, message, {\n statusCode: status,\n details: errorData,\n });\n }\n\n return new NAuthClientError(\n NAuthErrorCode.INTERNAL_ERROR,\n error instanceof Error ? error.message : 'Unknown error',\n );\n }\n}\n","import { resolveConfig, ResolvedNAuthClientConfig } from './config';\nimport { TokenManager } from './refresh';\nimport { BrowserStorage } from '../storage/browser';\nimport { InMemoryStorage } from '../storage/memory';\nimport type { NAuthStorageAdapter } from '../storage/interface';\nimport { EventEmitter, AuthEventType, AuthEventListener } from './events';\nimport { NAuthClientError } from './errors';\nimport { NAuthErrorCode } from '../types/error.types';\nimport { FetchAdapter } from '../adapters/fetch-adapter';\nimport {\n AuthChallenge,\n ChallengeResponse,\n GetChallengeDataRequest,\n GetSetupDataRequest,\n LoginRequest,\n LogoutAllRequest,\n AuthResponse,\n ResendCodeRequest,\n SignupRequest,\n TokenResponse,\n MFACodeResponse,\n MFAPasskeyResponse,\n} from '../types/auth.types';\nimport { NAuthClientConfig } from '../types/config.types';\nimport {\n GetChallengeDataResponse,\n GetSetupDataResponse,\n GetMFADevicesResponse,\n MFAStatus,\n RemoveMFADeviceResponse,\n} from '../types/mfa.types';\nimport { AuditHistoryResponse } from '../types/audit.types';\nimport { LinkedAccountsResponse, SocialLoginOptions, SocialVerifyRequest, SocialProvider } from '../types/social.types';\nimport {\n AuthUser,\n ChangePasswordRequest,\n ConfirmForgotPasswordRequest,\n ConfirmForgotPasswordResponse,\n ForgotPasswordRequest,\n ForgotPasswordResponse,\n ResetPasswordWithCodeRequest,\n ResetPasswordWithCodeResponse,\n UpdateProfileRequest,\n} from '../types/user.types';\nimport { ChallengeRouter } from './challenge-router';\nimport { AdminOperations } from './admin-operations';\n\nconst USER_KEY = 'nauth_user';\nconst CHALLENGE_KEY = 'nauth_challenge_session';\nconst OAUTH_STATE_KEY = 'nauth_oauth_state';\nconst hasWindow = (): boolean =>\n typeof globalThis !== 'undefined' && typeof (globalThis as { window?: unknown }).window !== 'undefined';\n\n/**\n * Get sessionStorage-based storage for ephemeral OAuth state.\n * Falls back to main storage if sessionStorage is unavailable.\n */\nconst getOauthStorage = (mainStorage: NAuthStorageAdapter): NAuthStorageAdapter => {\n if (hasWindow() && typeof window.sessionStorage !== 'undefined') {\n return new BrowserStorage(window.sessionStorage);\n }\n // Fallback to main storage (memory storage in SSR, localStorage in browser without sessionStorage)\n return mainStorage;\n};\n\n/**\n * Choose default storage implementation.\n */\nconst defaultStorage = () => {\n if (hasWindow() && typeof window.localStorage !== 'undefined') {\n return new BrowserStorage();\n }\n return new InMemoryStorage();\n};\n\n/**\n * Primary client for interacting with nauth-toolkit backend.\n */\nexport class NAuthClient {\n private readonly config: ResolvedNAuthClientConfig;\n private readonly tokenManager: TokenManager;\n private readonly eventEmitter: EventEmitter;\n private readonly challengeRouter: ChallengeRouter;\n private readonly oauthStorage: NAuthStorageAdapter;\n private currentUser: AuthUser | null = null;\n\n /**\n * Internally tracked MFA device ID\n * When user selects a specific device (e.g., \"Microsoft Authenticator\" vs \"Google Authenticator\"),\n * SDK stores it here and auto-injects into respondToChallenge()\n */\n private selectedDeviceId?: number;\n\n /**\n * Admin operations (available if admin config provided).\n *\n * Provides admin-level user management methods:\n * - User CRUD operations\n * - Password management\n * - Session management\n * - MFA management\n * - Audit history\n *\n * @example\n * ```typescript\n * const client = new NAuthClient({\n * baseUrl: 'https://api.example.com/auth',\n * tokenDelivery: 'cookies',\n * admin: {\n * pathPrefix: '/admin',\n * },\n * });\n *\n * // Use admin operations\n * const users = await client.admin.getUsers({ page: 1 });\n * await client.admin.deleteUser('user-uuid');\n * ```\n */\n public readonly admin?: AdminOperations;\n\n /**\n * Create a new client instance.\n *\n * @param userConfig - Client configuration\n */\n constructor(userConfig: NAuthClientConfig) {\n const storage = userConfig.storage ?? defaultStorage();\n const defaultAdapter = userConfig.httpAdapter ?? new FetchAdapter();\n this.config = resolveConfig({ ...userConfig, storage }, defaultAdapter);\n this.tokenManager = new TokenManager(storage);\n this.eventEmitter = new EventEmitter();\n this.oauthStorage = getOauthStorage(storage);\n this.challengeRouter = new ChallengeRouter(this.config, this.oauthStorage);\n\n // Initialize admin operations if configured\n if (this.config.admin) {\n this.admin = new AdminOperations(this.config);\n }\n\n if (hasWindow()) {\n window.addEventListener('storage', this.handleStorageEvent);\n }\n }\n\n /**\n * Clean up resources.\n */\n dispose(): void {\n if (hasWindow()) {\n window.removeEventListener('storage', this.handleStorageEvent);\n }\n }\n\n /**\n * Login with identifier and password.\n *\n * @param identifier - Username or email\n * @param password - User password\n * @param recaptchaToken - Optional reCAPTCHA token for bot protection\n *\n * @example Basic login\n * ```typescript\n * const response = await client.login('user@example.com', 'password123');\n * ```\n *\n * @example With reCAPTCHA\n * ```typescript\n * const token = await grecaptcha.execute(siteKey, { action: 'login' });\n * const response = await client.login('user@example.com', 'password123', token);\n * ```\n */\n async login(identifier: string, password: string, recaptchaToken?: string): Promise<AuthResponse> {\n const loginEvent = { type: 'auth:login' as const, data: { identifier }, timestamp: Date.now() };\n this.eventEmitter.emit(loginEvent);\n\n try {\n const body: LoginRequest = { identifier, password, recaptchaToken };\n const response = await this.post<AuthResponse>(this.config.endpoints.login, body);\n await this.handleAuthResponse(response);\n\n // Emit success or challenge event\n if (response.challengeName) {\n const challengeEvent = { type: 'auth:challenge' as const, data: response, timestamp: Date.now() };\n this.eventEmitter.emit(challengeEvent);\n } else {\n const successEvent = { type: 'auth:success' as const, data: response, timestamp: Date.now() };\n this.eventEmitter.emit(successEvent);\n }\n\n // Auto-handle navigation\n await this.challengeRouter.handleAuthResponse(response, { source: 'login' });\n\n return response;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(NAuthErrorCode.AUTH_INVALID_CREDENTIALS, (error as Error).message || 'Login failed');\n const errorEvent = { type: 'auth:error' as const, data: authError, timestamp: Date.now() };\n this.eventEmitter.emit(errorEvent);\n throw authError;\n }\n }\n\n /**\n * Signup with credentials.\n */\n async signup(payload: SignupRequest): Promise<AuthResponse> {\n this.eventEmitter.emit({ type: 'auth:signup', data: { email: payload.email }, timestamp: Date.now() });\n\n try {\n const response = await this.post<AuthResponse>(this.config.endpoints.signup, payload);\n await this.handleAuthResponse(response);\n\n // Emit success or challenge event\n if (response.challengeName) {\n this.eventEmitter.emit({ type: 'auth:challenge', data: response, timestamp: Date.now() });\n } else {\n this.eventEmitter.emit({ type: 'auth:success', data: response, timestamp: Date.now() });\n }\n\n // Auto-handle navigation\n await this.challengeRouter.handleAuthResponse(response, { source: 'signup' });\n\n return response;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(NAuthErrorCode.AUTH_INVALID_CREDENTIALS, (error as Error).message || 'Signup failed');\n this.eventEmitter.emit({ type: 'auth:error', data: authError, timestamp: Date.now() });\n throw authError;\n }\n }\n\n /**\n * Refresh tokens manually.\n *\n * @throws {NAuthClientError} When refresh fails (e.g., session expired)\n *\n * @example\n * ```typescript\n * try {\n * await client.refreshTokens();\n * } catch (error) {\n * // Session expired - user is already logged out automatically\n * router.navigate(['/login']);\n * }\n * ```\n */\n async refreshTokens(): Promise<TokenResponse> {\n const tokenDelivery = this.getTokenDeliveryMode();\n\n // Only check for refresh token in localStorage for JSON mode\n // In cookies mode, refresh token is in httpOnly cookie (backend manages it)\n if (tokenDelivery === 'json') {\n await this.tokenManager.assertHasRefreshToken();\n }\n\n const body =\n tokenDelivery === 'json'\n ? { refreshToken: (await this.tokenManager.getTokens()).refreshToken }\n : { refreshToken: '' };\n const refreshFn = async () => {\n // In cookies mode, refresh token is sent via httpOnly cookie (no access token needed, auth=false)\n // In JSON mode, refresh token is in body (no access token needed, auth=false)\n // Refresh endpoint is PUBLIC - it doesn't need an access token\n return this.post<TokenResponse>(this.config.endpoints.refresh, body, false);\n };\n\n try {\n // Cookies mode MUST NEVER persist tokens to storage, even if a misconfigured backend\n // accidentally returns tokens in the response body (security footgun).\n const tokens = await this.tokenManager.refreshOnce(refreshFn, { persist: tokenDelivery === 'json' });\n this.config.onTokenRefresh?.();\n this.eventEmitter.emit({ type: 'auth:refresh', data: { success: true }, timestamp: Date.now() });\n return tokens;\n } catch (error) {\n // Handle session expiration (401 error)\n // Clear local auth state to prevent isAuthenticated() from returning true with stale data\n if (error instanceof NAuthClientError && error.statusCode === 401) {\n await this.clearLocalAuthState();\n this.config.onSessionExpired?.();\n this.eventEmitter.emit({ type: 'auth:session_expired', data: {}, timestamp: Date.now() });\n }\n throw error;\n }\n }\n\n // ============================================================================\n // Local state management (no network)\n // ============================================================================\n\n /**\n * Clear all local auth state without making any network requests.\n *\n * WHY:\n * - When refresh fails with 401 (session expired), clients should immediately drop any cached\n * auth state (user + tokens) to prevent \"sticky auth\" across hard reloads.\n * - In cookie delivery modes, httpOnly cookies can only be cleared by the backend; this method\n * only clears client-side state (e.g., cached user + persisted tokens in JSON mode).\n *\n * IMPORTANT: Also clears any pending challenge sessions to prevent ghost states where the UI\n * shows a challenge screen but the backend session is invalid.\n *\n * @param options - Optional behavior flags\n * @returns Promise that resolves when local state is cleared\n *\n * @example\n * ```typescript\n * // Called by framework adapters/interceptors when refresh fails with 401\n * await client.clearLocalAuthState();\n * ```\n */\n async clearLocalAuthState(options?: { forgetDevice?: boolean }): Promise<void> {\n await this.clearAuthState(options?.forgetDevice ?? false);\n // ============================================================================\n // IMPORTANT: Clear challenge session to prevent ghost states\n // ============================================================================\n // WHY: If a user's session expires while they have a pending challenge (MFA, email verification, etc.),\n // the challenge session token becomes invalid. We must clear it to prevent the UI from showing\n // challenge screens that will fail. This fixes the \"ghost session\" issue where users get stuck\n // in challenge flows with stale auth data.\n await this.clearChallenge();\n }\n\n /**\n * Logout current session.\n *\n * Uses GET request to avoid CSRF token issues.\n *\n * @param forgetDevice - If true, also untrust the device (require MFA on next login)\n */\n async logout(forgetDevice?: boolean): Promise<void> {\n const queryParams = forgetDevice ? '?forgetMe=true' : '';\n try {\n await this.get(this.config.endpoints.logout + queryParams, true);\n } catch (error) {\n // Ignore logout errors (session might already be invalid)\n console.warn('[nauth] Logout request failed (session may already be invalid):', error);\n } finally {\n // Always clear local state even if request fails\n // Pass forgetDevice flag to clear device token in JSON mode\n await this.clearAuthState(forgetDevice);\n // Also clear any pending challenge sessions\n await this.clearChallenge();\n this.eventEmitter.emit({\n type: 'auth:logout',\n data: { forgetDevice: !!forgetDevice, global: false },\n timestamp: Date.now(),\n });\n }\n }\n\n /**\n * Logout all sessions.\n *\n * Revokes all active sessions for the current user across all devices.\n * Optionally revokes all trusted devices if forgetDevices is true.\n *\n * @param forgetDevices - If true, also revokes all trusted devices (default: false)\n * @returns Number of sessions revoked\n */\n async logoutAll(forgetDevices?: boolean): Promise<{ revokedCount: number }> {\n try {\n const payload: LogoutAllRequest = {\n forgetDevices: forgetDevices ?? false,\n };\n const result = await this.post<{ message: string; revokedCount: number }>(\n this.config.endpoints.logoutAll,\n payload,\n true,\n );\n // Clear device token in JSON mode if forgetDevices is true\n await this.clearAuthState(forgetDevices);\n // Also clear any pending challenge sessions\n await this.clearChallenge();\n this.eventEmitter.emit({\n type: 'auth:logout',\n data: { forgetDevice: !!forgetDevices, global: true },\n timestamp: Date.now(),\n });\n return { revokedCount: result.revokedCount };\n } catch (error) {\n // If request fails, still clear local state\n await this.clearAuthState(forgetDevices);\n // Also clear any pending challenge sessions\n await this.clearChallenge();\n this.eventEmitter.emit({\n type: 'auth:logout',\n data: { forgetDevice: !!forgetDevices, global: true },\n timestamp: Date.now(),\n });\n throw error;\n }\n }\n\n /**\n * Respond to a challenge.\n *\n * Validates challenge response data before sending to backend.\n * Provides helpful error messages for common validation issues.\n *\n * @param response - Challenge response data\n * @returns Auth response from backend\n * @throws {NAuthClientError} If validation fails\n */\n async respondToChallenge(response: ChallengeResponse): Promise<AuthResponse> {\n // Auto-inject deviceId if SDK has one stored and this is MFA verification\n // This allows SDK to handle device selection internally - consumers don't need to pass deviceId\n if (\n this.selectedDeviceId !== undefined &&\n response.type === AuthChallenge.MFA_REQUIRED &&\n (response.method === 'totp' || response.method === 'passkey')\n ) {\n // TypeScript knows response is MFACodeResponse or MFAPasskeyResponse at this point\n // Both have deviceId?: number property, so we can safely assign it\n const mfaResponse = response as MFACodeResponse | MFAPasskeyResponse;\n mfaResponse.deviceId = this.selectedDeviceId;\n }\n\n // Validate TOTP setup requires both secret and code\n if (response.type === AuthChallenge.MFA_SETUP_REQUIRED && response.method === 'totp') {\n const setupData = response.setupData;\n if (!setupData || typeof setupData !== 'object') {\n throw new NAuthClientError(\n NAuthErrorCode.VALIDATION_FAILED,\n 'TOTP setup requires setupData with both secret and code',\n { details: { field: 'setupData' } },\n );\n }\n\n const secret = setupData['secret'];\n const code = setupData['code'];\n\n if (!secret || typeof secret !== 'string') {\n throw new NAuthClientError(\n NAuthErrorCode.VALIDATION_FAILED,\n 'TOTP setup requires secret in setupData. Make sure to include the secret from getSetupData() response.',\n { details: { field: 'secret' } },\n );\n }\n\n if (!code || typeof code !== 'string') {\n throw new NAuthClientError(\n NAuthErrorCode.VALIDATION_FAILED,\n 'TOTP setup requires code in setupData. Please enter the verification code from your authenticator app.',\n { details: { field: 'code' } },\n );\n }\n }\n\n try {\n const result = await this.post<AuthResponse>(this.config.endpoints.respondChallenge, response);\n await this.handleAuthResponse(result);\n\n // Clear selected device on successful authentication (no more challenges)\n if (result.user && !result.challengeName) {\n this.selectedDeviceId = undefined;\n }\n\n // Emit success or challenge event\n if (result.challengeName) {\n const challengeEvent = { type: 'auth:challenge' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(challengeEvent);\n } else {\n const successEvent = { type: 'auth:success' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(successEvent);\n }\n\n // Auto-handle navigation\n await this.challengeRouter.handleAuthResponse(result, { source: 'challenge' });\n\n return result;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(\n NAuthErrorCode.CHALLENGE_INVALID,\n (error as Error).message || 'Challenge response failed',\n );\n const errorEvent = { type: 'auth:error' as const, data: authError, timestamp: Date.now() };\n this.eventEmitter.emit(errorEvent);\n throw authError;\n }\n }\n\n /**\n * Select an MFA device for verification\n *\n * Call this when user selects a specific device from the MFA selector UI.\n * SDK stores the deviceId internally and auto-injects it when respondToChallenge() is called.\n *\n * @param deviceId - ID of the device user selected\n *\n * @example\n * ```typescript\n * // User clicks \"Microsoft Authenticator\" button\n * client.selectMFADevice(48);\n *\n * // Later, when submitting OTP code:\n * await client.respondToChallenge({\n * type: 'MFA_REQUIRED',\n * session: 'abc123',\n * method: 'totp',\n * code: '123456',\n * // SDK auto-injects deviceId=48 here!\n * });\n * ```\n */\n selectMFADevice(deviceId: number): void {\n this.selectedDeviceId = deviceId;\n }\n\n /**\n * Get available MFA devices from challenge response\n *\n * Returns array of devices for methods that support multiple devices (TOTP, Passkey).\n * Use this to render device selection UI only.\n *\n * @param challenge - Challenge response from login/signup\n * @returns Array of MFA devices with id, name, and type\n *\n * @example\n * ```typescript\n * const devices = client.getMFADevices(challengeResponse);\n * // Returns: [\n * // { id: 48, name: \"Microsoft Authenticator\", type: \"totp\" },\n * // { id: 3, name: \"Google Authenticator\", type: \"totp\" }\n * // ]\n * ```\n */\n getMFADevices(challenge: AuthResponse): Array<{ id: number; name: string; type: string }> {\n const devices = challenge.challengeParameters?.['devices'];\n if (Array.isArray(devices)) {\n return devices as Array<{ id: number; name: string; type: string }>;\n }\n return [];\n }\n\n /**\n * Clear any selected MFA device\n *\n * Useful if user navigates back to device selector or cancels MFA flow.\n */\n clearSelectedDevice(): void {\n this.selectedDeviceId = undefined;\n }\n\n /**\n * Resend a challenge code.\n */\n async resendCode(session: string): Promise<{ destination: string }> {\n const payload: ResendCodeRequest = { session };\n return this.post<{ destination: string }>(this.config.endpoints.resendCode, payload);\n }\n\n /**\n * Get setup data for MFA.\n *\n * Returns method-specific setup information:\n * - TOTP: { secret, qrCode, manualEntryKey }\n * - SMS: { maskedPhone }\n * - Email: { maskedEmail }\n * - Passkey: WebAuthn registration options\n *\n * @param session - Challenge session token\n * @param method - MFA method to set up\n * @returns Setup data wrapped in GetSetupDataResponse\n */\n async getSetupData(session: string, method: GetSetupDataRequest['method']): Promise<GetSetupDataResponse> {\n const payload: GetSetupDataRequest = { session, method };\n return this.post<GetSetupDataResponse>(this.config.endpoints.getSetupData, payload);\n }\n\n /**\n * Get challenge data (e.g., WebAuthn options).\n *\n * Returns challenge-specific data for verification flows.\n *\n * @param session - Challenge session token\n * @param method - Challenge method to get data for\n * @returns Challenge data wrapped in GetChallengeDataResponse\n */\n async getChallengeData(\n session: string,\n method: GetChallengeDataRequest['method'],\n ): Promise<GetChallengeDataResponse> {\n const payload: GetChallengeDataRequest = { session, method };\n return this.post<GetChallengeDataResponse>(this.config.endpoints.getChallengeData, payload);\n }\n\n /**\n * Get current user profile.\n */\n async getProfile(): Promise<AuthUser> {\n const profile = await this.get<AuthUser>(this.config.endpoints.profile, true);\n await this.setUser(profile);\n return profile;\n }\n\n /**\n * Update user profile.\n */\n async updateProfile(updates: UpdateProfileRequest): Promise<AuthUser> {\n const updated = await this.put<AuthUser>(this.config.endpoints.updateProfile, updates, true);\n await this.setUser(updated);\n return updated;\n }\n\n /**\n * Change user password.\n */\n async changePassword(oldPassword: string, newPassword: string): Promise<void> {\n const payload: ChangePasswordRequest = { oldPassword, newPassword };\n await this.post(this.config.endpoints.changePassword, payload, true);\n }\n\n /**\n * Request a password reset code (forgot password).\n */\n async forgotPassword(identifier: string): Promise<ForgotPasswordResponse> {\n const payload: ForgotPasswordRequest = { identifier };\n return this.post<ForgotPasswordResponse>(this.config.endpoints.forgotPassword, payload);\n }\n\n /**\n * Confirm a password reset code and set a new password.\n */\n async confirmForgotPassword(\n identifier: string,\n code: string,\n newPassword: string,\n ): Promise<ConfirmForgotPasswordResponse> {\n const payload: ConfirmForgotPasswordRequest = { identifier, code, newPassword };\n const result = await this.post<ConfirmForgotPasswordResponse>(this.config.endpoints.confirmForgotPassword, payload);\n // ============================================================================\n // IMPORTANT: Password reset revokes all sessions\n // ============================================================================\n // WHY: The backend invalidates all sessions as a security measure. Clearing local auth state avoids\n // stale UI (e.g., still showing social-only/no-password state from cached user) and prevents the\n // client from attempting further authenticated calls with invalid tokens/cookies.\n await this.clearAuthState(false);\n return result;\n }\n\n /**\n * Reset password with verification code (works for both admin-initiated and user-initiated resets).\n *\n * NOTE:\n * - Links (when provided by the backend email provider) include the same verification code as a query param\n * (e.g., `...?code=123456`) so consumer apps stay code-only and consistent.\n *\n * WHY: Generic method that works for both admin-initiated (adminResetPassword) and\n * user-initiated (forgotPassword) password resets. Uses same backend endpoint.\n *\n * @param identifier - User identifier (email, username, phone)\n * @param code - Verification code from email/SMS (6-10 digits)\n * @param newPassword - New password\n * @returns Success response\n * @throws {NAuthClientError} When reset fails\n *\n * @example\n * ```typescript\n * await client.resetPasswordWithCode('user@example.com', '123456', 'NewPass123!');\n * ```\n */\n async resetPasswordWithCode(\n identifier: string,\n code: string,\n newPassword: string,\n ): Promise<ResetPasswordWithCodeResponse> {\n const payload: ResetPasswordWithCodeRequest = {\n identifier,\n code,\n newPassword,\n };\n\n const result = await this.post<ResetPasswordWithCodeResponse>(\n this.config.endpoints.confirmAdminResetPassword,\n payload,\n );\n\n // ============================================================================\n // IMPORTANT: Password reset revokes all sessions\n // ============================================================================\n // WHY: The backend invalidates all sessions as a security measure. Clearing local auth state avoids\n // stale UI (e.g., still showing social-only/no-password state from cached user) and prevents the\n // client from attempting further authenticated calls with invalid tokens/cookies.\n await this.clearAuthState(false);\n\n return result;\n }\n\n /**\n * Get MFA status for current user.\n *\n * @returns Promise of MFA status\n *\n * @example\n * ```typescript\n * const status = await this.client.getMfaStatus();\n * console.log('MFA enabled:', status.enabled);\n * ```\n */\n async getMfaStatus(): Promise<MFAStatus> {\n return this.get<MFAStatus>(this.config.endpoints.mfaStatus, true);\n }\n\n /**\n * Get MFA devices.\n *\n * @returns Promise of MFA devices response\n *\n * @example\n * ```typescript\n * const result = await client.getMfaDevices();\n * console.log('Devices:', result.devices);\n * ```\n */\n async getMfaDevices(): Promise<GetMFADevicesResponse> {\n return this.get<GetMFADevicesResponse>(this.config.endpoints.mfaDevices, true);\n }\n\n /**\n * Setup MFA device (authenticated user).\n *\n * Returns method-specific setup information:\n * - TOTP: { secret, qrCode, manualEntryKey }\n * - SMS: { maskedPhone } or { deviceId, autoCompleted: true }\n * - Email: { maskedEmail } or { deviceId, autoCompleted: true }\n * - Passkey: WebAuthn registration options\n *\n * @param method - MFA method to set up\n * @returns Promise of setup data response\n *\n * @example\n * ```typescript\n * const result = await client.setupMfaDevice('totp');\n * console.log('QR Code:', result.setupData.qrCode);\n * ```\n */\n async setupMfaDevice(method: string): Promise<GetSetupDataResponse> {\n // Backend expects `methodName` (SetupMFADTO). We keep the public SDK method name `method`\n // for ergonomics, but serialize as `methodName` to match the API contract.\n return this.post<GetSetupDataResponse>(this.config.endpoints.mfaSetupData, { methodName: method }, true);\n }\n\n /**\n * Verify MFA setup (authenticated user).\n *\n * Completes MFA device setup by verifying the setup data. The structure of `setupData` varies by method:\n *\n * **TOTP:**\n * - Requires both `secret` (from `getSetupData()` response) and `code` (from authenticator app)\n * - Example: `{ secret: 'JBSWY3DPEHPK3PXP', code: '123456' }`\n *\n * **SMS:**\n * - Requires `phoneNumber` and `code` (verification code sent to phone)\n * - Example: `{ phoneNumber: '+1234567890', code: '123456' }`\n *\n * **Email:**\n * - Requires `code` (verification code sent to email)\n * - Example: `{ code: '123456' }`\n *\n * **Passkey:**\n * - Requires `credential` (WebAuthn credential from registration) and `expectedChallenge`\n * - Example: `{ credential: {...}, expectedChallenge: '...' }`\n *\n * @param method - MFA method ('totp', 'sms', 'email', 'passkey')\n * @param setupData - Method-specific setup verification data\n * @param deviceName - Optional device name (can also be included in setupData for some methods)\n * @returns Promise with device ID of the created MFA device\n *\n * @example TOTP Setup\n * ```typescript\n * // Step 1: Get setup data\n * const setupData = await client.setupMfaDevice('totp');\n * // Returns: { setupData: { secret: 'JBSWY3DPEHPK3PXP', qrCode: '...', ... } }\n *\n * // Step 2: User scans QR code and enters code from authenticator app\n * const code = '123456'; // From authenticator app\n *\n * // Step 3: Verify setup (requires both secret and code)\n * const result = await client.verifyMfaSetup('totp', {\n * secret: setupData.setupData.secret,\n * code: code,\n * }, 'Google Authenticator');\n * // Returns: { deviceId: 123 }\n * ```\n *\n * @example SMS Setup\n * ```typescript\n * const result = await client.verifyMfaSetup('sms', {\n * phoneNumber: '+1234567890', // Phone number receiving the code\n * code: '123456', // Code sent to phone\n * }, 'My iPhone');\n * ```\n *\n * @example Passkey Setup\n * ```typescript\n * const credential = await navigator.credentials.create({\n * publicKey: setupData.setupData.options\n * });\n * const result = await client.verifyMfaSetup('passkey', {\n * credential: credential,\n * expectedChallenge: setupData.setupData.challenge,\n * }, 'MacBook Pro');\n * ```\n */\n async verifyMfaSetup(\n method: string,\n setupData: Record<string, unknown>,\n deviceName?: string,\n ): Promise<{ deviceId: number }> {\n return this.post<{ deviceId: number }>(\n this.config.endpoints.mfaVerifySetup,\n // Backend expects `methodName` (SetupMFADTO). `deviceName` is optional and may be ignored\n // by consumer controllers depending on their DTO/validation strategy.\n { methodName: method, setupData, deviceName },\n true,\n );\n }\n\n /**\n * Remove ALL MFA devices for a specific method type.\n *\n * WARNING: This removes ALL devices of the specified method.\n * For example, if you have 3 TOTP devices, this will remove all 3.\n *\n * **Prefer `removeMfaDeviceById()`** to remove individual devices.\n *\n * @param method - MFA method type ('totp', 'sms', 'email', 'passkey')\n * @returns Success message\n *\n * @example\n * ```typescript\n * // Removes ALL TOTP devices (all authenticator apps)\n * await client.removeMfaDevice('totp');\n * ```\n */\n\n /**\n * Remove a single MFA device by device ID.\n *\n * @param deviceId - MFA device ID\n * @returns Removal response\n */\n async removeMfaDeviceById(deviceId: number): Promise<RemoveMFADeviceResponse> {\n const path = `${this.config.endpoints.mfaDevices}/${deviceId}`;\n return this.delete<RemoveMFADeviceResponse>(path, true);\n }\n\n /**\n * Set a specific MFA device as preferred.\n *\n * This marks the device as preferred and updates the user's preferred MFA method.\n *\n * @param deviceId - MFA device ID\n * @returns Success message\n */\n async setPreferredMfaDevice(deviceId: number): Promise<{ message: string }> {\n const path = `${this.config.endpoints.mfaDevices}/${deviceId}/preferred`;\n return this.post<{ message: string }>(path, {}, true);\n }\n\n /**\n * Generate backup codes.\n */\n async generateBackupCodes(): Promise<string[]> {\n const result = await this.post<{ codes: string[] }>(this.config.endpoints.mfaBackupCodes, {}, true);\n return result.codes;\n }\n\n /**\n * ============================================================================\n * Event System\n * ============================================================================\n */\n\n /**\n * Subscribe to authentication events.\n *\n * Emits events throughout the auth lifecycle for custom logic, analytics, or UI updates.\n *\n * @param event - Event type to listen for, or '*' for all events\n * @param listener - Callback function to handle the event\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * // Listen to successful authentication\n * const unsubscribe = client.on('auth:success', (event) => {\n * console.log('User logged in:', event.data.user);\n * analytics.track('login_success');\n * });\n *\n * // Listen to all events\n * client.on('*', (event) => {\n * console.log('Auth event:', event.type, event.data);\n * });\n *\n * // Unsubscribe when done\n * unsubscribe();\n * ```\n */\n on(event: AuthEventType | '*', listener: AuthEventListener): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Unsubscribe from authentication events.\n *\n * @param event - Event type\n * @param listener - Callback function to remove\n */\n off(event: AuthEventType | '*', listener: AuthEventListener): void {\n this.eventEmitter.off(event, listener);\n }\n\n // ============================================================================\n // Social Authentication\n // ============================================================================\n\n /**\n * Start redirect-first social OAuth flow (web).\n *\n * This performs a browser navigation to:\n * `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`\n *\n * The backend:\n * - generates and stores CSRF state (cluster-safe)\n * - redirects the user to the provider\n * - completes OAuth on callback and sets cookies (or issues an exchange token)\n * - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)\n *\n * @param provider - OAuth provider ('google', 'apple', 'facebook')\n * @param options - Optional redirect options\n *\n * @example\n * ```typescript\n * await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });\n * ```\n */\n async loginWithSocial(provider: SocialProvider, options?: SocialLoginOptions): Promise<void> {\n // Emit event\n this.eventEmitter.emit({ type: 'oauth:started', data: { provider }, timestamp: Date.now() });\n\n if (hasWindow()) {\n const startPath = this.config.endpoints.socialRedirectStart.replace(':provider', provider);\n // Use buildUrl to ensure authPathPrefix is applied\n const fullUrl = this.buildUrl(startPath);\n const startUrl = new URL(fullUrl);\n\n const redirects = this.config.redirects;\n // Default returnTo to configured post-login success route (best-effort).\n // Consumers are expected to pass an explicit callback route (e.g. '/auth/callback').\n const returnTo = options?.returnTo ?? redirects?.loginSuccess ?? redirects?.success ?? '/';\n\n startUrl.searchParams.set('returnTo', returnTo);\n // Only include action when deviating from the default ('login').\n if (options?.action === 'link') {\n startUrl.searchParams.set('action', 'link');\n }\n if (typeof options?.appState === 'string' && options.appState.trim() !== '') {\n startUrl.searchParams.set('appState', options.appState);\n }\n\n // Serialize oauthParams as JSON string\n if (options?.oauthParams && Object.keys(options.oauthParams).length > 0) {\n startUrl.searchParams.set('oauthParams', JSON.stringify(options.oauthParams));\n }\n\n window.location.href = startUrl.toString();\n }\n }\n\n /**\n * Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.\n *\n * Used for `tokenDelivery: 'json'` or hybrid flows where the backend redirects back\n * with `exchangeToken` instead of setting cookies.\n *\n * @param exchangeToken - One-time exchange token from the callback URL\n * @returns AuthResponse\n */\n async exchangeSocialRedirect(exchangeToken: string): Promise<AuthResponse> {\n const token = exchangeToken?.trim();\n if (!token) {\n throw new NAuthClientError(NAuthErrorCode.CHALLENGE_INVALID, 'Missing exchangeToken');\n }\n const result = await this.post<AuthResponse>(this.config.endpoints.socialExchange, { exchangeToken: token });\n await this.handleAuthResponse(result);\n\n // Read appState from sessionStorage WITHOUT clearing (consumer may want to retrieve it later via getLastOauthState())\n const appState = await this.oauthStorage.getItem(OAUTH_STATE_KEY);\n\n // Auto-handle navigation with appState in context\n await this.challengeRouter.handleAuthResponse(result, {\n source: 'social',\n appState: appState ?? undefined,\n });\n\n return result;\n }\n\n /**\n * Verify native social token (mobile).\n */\n async verifyNativeSocial(request: SocialVerifyRequest): Promise<AuthResponse> {\n try {\n const path = this.config.endpoints.socialVerify.replace(':provider', request.provider);\n const result = await this.post<AuthResponse>(path, request);\n await this.handleAuthResponse(result);\n\n // Emit success or challenge event\n if (result.challengeName) {\n const challengeEvent = { type: 'auth:challenge' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(challengeEvent);\n } else {\n const successEvent = { type: 'auth:success' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(successEvent);\n }\n\n return result;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(\n NAuthErrorCode.SOCIAL_TOKEN_INVALID,\n (error as Error).message || 'Social verification failed',\n );\n const errorEvent = { type: 'auth:error' as const, data: authError, timestamp: Date.now() };\n this.eventEmitter.emit(errorEvent);\n throw authError;\n }\n }\n\n /**\n * Get linked accounts.\n */\n async getLinkedAccounts(): Promise<LinkedAccountsResponse> {\n return this.get<LinkedAccountsResponse>(this.config.endpoints.socialLinked, true);\n }\n\n /**\n * Link social account.\n */\n async linkSocialAccount(provider: string, code: string, state: string): Promise<{ message: string }> {\n return this.post<{ message: string }>(this.config.endpoints.socialLink, { provider, code, state }, true);\n }\n\n /**\n * Unlink social account.\n */\n async unlinkSocialAccount(provider: string): Promise<{ message: string }> {\n return this.post<{ message: string }>(this.config.endpoints.socialUnlink, { provider }, true);\n }\n\n /**\n * Trust current device.\n */\n async trustDevice(): Promise<{ deviceToken: string }> {\n const result = await this.post<{ deviceToken: string }>(this.config.endpoints.trustDevice, {}, true);\n\n // Only store device token in JSON mode (cookies mode uses httpOnly cookie)\n if (this.config.tokenDelivery === 'json' && result.deviceToken) {\n await this.setDeviceToken(result.deviceToken);\n }\n\n return result;\n }\n\n /**\n * Check if the current device is trusted.\n *\n * Returns whether the current device is trusted based on the device token\n * (from cookie in cookies mode, or header in JSON mode).\n *\n * This performs a server-side validation of the device token and checks:\n * - Device token exists and is valid\n * - Device token matches a trusted device record in the database\n * - Trust has not expired\n *\n * @returns Object with trusted status\n *\n * @example\n * ```typescript\n * const result = await client.isTrustedDevice();\n * if (result.trusted) {\n * console.log('This device is trusted');\n * }\n * ```\n */\n async isTrustedDevice(): Promise<{ trusted: boolean }> {\n return this.get<{ trusted: boolean }>(this.config.endpoints.isTrustedDevice, true);\n }\n\n /**\n * Get authentication audit history for current user.\n *\n * @param params - Optional query parameters (page, limit, eventType, etc.)\n * @returns Paginated audit history\n *\n * @example\n * ```typescript\n * const history = await client.getAuditHistory({\n * page: 1,\n * limit: 20,\n * eventTypes: ['LOGIN_SUCCESS'],\n * eventStatus: ['FAILURE'],\n * });\n * ```\n */\n async getAuditHistory(\n params?: Record<string, string | number | boolean | Array<string | number | boolean>>,\n ): Promise<AuditHistoryResponse> {\n const searchParams = new URLSearchParams();\n\n for (const [key, rawValue] of Object.entries(params ?? {})) {\n if (Array.isArray(rawValue)) {\n for (const item of rawValue) {\n searchParams.append(key, String(item));\n }\n continue;\n }\n\n searchParams.append(key, String(rawValue));\n }\n\n const query = searchParams.toString() ? `?${searchParams.toString()}` : '';\n const path = `${this.config.endpoints.auditHistory}${query}`;\n return this.get<AuditHistoryResponse>(path, true);\n }\n\n /**\n * Initialize client by hydrating state from storage.\n * Call this on app startup to restore auth state.\n */\n async initialize(): Promise<void> {\n const userJson = await this.config.storage.getItem(USER_KEY);\n if (userJson) {\n try {\n this.currentUser = JSON.parse(userJson) as AuthUser;\n this.config.onAuthStateChange?.(this.currentUser);\n } catch {\n // Invalid stored user - clear it\n await this.config.storage.removeItem(USER_KEY);\n }\n }\n }\n\n /**\n * Determine if user is authenticated (async - checks tokens).\n */\n async isAuthenticated(): Promise<boolean> {\n const tokens = await this.tokenManager.getTokens();\n return Boolean(tokens.accessToken);\n }\n\n /**\n * Determine if user is authenticated (sync - checks cached user).\n * Use this for guards and sync checks. Use `isAuthenticated()` for definitive check.\n */\n isAuthenticatedSync(): boolean {\n return this.currentUser !== null;\n }\n\n /**\n * Get current access token (may be null).\n */\n async getAccessToken(): Promise<string | null> {\n const tokens = await this.tokenManager.getTokens();\n return tokens.accessToken ?? null;\n }\n\n /**\n * Get current user (cached, sync).\n */\n getCurrentUser(): AuthUser | null {\n return this.currentUser;\n }\n\n /**\n * Get stored challenge session (for resuming challenge flows).\n */\n async getStoredChallenge(): Promise<AuthResponse | null> {\n const raw = await this.config.storage.getItem(CHALLENGE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as AuthResponse;\n } catch {\n return null;\n }\n }\n\n /**\n * Clear stored challenge session.\n */\n async clearStoredChallenge(): Promise<void> {\n await this.config.storage.removeItem(CHALLENGE_KEY);\n }\n\n /**\n * Internal: handle auth response (tokens or challenge).\n *\n * In cookies mode: Tokens are set as httpOnly cookies by backend, not stored in client storage.\n * In JSON mode: Tokens are stored in tokenManager for Authorization header.\n */\n private async handleAuthResponse(response: AuthResponse): Promise<void> {\n if (response.challengeName) {\n await this.persistChallenge(response);\n return;\n }\n\n // Only store tokens in JSON mode (cookies mode uses httpOnly cookies set by backend)\n if (this.config.tokenDelivery === 'json' && response.accessToken && response.refreshToken) {\n await this.tokenManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n accessTokenExpiresAt: response.accessTokenExpiresAt ?? 0,\n refreshTokenExpiresAt: response.refreshTokenExpiresAt ?? 0,\n });\n }\n\n // Device token handling (only for JSON mode - cookies mode uses httpOnly cookie)\n if (this.config.tokenDelivery === 'json' && response.deviceToken) {\n await this.setDeviceToken(response.deviceToken);\n }\n\n // Always store user info (needed for both modes)\n if (response.user) {\n const user = response.user as AuthUser;\n // WHY: Consumers often need to know if the current session was created via password\n // or via a specific social provider (google/apple/facebook).\n user.sessionAuthMethod = response.authMethod ?? null;\n await this.setUser(user);\n }\n\n await this.clearChallenge();\n }\n\n /**\n * Persist challenge state.\n */\n private async persistChallenge(challenge: AuthResponse): Promise<void> {\n await this.config.storage.setItem(CHALLENGE_KEY, JSON.stringify(challenge));\n }\n\n /**\n * Clear challenge state.\n */\n private async clearChallenge(): Promise<void> {\n await this.config.storage.removeItem(CHALLENGE_KEY);\n }\n\n /**\n * Persist user.\n */\n private async setUser(user: AuthUser): Promise<void> {\n this.currentUser = user;\n await this.config.storage.setItem(USER_KEY, JSON.stringify(user));\n this.config.onAuthStateChange?.(user);\n }\n\n /**\n * Clear all auth state.\n *\n * @param forgetDevice - If true, also clear device token (for JSON mode)\n */\n private async clearAuthState(forgetDevice?: boolean): Promise<void> {\n this.currentUser = null;\n await this.tokenManager.clearTokens();\n await this.config.storage.removeItem(USER_KEY);\n\n // Clear device token when forgetDevice is true\n if (forgetDevice) {\n // ============================================================================\n // Defensive cleanup: Always attempt to remove device token from localStorage\n // ============================================================================\n // WHY: In JSON mode, device token should be in localStorage and must be cleared.\n // In cookies mode, device token shouldn't be in localStorage, but we attempt cleanup\n // anyway as a defensive measure in case of bugs or mode switches.\n try {\n await this.config.storage.removeItem(this.config.deviceTrust.storageKey);\n } catch {\n // Non-fatal: storage can fail in restricted environments (private mode, SSR, etc.)\n }\n }\n\n // ============================================================================\n // Clear OAuth state to prevent stale appState from being reused\n // ============================================================================\n // WHY: If user logs out or session expires during/after OAuth flow, the stored appState\n // should be cleared to prevent it from being incorrectly applied to a future login.\n try {\n await this.oauthStorage.removeItem(OAUTH_STATE_KEY);\n } catch {\n // Non-fatal: OAuth state is in sessionStorage which might fail in restricted environments\n }\n\n this.config.onAuthStateChange?.(null);\n }\n\n /**\n * Persist device token (json mode mobile).\n */\n private async setDeviceToken(token: string): Promise<void> {\n await this.config.storage.setItem(this.config.deviceTrust.storageKey, token);\n }\n\n /**\n * Determine token delivery mode for this environment.\n */\n private getTokenDeliveryMode(): 'json' | 'cookies' {\n return this.config.tokenDelivery;\n }\n\n /**\n * Build request URL by combining baseUrl with path.\n * Automatically prepends authPathPrefix if configured and not already in path.\n * @private\n */\n private buildUrl(path: string): string {\n // Prepend authPathPrefix if configured and path doesn't already start with it\n // Ensure path starts with '/' for proper prefix concatenation\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n const effectivePath =\n this.config.authPathPrefix && !normalizedPath.startsWith(this.config.authPathPrefix)\n ? `${this.config.authPathPrefix}${normalizedPath}`\n : normalizedPath;\n return `${this.config.baseUrl}${effectivePath}`;\n }\n\n /**\n * Build request headers for authentication.\n *\n * @param auth - Whether to include authentication headers\n * @param method - HTTP method (GET, POST, PUT, DELETE, PATCH)\n * @returns Headers object with auth, CSRF, and device trust headers\n * @private\n */\n private async buildHeaders(\n auth: boolean,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' = 'GET',\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n ...this.config.headers,\n };\n\n // Set Content-Type for mutating requests\n if (method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n // Add access token in JSON mode\n if (auth && this.config.tokenDelivery === 'json') {\n const accessToken = (await this.tokenManager.getTokens()).accessToken;\n if (accessToken) {\n headers['Authorization'] = `Bearer ${accessToken}`;\n }\n }\n\n // ============================================================================\n // Trusted Device Header (JSON mode)\n // ============================================================================\n // In cookies mode the device token is sent automatically via httpOnly cookie.\n // In JSON mode the backend expects the device token via a header (default: X-Device-Token).\n //\n // This is required for:\n // - Checking trust status (`isTrustedDevice`)\n // - Skipping MFA on future logins when a device is trusted\n //\n // We intentionally send it on all requests in JSON mode so the backend can\n // consistently associate requests with a trusted device when present.\n if (this.config.tokenDelivery === 'json') {\n try {\n const deviceToken = await this.config.storage.getItem(this.config.deviceTrust.storageKey);\n if (deviceToken) {\n headers[this.config.deviceTrust.headerName] = deviceToken;\n }\n } catch {\n // Non-fatal: storage can fail in restricted environments (private mode, SSR, etc.).\n }\n }\n\n // ============================================================================\n // CSRF Token (Cookies mode, mutating requests only)\n // ============================================================================\n // CSRF protection is required for mutating HTTP methods (POST, PUT, PATCH, DELETE)\n // to prevent cross-site request forgery attacks when using cookie-based auth.\n const mutatingMethods: readonly ('POST' | 'PUT' | 'PATCH' | 'DELETE')[] = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (\n this.config.tokenDelivery === 'cookies' &&\n hasWindow() &&\n (mutatingMethods as readonly string[]).includes(method)\n ) {\n const csrfToken = this.getCsrfToken();\n if (csrfToken) {\n headers[this.config.csrf.headerName] = csrfToken;\n }\n }\n\n return headers;\n }\n\n /**\n * Get CSRF token from cookie (browser only).\n * @private\n */\n private getCsrfToken(): string | null {\n if (!hasWindow() || typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`));\n return match ? decodeURIComponent(match[2]) : null;\n }\n\n /**\n * Execute GET request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async get<T>(path: string, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'GET');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'GET',\n url,\n headers,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Execute POST request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async post<T>(path: string, body: unknown, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'POST');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'POST',\n url,\n headers,\n body,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Execute PUT request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async put<T>(path: string, body: unknown, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'PUT');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'PUT',\n url,\n headers,\n body,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Execute DELETE request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async delete<T>(path: string, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'DELETE');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'DELETE',\n url,\n headers,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Handle cross-tab storage updates.\n */\n private readonly handleStorageEvent = (event: StorageEvent): void => {\n if (event.key === 'nauth_sync') {\n // Best-effort reload of user state\n this.config.storage\n .getItem(USER_KEY)\n .then((value: string | null) => (value ? (JSON.parse(value) as AuthUser) : null))\n .then((user: AuthUser | null) => {\n this.currentUser = user;\n this.config.onAuthStateChange?.(user);\n })\n .catch(() => {\n // ignore\n });\n }\n };\n\n /**\n * Get challenge router for manual navigation control.\n * Useful for guards that need to handle errors or build custom URLs.\n *\n * @returns ChallengeRouter instance\n *\n * @example\n * ```typescript\n * const router = client.getChallengeRouter();\n * await router.navigateToError('oauth');\n * ```\n */\n getChallengeRouter(): ChallengeRouter {\n return this.challengeRouter;\n }\n\n /**\n * Store OAuth appState from social redirect callback.\n *\n * This is called automatically by the social redirect callback guard\n * when appState is present in the callback URL. The stored state can\n * be retrieved using getLastOauthState().\n *\n * Stores in sessionStorage (ephemeral) for better security.\n *\n * @param appState - OAuth appState value from callback URL\n *\n * @example\n * ```typescript\n * await client.storeOauthState('invite-code-123');\n * ```\n */\n async storeOauthState(appState: string): Promise<void> {\n if (appState && appState.trim() !== '') {\n await this.oauthStorage.setItem(OAUTH_STATE_KEY, appState);\n }\n }\n\n /**\n * Get the last OAuth appState from social redirect callback.\n *\n * Returns the appState that was stored during the most recent social\n * login redirect callback. This is useful for restoring UI state,\n * applying invite codes, or tracking referral information.\n *\n * The state is automatically cleared after retrieval to prevent reuse.\n * Stored in sessionStorage (ephemeral) for better security.\n *\n * @returns The stored appState, or null if none exists\n *\n * @example\n * ```typescript\n * const appState = await client.getLastOauthState();\n * if (appState) {\n * // Apply invite code or restore UI state\n * console.log('OAuth state:', appState);\n * }\n * ```\n */\n async getLastOauthState(): Promise<string | null> {\n const stored = await this.oauthStorage.getItem(OAUTH_STATE_KEY);\n if (stored) {\n // Clear after retrieval to prevent reuse\n await this.oauthStorage.removeItem(OAUTH_STATE_KEY);\n return stored;\n }\n return null;\n }\n}\n","import { AuthResponse, AuthChallenge } from '../types/auth.types';\nimport { MFAMethod } from '../types/mfa.types';\n\n/**\n * Helper utilities for working with authentication challenges.\n *\n * These utilities reduce boilerplate in consumer applications by providing\n * type-safe access to challenge parameters and state detection.\n */\n\n/**\n * Check if a challenge requires phone collection (user has no phone number).\n *\n * @param challenge - Auth response with challenge\n * @returns True if phone collection is required\n *\n * @example\n * ```typescript\n * const challenge = auth.getCurrentChallenge();\n * if (challenge && requiresPhoneCollection(challenge)) {\n * // Show phone input form\n * }\n * ```\n */\nexport function requiresPhoneCollection(challenge: AuthResponse): boolean {\n if (challenge.challengeName !== AuthChallenge.VERIFY_PHONE) {\n return false;\n }\n\n const params = challenge.challengeParameters;\n return (params?.['requiresPhoneCollection'] as string) === 'true';\n}\n\n/**\n * Get masked destination (email or phone) from challenge parameters.\n *\n * For VERIFY_EMAIL/VERIFY_PHONE challenges, returns `codeDeliveryDestination`.\n * For MFA_REQUIRED challenges, returns `maskedEmail` or `maskedPhone` based on method.\n *\n * @param challenge - Auth response with challenge\n * @returns Masked destination string or null if not available\n *\n * @example\n * ```typescript\n * const destination = getMaskedDestination(challenge);\n * if (destination) {\n * console.log(`Code sent to ${destination}`);\n * }\n * ```\n */\nexport function getMaskedDestination(challenge: AuthResponse): string | null {\n const params = challenge.challengeParameters;\n if (!params) {\n return null;\n }\n\n const challengeName = challenge.challengeName;\n\n if (challengeName === AuthChallenge.MFA_REQUIRED) {\n // MFA_REQUIRED uses maskedEmail or maskedPhone based on preferredMethod\n const method = (params?.['preferredMethod'] || params?.['method']) as MFAMethod | undefined;\n if (method === 'sms') {\n return (params['maskedPhone'] as string) || null;\n }\n if (method === 'email') {\n return (params['maskedEmail'] as string) || null;\n }\n // Fallback: try both if method is not specified\n return (params['maskedPhone'] as string) || (params['maskedEmail'] as string) || null;\n }\n\n // VERIFY_EMAIL and VERIFY_PHONE use codeDeliveryDestination\n return (params['codeDeliveryDestination'] as string) || null;\n}\n\n/**\n * Get preferred MFA method from challenge parameters.\n *\n * @param challenge - Auth response with MFA_REQUIRED challenge\n * @returns MFA method or undefined if not available\n *\n * @example\n * ```typescript\n * const method = getMFAMethod(challenge);\n * if (method === 'totp') {\n * // Show TOTP input\n * }\n * ```\n */\nexport function getMFAMethod(challenge: AuthResponse): MFAMethod | undefined {\n if (challenge.challengeName !== AuthChallenge.MFA_REQUIRED) {\n return undefined;\n }\n\n const params = challenge.challengeParameters;\n return (params?.['preferredMethod'] || params?.['method']) as MFAMethod | undefined;\n}\n\n/**\n * Get challenge instructions from challenge parameters.\n *\n * @param challenge - Auth response with challenge\n * @returns Instructions string or undefined if not available\n *\n * @example\n * ```typescript\n * const instructions = getChallengeInstructions(challenge);\n * if (instructions) {\n * console.log(instructions);\n * }\n * ```\n */\nexport function getChallengeInstructions(challenge: AuthResponse): string | undefined {\n const params = challenge.challengeParameters;\n return params?.['instructions'] as string | undefined;\n}\n\n/**\n * Check if challenge is OTP-based (requires code input).\n *\n * @param challenge - Auth response with challenge\n * @returns True if challenge requires OTP code\n *\n * @example\n * ```typescript\n * if (isOTPChallenge(challenge)) {\n * // Show OTP input component\n * }\n * ```\n */\nexport function isOTPChallenge(challenge: AuthResponse): boolean {\n const challengeName = challenge.challengeName;\n return (\n challengeName === AuthChallenge.VERIFY_EMAIL ||\n challengeName === AuthChallenge.VERIFY_PHONE ||\n challengeName === AuthChallenge.MFA_REQUIRED\n );\n}\n"],"mappings":"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","names":["AuthChallenge","NAuthErrorCode","AuthAuditEventType","ACCESS_TOKEN_KEY","USER_KEY","CHALLENGE_KEY","OAUTH_STATE_KEY","hasWindow"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types/auth.types.ts","../src/types/error.types.ts","../src/types/audit.types.ts","../src/core/config.ts","../src/core/errors.ts","../src/core/refresh.ts","../src/storage/browser.ts","../src/storage/memory.ts","../src/core/events.ts","../src/adapters/fetch-adapter.ts","../src/core/challenge-router.ts","../src/core/admin-operations.ts","../src/core/client.ts","../src/core/challenge-helpers.ts"],"sourcesContent":["export * from './types';\nexport * from './core/client';\nexport * from './core/config';\nexport * from './core/errors';\nexport * from './core/events';\nexport * from './core/http-adapter';\nexport * from './core/challenge-helpers';\nexport * from './core/challenge-router';\nexport * from './core/admin-operations';\nexport * from './storage/interface';\nexport * from './storage/browser';\nexport * from './storage/memory';\nexport * from './adapters/fetch-adapter';\n","/**\n * Authentication challenge types returned by the backend.\n */\nexport enum AuthChallenge {\n VERIFY_EMAIL = 'VERIFY_EMAIL',\n VERIFY_PHONE = 'VERIFY_PHONE',\n MFA_REQUIRED = 'MFA_REQUIRED',\n MFA_SETUP_REQUIRED = 'MFA_SETUP_REQUIRED',\n FORCE_CHANGE_PASSWORD = 'FORCE_CHANGE_PASSWORD',\n}\n\n/**\n * Auth response returned by backend for all auth operations.\n * Either contains tokens/user or a challenge to complete.\n */\nimport { MFAMethod, MFAChallengeMethod } from './mfa.types';\n\nexport interface AuthResponse {\n user?: AuthUserSummary;\n accessToken?: string;\n refreshToken?: string;\n accessTokenExpiresAt?: number;\n refreshTokenExpiresAt?: number;\n /**\n * Authentication method used to create the current session.\n *\n * Examples:\n * - `password`\n * - `google`\n * - `apple`\n * - `facebook`\n */\n authMethod?: string;\n trusted?: boolean;\n deviceToken?: string;\n challengeName?: AuthChallenge;\n session?: string;\n challengeParameters?: Record<string, unknown>;\n sub?: string;\n}\n\n/**\n * Minimal user information returned inside auth responses.\n */\nexport interface AuthUserSummary {\n sub: string;\n email: string;\n firstName?: string | null;\n lastName?: string | null;\n phone?: string | null;\n isEmailVerified: boolean;\n isPhoneVerified?: boolean;\n socialProviders?: string[] | null;\n hasPasswordHash?: boolean;\n}\n\n/**\n * Token response returned by refresh endpoint.\n */\nexport interface TokenResponse {\n accessToken: string;\n refreshToken: string;\n accessTokenExpiresAt: number;\n refreshTokenExpiresAt: number;\n}\n\n/**\n * Signup request payload.\n */\nexport interface SignupRequest {\n email: string;\n password: string;\n firstName?: string;\n lastName?: string;\n phone?: string;\n metadata?: Record<string, unknown>;\n /**\n * Optional reCAPTCHA token for bot protection.\n * - v2: Token from visible checkbox challenge\n * - v3: Token from invisible/automatic challenge\n * - If required by server but not provided, request will fail with RECAPTCHA_REQUIRED error\n */\n recaptchaToken?: string;\n}\n\n/**\n * Login request payload.\n */\nexport interface LoginRequest {\n identifier: string;\n password: string;\n /**\n * Optional reCAPTCHA token for bot protection.\n * - v2: Token from visible checkbox challenge\n * - v3: Token from invisible/automatic challenge\n * - If required by server but not provided, request will fail with RECAPTCHA_REQUIRED error\n */\n recaptchaToken?: string;\n}\n\n/**\n * Logout request payload.\n */\nexport interface LogoutRequest {\n forgetMe?: boolean;\n}\n\n/**\n * Logout all sessions request payload.\n */\nexport interface LogoutAllRequest {\n /**\n * Whether to also revoke all trusted devices\n * Default: false (devices remain trusted)\n */\n forgetDevices?: boolean;\n}\n\n/**\n * Resend code request payload.\n */\nexport interface ResendCodeRequest {\n session: string;\n}\n\n/**\n * MFA setup data request payload during challenge flows.\n */\nexport interface GetSetupDataRequest {\n session: string;\n method: MFAMethod;\n}\n\n/**\n * Challenge data request payload (e.g., passkey options).\n */\nexport interface GetChallengeDataRequest {\n session: string;\n method: MFAChallengeMethod;\n}\n\n/**\n * Unified challenge response discriminated union.\n */\nexport type ChallengeResponse =\n | VerifyEmailResponse\n | VerifyPhoneCollectResponse\n | VerifyPhoneCodeResponse\n | MFACodeResponse\n | MFAPasskeyResponse\n | MFASetupResponse\n | ForceChangePasswordResponse;\n\n/**\n * Base challenge response shape.\n */\nexport interface BaseChallengeResponse {\n session: string;\n}\n\nexport interface VerifyEmailResponse extends BaseChallengeResponse {\n type: AuthChallenge.VERIFY_EMAIL;\n code: string;\n}\n\nexport interface VerifyPhoneCollectResponse extends BaseChallengeResponse {\n type: AuthChallenge.VERIFY_PHONE;\n phone: string;\n}\n\nexport interface VerifyPhoneCodeResponse extends BaseChallengeResponse {\n type: AuthChallenge.VERIFY_PHONE;\n code: string;\n}\n\nexport interface MFACodeResponse extends BaseChallengeResponse {\n type: AuthChallenge.MFA_REQUIRED;\n method: MFAMethod;\n code: string;\n deviceId?: number;\n}\n\nexport interface MFAPasskeyResponse extends BaseChallengeResponse {\n type: AuthChallenge.MFA_REQUIRED;\n method: 'passkey';\n credential: Record<string, unknown>;\n deviceId?: number;\n}\n\nexport interface MFASetupResponse extends BaseChallengeResponse {\n type: AuthChallenge.MFA_SETUP_REQUIRED;\n method: MFAMethod;\n setupData: Record<string, unknown>;\n}\n\nexport interface ForceChangePasswordResponse extends BaseChallengeResponse {\n type: AuthChallenge.FORCE_CHANGE_PASSWORD;\n newPassword: string;\n}\n\n// MFAMethod and MFAChallengeMethod are imported from mfa.types\n","/**\n * Standardized error codes mirroring backend AuthErrorCode.\n */\nexport enum NAuthErrorCode {\n // Authentication Errors\n AUTH_INVALID_CREDENTIALS = 'AUTH_INVALID_CREDENTIALS',\n AUTH_ACCOUNT_LOCKED = 'AUTH_ACCOUNT_LOCKED',\n AUTH_ACCOUNT_INACTIVE = 'AUTH_ACCOUNT_INACTIVE',\n AUTH_TOKEN_EXPIRED = 'AUTH_TOKEN_EXPIRED',\n AUTH_TOKEN_INVALID = 'AUTH_TOKEN_INVALID',\n AUTH_BEARER_NOT_ALLOWED = 'AUTH_BEARER_NOT_ALLOWED',\n AUTH_COOKIES_NOT_ALLOWED = 'AUTH_COOKIES_NOT_ALLOWED',\n AUTH_CSRF_TOKEN_INVALID = 'AUTH_CSRF_TOKEN_INVALID',\n AUTH_CSRF_TOKEN_MISSING = 'AUTH_CSRF_TOKEN_MISSING',\n AUTH_TOKEN_REUSE_DETECTED = 'AUTH_TOKEN_REUSE_DETECTED',\n AUTH_SESSION_NOT_FOUND = 'AUTH_SESSION_NOT_FOUND',\n AUTH_SESSION_EXPIRED = 'AUTH_SESSION_EXPIRED',\n\n // Signup Errors\n SIGNUP_DISABLED = 'SIGNUP_DISABLED',\n SIGNUP_EMAIL_EXISTS = 'SIGNUP_EMAIL_EXISTS',\n SIGNUP_USERNAME_EXISTS = 'SIGNUP_USERNAME_EXISTS',\n SIGNUP_PHONE_EXISTS = 'SIGNUP_PHONE_EXISTS',\n SIGNUP_WEAK_PASSWORD = 'SIGNUP_WEAK_PASSWORD',\n SIGNUP_PHONE_REQUIRED = 'SIGNUP_PHONE_REQUIRED',\n SIGNUP_NOT_ALLOWED = 'SIGNUP_NOT_ALLOWED',\n\n // Verification Errors\n VERIFY_CODE_INVALID = 'VERIFY_CODE_INVALID',\n VERIFY_CODE_EXPIRED = 'VERIFY_CODE_EXPIRED',\n VERIFY_TOO_MANY_ATTEMPTS = 'VERIFY_TOO_MANY_ATTEMPTS',\n VERIFY_ALREADY_VERIFIED = 'VERIFY_ALREADY_VERIFIED',\n\n // MFA Errors\n MFA_SETUP_REQUIRED = 'MFA_SETUP_REQUIRED',\n\n // Rate Limit Errors\n RATE_LIMIT_SMS = 'RATE_LIMIT_SMS',\n RATE_LIMIT_EMAIL = 'RATE_LIMIT_EMAIL',\n RATE_LIMIT_LOGIN = 'RATE_LIMIT_LOGIN',\n RATE_LIMIT_RESEND = 'RATE_LIMIT_RESEND',\n\n // Social Auth Errors\n SOCIAL_TOKEN_INVALID = 'SOCIAL_TOKEN_INVALID',\n SOCIAL_ACCOUNT_LINKED = 'SOCIAL_ACCOUNT_LINKED',\n SOCIAL_CONFIG_MISSING = 'SOCIAL_CONFIG_MISSING',\n SOCIAL_EMAIL_REQUIRED = 'SOCIAL_EMAIL_REQUIRED',\n SOCIAL_ACCOUNT_NOT_FOUND = 'SOCIAL_ACCOUNT_NOT_FOUND',\n\n // Challenge Errors\n CHALLENGE_EXPIRED = 'CHALLENGE_EXPIRED',\n CHALLENGE_INVALID = 'CHALLENGE_INVALID',\n CHALLENGE_TYPE_MISMATCH = 'CHALLENGE_TYPE_MISMATCH',\n CHALLENGE_MAX_ATTEMPTS = 'CHALLENGE_MAX_ATTEMPTS',\n CHALLENGE_ALREADY_COMPLETED = 'CHALLENGE_ALREADY_COMPLETED',\n\n // Validation Errors\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n VALIDATION_INVALID_PHONE = 'VALIDATION_INVALID_PHONE',\n VALIDATION_INVALID_EMAIL = 'VALIDATION_INVALID_EMAIL',\n VALIDATION_INVALID_PASSWORD = 'VALIDATION_INVALID_PASSWORD',\n\n // Password Errors\n PASSWORD_INCORRECT = 'PASSWORD_INCORRECT',\n PASSWORD_REUSED = 'PASSWORD_REUSED',\n PASSWORD_CHANGE_NOT_ALLOWED = 'PASSWORD_CHANGE_NOT_ALLOWED',\n WEAK_PASSWORD = 'SIGNUP_WEAK_PASSWORD',\n PASSWORD_RESET_CODE_INVALID = 'PASSWORD_RESET_CODE_INVALID',\n PASSWORD_RESET_CODE_EXPIRED = 'PASSWORD_RESET_CODE_EXPIRED',\n PASSWORD_RESET_MAX_ATTEMPTS = 'PASSWORD_RESET_MAX_ATTEMPTS',\n RATE_LIMIT_PASSWORD_RESET = 'RATE_LIMIT_PASSWORD_RESET',\n\n // Adaptive MFA\n SIGNIN_BLOCKED_HIGH_RISK = 'SIGNIN_BLOCKED_HIGH_RISK',\n\n // General Errors\n RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',\n FORBIDDEN = 'FORBIDDEN',\n INTERNAL_ERROR = 'INTERNAL_ERROR',\n SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE',\n}\n\n/**\n * Structured client error.\n */\nexport interface NAuthError {\n code: NAuthErrorCode;\n message: string;\n details?: Record<string, unknown>;\n timestamp?: string;\n statusCode?: number;\n}\n","/**\n * Audit event types.\n */\nexport enum AuthAuditEventType {\n // Authentication events\n LOGIN_SUCCESS = 'LOGIN_SUCCESS',\n LOGIN_FAILED = 'LOGIN_FAILED',\n LOGOUT = 'LOGOUT',\n TOKEN_REFRESH = 'TOKEN_REFRESH',\n TOKEN_REVOKED = 'TOKEN_REVOKED',\n\n // Registration events\n SIGNUP_SUCCESS = 'SIGNUP_SUCCESS',\n SIGNUP_FAILED = 'SIGNUP_FAILED',\n\n // Verification events\n EMAIL_VERIFIED = 'EMAIL_VERIFIED',\n PHONE_VERIFIED = 'PHONE_VERIFIED',\n VERIFICATION_FAILED = 'VERIFICATION_FAILED',\n\n // MFA events\n MFA_ENABLED = 'MFA_ENABLED',\n MFA_DISABLED = 'MFA_DISABLED',\n MFA_VERIFIED = 'MFA_VERIFIED',\n MFA_FAILED = 'MFA_FAILED',\n MFA_BACKUP_CODE_USED = 'MFA_BACKUP_CODE_USED',\n\n // Password events\n PASSWORD_CHANGED = 'PASSWORD_CHANGED',\n PASSWORD_RESET_REQUESTED = 'PASSWORD_RESET_REQUESTED',\n PASSWORD_RESET_COMPLETED = 'PASSWORD_RESET_COMPLETED',\n\n // Security events\n ACCOUNT_LOCKED = 'ACCOUNT_LOCKED',\n ACCOUNT_UNLOCKED = 'ACCOUNT_UNLOCKED',\n SUSPICIOUS_ACTIVITY = 'SUSPICIOUS_ACTIVITY',\n ADAPTIVE_MFA_TRIGGERED = 'ADAPTIVE_MFA_TRIGGERED',\n\n // Social auth events\n SOCIAL_LINK_SUCCESS = 'SOCIAL_LINK_SUCCESS',\n SOCIAL_LINK_FAILED = 'SOCIAL_LINK_FAILED',\n SOCIAL_UNLINK = 'SOCIAL_UNLINK',\n}\n\n/**\n * Audit event status.\n */\nexport type AuthAuditEventStatus = 'SUCCESS' | 'FAILURE' | 'INFO' | 'SUSPICIOUS';\n\n/**\n * Individual audit event record.\n */\nexport interface AuthAuditEvent {\n id: number;\n userId: number;\n eventType: AuthAuditEventType;\n eventStatus: AuthAuditEventStatus;\n riskFactor?: number | null;\n riskFactors?: string[] | null;\n adaptiveMfaTriggered?: boolean | null;\n ipAddress?: string | null;\n ipCountry?: string | null;\n ipCity?: string | null;\n ipLatitude?: number | null;\n ipLongitude?: number | null;\n userAgent?: string | null;\n platform?: string | null;\n browser?: string | null;\n deviceId?: string | null;\n deviceName?: string | null;\n deviceType?: string | null;\n sessionId?: number | null;\n challengeSessionId?: number | null;\n authMethod?: string | null;\n performedBy?: string | null;\n reason?: string | null;\n description?: string | null;\n metadata?: Record<string, unknown> | null;\n createdAt: string | Date;\n}\n\n/**\n * Paginated audit history response.\n */\nexport interface AuditHistoryResponse {\n data: AuthAuditEvent[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n","import {\n NAuthClientConfig,\n NAuthEndpoints,\n NAuthAdminEndpoints,\n NAuthStorageAdapter,\n TokenDeliveryMode,\n HttpAdapter,\n} from '../types/config.types';\n\n/**\n * Fully resolved configuration with all defaults applied.\n */\nexport type ResolvedNAuthClientConfig = Omit<\n NAuthClientConfig,\n 'endpoints' | 'storage' | 'tokenDelivery' | 'httpAdapter'\n> & {\n tokenDelivery: TokenDeliveryMode;\n endpoints: NAuthEndpoints;\n storage: NAuthStorageAdapter;\n httpAdapter: HttpAdapter;\n csrf: { cookieName: string; headerName: string };\n deviceTrust: { headerName: string; storageKey: string };\n headers: Record<string, string>;\n timeout: number;\n admin?: {\n pathPrefix: string;\n endpoints: NAuthAdminEndpoints;\n headers: Record<string, string>;\n };\n};\n\n/**\n * Default endpoint paths matching backend controller.\n */\nexport const defaultEndpoints: NAuthEndpoints = {\n login: '/login',\n signup: '/signup',\n logout: '/logout',\n logoutAll: '/logout/all',\n refresh: '/refresh',\n respondChallenge: '/respond-challenge',\n resendCode: '/challenge/resend',\n getSetupData: '/challenge/setup-data',\n getChallengeData: '/challenge/challenge-data',\n profile: '/profile',\n changePassword: '/change-password',\n forgotPassword: '/forgot-password',\n confirmForgotPassword: '/forgot-password/confirm',\n confirmAdminResetPassword: '/reset-password/confirm',\n mfaStatus: '/mfa/status',\n mfaDevices: '/mfa/devices',\n mfaSetupData: '/mfa/setup-data',\n mfaVerifySetup: '/mfa/verify-setup',\n mfaPreferred: '/mfa/devices/:deviceId/preferred',\n mfaBackupCodes: '/mfa/backup-codes/generate',\n socialLinked: '/social/linked',\n socialLink: '/social/link',\n socialUnlink: '/social/unlink',\n socialVerify: '/social/:provider/verify',\n socialRedirectStart: '/social/:provider/redirect',\n socialExchange: '/social/exchange',\n trustDevice: '/trust-device',\n isTrustedDevice: '/is-trusted-device',\n auditHistory: '/audit/history',\n updateProfile: '/profile',\n};\n\n/**\n * Default admin endpoint paths matching backend admin controller.\n */\nexport const defaultAdminEndpoints: NAuthAdminEndpoints = {\n signup: '/signup',\n signupSocial: '/signup-social',\n getUsers: '/users',\n getUser: '/users/:sub',\n deleteUser: '/users/:sub',\n disableUser: '/users/:sub/disable',\n enableUser: '/users/:sub/enable',\n forcePasswordChange: '/users/:sub/force-password-change',\n setPassword: '/set-password',\n resetPasswordInitiate: '/reset-password/initiate',\n getUserSessions: '/users/:sub/sessions',\n logoutAll: '/users/:sub/logout-all',\n getMfaStatus: '/users/:sub/mfa/status',\n getMfaDevices: '/users/:sub/mfa/devices',\n removeMfaDeviceById: '/mfa/devices/:deviceId',\n setPreferredMfaDevice: '/users/:sub/mfa/devices/:deviceId/preferred',\n setMfaExemption: '/mfa/exemption',\n getAuditHistory: '/audit/history',\n};\n\n/**\n * Normalize user config with defaults.\n *\n * @param config - User supplied config\n * @param defaultAdapter - Default HTTP adapter (FetchAdapter for vanilla, AngularHttpAdapter for Angular)\n * @returns Resolved config with defaults applied\n */\nexport const resolveConfig = (config: NAuthClientConfig, defaultAdapter: HttpAdapter): ResolvedNAuthClientConfig => {\n const resolvedEndpoints: NAuthEndpoints = {\n ...defaultEndpoints,\n ...(config.endpoints ?? {}),\n };\n\n // Resolve admin config if provided\n let resolvedAdmin: ResolvedNAuthClientConfig['admin'];\n if (config.admin) {\n const resolvedAdminEndpoints: NAuthAdminEndpoints = {\n ...defaultAdminEndpoints,\n ...(config.admin.endpoints ?? {}),\n };\n\n resolvedAdmin = {\n pathPrefix: config.admin.pathPrefix ?? '/admin',\n endpoints: resolvedAdminEndpoints,\n headers: {\n ...config.headers,\n ...(config.admin.headers ?? {}),\n },\n };\n }\n\n return {\n ...config,\n csrf: {\n cookieName: config.csrf?.cookieName ?? 'nauth_csrf_token',\n headerName: config.csrf?.headerName ?? 'x-csrf-token',\n },\n deviceTrust: {\n headerName: config.deviceTrust?.headerName ?? 'X-Device-Token',\n storageKey: config.deviceTrust?.storageKey ?? 'nauth_device_token',\n },\n headers: config.headers ?? {},\n timeout: config.timeout ?? 30000,\n endpoints: resolvedEndpoints,\n storage: config.storage as NAuthStorageAdapter,\n httpAdapter: config.httpAdapter ?? defaultAdapter,\n admin: resolvedAdmin,\n };\n};\n","import { NAuthError, NAuthErrorCode } from '../types/error.types';\n\n/**\n * Client-side error wrapper for SDK operations.\n *\n * Mirrors the backend NAuthException structure for consistent error handling.\n *\n * @example\n * ```typescript\n * try {\n * await client.login({ identifier: 'user@example.com', password: 'wrong' });\n * } catch (error) {\n * if (error instanceof NAuthClientError) {\n * console.log(error.code); // 'AUTH_INVALID_CREDENTIALS'\n * console.log(error.message); // 'Invalid credentials'\n * console.log(error.timestamp); // '2025-12-06T...'\n *\n * // Check specific error code\n * if (error.isCode(NAuthErrorCode.RATE_LIMIT_LOGIN)) {\n * const retryAfter = error.details?.retryAfter as number;\n * console.log(`Rate limited. Retry in ${retryAfter}s`);\n * }\n * }\n * }\n * ```\n */\nexport class NAuthClientError extends Error implements NAuthError {\n public readonly code: NAuthErrorCode;\n public readonly details?: Record<string, unknown>;\n public readonly statusCode?: number;\n public readonly timestamp: string;\n public readonly isNetworkError: boolean;\n\n /**\n * Create a new client error.\n *\n * @param code - Error code from NAuthErrorCode enum\n * @param message - Human-readable error message\n * @param options - Optional metadata including details, statusCode, timestamp, and network error flag\n */\n constructor(\n code: NAuthErrorCode,\n message: string,\n options?: {\n details?: Record<string, unknown>;\n statusCode?: number;\n timestamp?: string;\n isNetworkError?: boolean;\n },\n ) {\n super(message);\n this.code = code;\n this.details = options?.details;\n this.statusCode = options?.statusCode;\n this.timestamp = options?.timestamp || new Date().toISOString();\n this.isNetworkError = options?.isNetworkError ?? false;\n this.name = 'NAuthClientError';\n Object.setPrototypeOf(this, NAuthClientError.prototype);\n }\n\n /**\n * Check if error matches a specific error code.\n *\n * @param code - Error code to check against\n * @returns True if the error code matches\n *\n * @example\n * ```typescript\n * if (error.isCode(NAuthErrorCode.RATE_LIMIT_SMS)) {\n * // Handle SMS rate limit\n * }\n * ```\n */\n isCode(code: NAuthErrorCode): boolean {\n return this.code === code;\n }\n\n /**\n * Get error details/metadata.\n *\n * @returns Error details object or undefined\n *\n * @example\n * ```typescript\n * const details = error.getDetails();\n * if (details?.retryAfter) {\n * console.log(`Retry after ${details.retryAfter} seconds`);\n * }\n * ```\n */\n getDetails(): Record<string, unknown> | undefined {\n return this.details;\n }\n\n /**\n * Get the error code.\n *\n * @returns The error code enum value\n */\n getCode(): NAuthErrorCode {\n return this.code;\n }\n\n /**\n * Serialize error to JSON object.\n *\n * @returns Plain object representation\n *\n * @example\n * ```typescript\n * const errorJson = error.toJSON();\n * // { code: 'AUTH_INVALID_CREDENTIALS', message: '...', timestamp: '...', details: {...} }\n * ```\n */\n toJSON(): {\n code: string;\n message: string;\n timestamp: string;\n details?: Record<string, unknown>;\n statusCode?: number;\n } {\n return {\n code: this.code,\n message: this.message,\n timestamp: this.timestamp,\n details: this.details,\n statusCode: this.statusCode,\n };\n }\n}\n","import { TokenResponse } from '../types/auth.types';\nimport { NAuthStorageAdapter } from '../types/config.types';\nimport { NAuthClientError } from './errors';\nimport { NAuthErrorCode } from '../types/error.types';\n\nconst ACCESS_TOKEN_KEY = 'nauth_access_token';\nconst REFRESH_TOKEN_KEY = 'nauth_refresh_token';\nconst ACCESS_EXPIRES_AT_KEY = 'nauth_access_token_expires_at';\nconst REFRESH_EXPIRES_AT_KEY = 'nauth_refresh_token_expires_at';\nconst USER_KEY = 'nauth_user';\nconst CHALLENGE_KEY = 'nauth_challenge_session';\n\n/**\n * Token state persisted in storage.\n */\nexport interface TokenState {\n accessToken: string | null;\n refreshToken: string | null;\n accessTokenExpiresAt?: number | null;\n refreshTokenExpiresAt?: number | null;\n}\n\n/**\n * Manage token persistence and refresh queuing.\n */\nexport class TokenManager {\n private readonly storage: NAuthStorageAdapter;\n private refreshPromise: Promise<TokenResponse> | null = null;\n private readonly isBrowser = typeof window !== 'undefined';\n\n /**\n * @param storage - storage adapter\n */\n constructor(storage: NAuthStorageAdapter) {\n this.storage = storage;\n }\n\n /**\n * Load tokens from storage.\n */\n async getTokens(): Promise<TokenState> {\n const [accessToken, refreshToken, accessExpRaw, refreshExpRaw] = await Promise.all([\n this.storage.getItem(ACCESS_TOKEN_KEY),\n this.storage.getItem(REFRESH_TOKEN_KEY),\n this.storage.getItem(ACCESS_EXPIRES_AT_KEY),\n this.storage.getItem(REFRESH_EXPIRES_AT_KEY),\n ]);\n return {\n accessToken,\n refreshToken,\n accessTokenExpiresAt: accessExpRaw ? Number(accessExpRaw) : null,\n refreshTokenExpiresAt: refreshExpRaw ? Number(refreshExpRaw) : null,\n };\n }\n\n /**\n * Persist tokens.\n *\n * @param tokens - new token pair\n */\n async setTokens(tokens: TokenResponse): Promise<void> {\n await Promise.all([\n this.storage.setItem(ACCESS_TOKEN_KEY, tokens.accessToken),\n this.storage.setItem(REFRESH_TOKEN_KEY, tokens.refreshToken),\n this.storage.setItem(ACCESS_EXPIRES_AT_KEY, tokens.accessTokenExpiresAt.toString()),\n this.storage.setItem(REFRESH_EXPIRES_AT_KEY, tokens.refreshTokenExpiresAt.toString()),\n ]);\n this.broadcastStorage();\n }\n\n /**\n * Clear tokens and related auth state.\n */\n async clearTokens(): Promise<void> {\n await Promise.all([\n this.storage.removeItem(ACCESS_TOKEN_KEY),\n this.storage.removeItem(REFRESH_TOKEN_KEY),\n this.storage.removeItem(ACCESS_EXPIRES_AT_KEY),\n this.storage.removeItem(REFRESH_EXPIRES_AT_KEY),\n this.storage.removeItem(USER_KEY),\n this.storage.removeItem(CHALLENGE_KEY),\n ]);\n this.broadcastStorage();\n }\n\n /**\n * Ensure only one refresh in flight.\n *\n * @param refreshFn - function performing refresh request\n */\n async refreshOnce(\n refreshFn: () => Promise<TokenResponse>,\n options?: {\n /**\n * Whether to persist returned tokens to storage.\n *\n * WHY:\n * - JSON mode needs persisted tokens for Authorization headers.\n * - Cookies mode uses httpOnly cookies; persisting tokens would be a security footgun.\n */\n persist?: boolean;\n },\n ): Promise<TokenResponse> {\n const shouldPersist = options?.persist !== false;\n if (!this.refreshPromise) {\n this.refreshPromise = refreshFn()\n .then(async (tokens) => {\n if (shouldPersist) {\n await this.setTokens(tokens);\n }\n return tokens;\n })\n .catch((error) => {\n throw error;\n })\n .finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n\n /**\n * Validate that a refresh token exists before attempting refresh.\n */\n async assertHasRefreshToken(): Promise<void> {\n const state = await this.getTokens();\n if (!state.refreshToken) {\n throw new NAuthClientError(NAuthErrorCode.AUTH_SESSION_NOT_FOUND, 'No refresh token available');\n }\n }\n\n /**\n * Broadcast a no-op write to trigger storage listeners in other tabs.\n */\n private broadcastStorage(): void {\n if (!this.isBrowser) return;\n try {\n window.localStorage.setItem('nauth_sync', Date.now().toString());\n } catch {\n // Best-effort; ignore if storage unavailable\n }\n }\n}\n","import { NAuthStorageAdapter } from './interface';\n\n/**\n * Browser storage adapter wrapping localStorage or sessionStorage.\n */\nexport class BrowserStorage implements NAuthStorageAdapter {\n private readonly storage: Storage;\n\n /**\n * Create a browser storage adapter.\n *\n * @param storage - Storage implementation (localStorage by default)\n */\n constructor(storage: Storage = window.localStorage) {\n this.storage = storage;\n }\n\n async getItem(key: string): Promise<string | null> {\n return this.storage.getItem(key);\n }\n\n async setItem(key: string, value: string): Promise<void> {\n this.storage.setItem(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n this.storage.removeItem(key);\n }\n\n async clear(): Promise<void> {\n this.storage.clear();\n }\n}\n","import { NAuthStorageAdapter } from './interface';\n\n/**\n * In-memory storage adapter for SSR, tests, or environments without Web Storage.\n */\nexport class InMemoryStorage implements NAuthStorageAdapter {\n private readonly store = new Map<string, string>();\n\n async getItem(key: string): Promise<string | null> {\n return this.store.has(key) ? (this.store.get(key) as string) : null;\n }\n\n async setItem(key: string, value: string): Promise<void> {\n this.store.set(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n}\n","import type { AuthResponse } from '../types/auth.types';\nimport type { NAuthClientError } from './errors';\n\n/**\n * Authentication event types emitted by NAuthClient\n *\n * Events are emitted throughout the authentication lifecycle,\n * allowing applications to react to auth state changes.\n */\nexport type AuthEventType =\n | 'auth:login'\n | 'auth:signup'\n | 'auth:success'\n | 'auth:challenge'\n | 'auth:error'\n | 'auth:logout'\n | 'auth:refresh'\n | 'auth:session_expired'\n | 'oauth:started'\n | 'oauth:callback'\n | 'oauth:completed'\n | 'oauth:error';\n\n/**\n * Discriminated union of all authentication events with type-safe payloads\n *\n * Each event has a specific payload type for better type safety.\n */\nexport type AuthEvent =\n | AuthLoginEvent\n | AuthSignupEvent\n | AuthSuccessEvent\n | AuthChallengeEvent\n | AuthErrorEvent\n | AuthLogoutEvent\n | AuthRefreshEvent\n | AuthSessionExpiredEvent\n | OAuthStartedEvent\n | OAuthCallbackEvent\n | OAuthCompletedEvent\n | OAuthErrorEvent;\n\n/**\n * Login initiated event\n */\nexport interface AuthLoginEvent {\n type: 'auth:login';\n data: {\n identifier: string;\n };\n timestamp: number;\n}\n\n/**\n * Signup initiated event\n */\nexport interface AuthSignupEvent {\n type: 'auth:signup';\n data: {\n email: string;\n };\n timestamp: number;\n}\n\n/**\n * Successful authentication event\n */\nexport interface AuthSuccessEvent {\n type: 'auth:success';\n data: AuthResponse;\n timestamp: number;\n}\n\n/**\n * Challenge required event\n */\nexport interface AuthChallengeEvent {\n type: 'auth:challenge';\n data: AuthResponse;\n timestamp: number;\n}\n\n/**\n * Authentication error event\n */\nexport interface AuthErrorEvent {\n type: 'auth:error';\n data: NAuthClientError;\n timestamp: number;\n}\n\n/**\n * User logged out event\n */\nexport interface AuthLogoutEvent {\n type: 'auth:logout';\n data: {\n forgetDevice?: boolean;\n global?: boolean;\n };\n timestamp: number;\n}\n\n/**\n * Token refreshed event\n */\nexport interface AuthRefreshEvent {\n type: 'auth:refresh';\n data: {\n success: boolean;\n };\n timestamp: number;\n}\n\n/**\n * Session expired event (refresh token expired or invalid)\n *\n * Emitted when token refresh fails with 401, indicating the session\n * has expired and the user needs to re-authenticate.\n */\nexport interface AuthSessionExpiredEvent {\n type: 'auth:session_expired';\n data: Record<string, never>;\n timestamp: number;\n}\n\n/**\n * OAuth flow started event\n */\nexport interface OAuthStartedEvent {\n type: 'oauth:started';\n data: {\n provider: string;\n };\n timestamp: number;\n}\n\n/**\n * OAuth callback received event\n */\nexport interface OAuthCallbackEvent {\n type: 'oauth:callback';\n data: {\n provider: string;\n };\n timestamp: number;\n}\n\n/**\n * OAuth flow completed event\n */\nexport interface OAuthCompletedEvent {\n type: 'oauth:completed';\n data: AuthResponse;\n timestamp: number;\n}\n\n/**\n * OAuth error event\n */\nexport interface OAuthErrorEvent {\n type: 'oauth:error';\n data: NAuthClientError;\n timestamp: number;\n}\n\n/**\n * Event listener callback function\n */\nexport type AuthEventListener = (event: AuthEvent) => void;\n\n/**\n * Internal event emitter for authentication events\n *\n * Provides pub/sub functionality for auth lifecycle events.\n *\n * @internal\n */\nexport class EventEmitter {\n private listeners = new Map<AuthEventType | '*', Set<AuthEventListener>>();\n\n /**\n * Subscribe to an authentication event\n *\n * @param event - Event type or '*' for all events\n * @param listener - Callback function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = emitter.on('auth:success', (event) => {\n * console.log('User logged in:', event.data);\n * });\n *\n * // Later\n * unsubscribe();\n * ```\n */\n on(event: AuthEventType | '*', listener: AuthEventListener): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n\n // Return unsubscribe function\n return () => this.off(event, listener);\n }\n\n /**\n * Unsubscribe from an authentication event\n *\n * @param event - Event type or '*'\n * @param listener - Callback function to remove\n */\n off(event: AuthEventType | '*', listener: AuthEventListener): void {\n this.listeners.get(event)?.delete(listener);\n }\n\n /**\n * Emit an authentication event\n *\n * Notifies all listeners for the specific event type and wildcard listeners.\n *\n * @param event - Event to emit\n * @internal\n */\n emit(event: AuthEvent): void {\n const specificListeners = this.listeners.get(event.type);\n const wildcardListeners = this.listeners.get('*');\n\n // Emit to specific event listeners\n specificListeners?.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n console.error(`Error in ${event.type} event listener:`, error);\n }\n });\n\n // Emit to wildcard listeners\n wildcardListeners?.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n console.error(`Error in wildcard event listener:`, error);\n }\n });\n }\n\n /**\n * Remove all listeners\n *\n * @internal\n */\n clear(): void {\n this.listeners.clear();\n }\n}\n","import { HttpAdapter, HttpRequest, HttpResponse } from '../core/http-adapter';\nimport { NAuthClientError } from '../core/errors';\nimport { NAuthErrorCode } from '../types/error.types';\n\n/**\n * HTTP adapter using native fetch API.\n *\n * Suitable for:\n * - Vanilla JavaScript/TypeScript\n * - Node.js (with fetch polyfill or Node 18+)\n * - Environments without framework-specific HTTP clients\n *\n * @example\n * ```typescript\n * import { NAuthClient } from '@nauth-toolkit/client';\n * import { FetchAdapter } from '@nauth-toolkit/client/adapters/fetch';\n *\n * const client = new NAuthClient({\n * baseUrl: 'https://api.example.com/auth',\n * httpAdapter: new FetchAdapter(),\n * });\n * ```\n */\nexport class FetchAdapter implements HttpAdapter {\n /**\n * Execute HTTP request using native fetch.\n *\n * @param config - Request configuration\n * @returns Response with parsed data\n * @throws NAuthClientError if request fails\n */\n async request<T>(config: HttpRequest): Promise<HttpResponse<T>> {\n const fetchOptions: RequestInit = {\n method: config.method,\n headers: config.headers,\n signal: config.signal,\n credentials: config.credentials,\n };\n\n if (config.body !== undefined) {\n fetchOptions.body = JSON.stringify(config.body);\n }\n\n let response: Response;\n try {\n response = await fetch(config.url, fetchOptions);\n } catch (error) {\n throw new NAuthClientError(NAuthErrorCode.INTERNAL_ERROR, 'Network request failed', {\n isNetworkError: true,\n details: { url: config.url, message: (error as Error).message },\n });\n }\n\n const status = response.status;\n let data: unknown = null;\n const text = await response.text();\n if (text) {\n try {\n data = JSON.parse(text);\n } catch {\n data = text;\n }\n }\n\n // Extract headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n if (!response.ok) {\n const errorData = typeof data === 'object' && data !== null ? (data as Record<string, unknown>) : {};\n const code =\n typeof errorData['code'] === 'string' ? (errorData['code'] as NAuthErrorCode) : NAuthErrorCode.INTERNAL_ERROR;\n const message =\n typeof errorData['message'] === 'string'\n ? (errorData['message'] as string)\n : `Request failed with status ${status}`;\n const timestamp = typeof errorData['timestamp'] === 'string' ? (errorData['timestamp'] as string) : undefined;\n const details = errorData['details'] as Record<string, unknown> | undefined;\n\n throw new NAuthClientError(code, message, {\n statusCode: status,\n timestamp,\n details,\n });\n }\n\n return { data: data as T, status, headers };\n }\n}\n","import { AuthResponse, AuthChallenge } from '../types/auth.types';\nimport { ResolvedNAuthClientConfig } from './config';\nimport { AuthResponseContext, type NAuthStorageAdapter } from '../types/config.types';\n\nconst OAUTH_STATE_KEY = 'nauth_oauth_state';\n\n/**\n * Challenge router - handles automatic navigation after auth operations.\n *\n * This is internal to the SDK. Consumer apps interact via config options:\n * - `onAuthResponse` callback for full control (dialogs, etc.)\n * - `navigationHandler` for custom navigation\n * - `redirects.challengeRoutes` for custom route mapping\n * - `redirects.useSingleChallengeRoute` for query param mode\n *\n * @example Dialog-based app\n * ```typescript\n * {\n * onAuthResponse: (response, context) => {\n * if (response.challengeName) {\n * dialog.open(ChallengeComponent, { data: response });\n * }\n * }\n * }\n * ```\n *\n * @example Custom routes\n * ```typescript\n * {\n * redirects: {\n * challengeRoutes: {\n * [AuthChallenge.MFA_REQUIRED]: '/auth/mfa',\n * }\n * }\n * }\n * ```\n *\n * @example Single route with query param\n * ```typescript\n * {\n * redirects: {\n * useSingleChallengeRoute: true\n * }\n * }\n * ```\n */\nexport class ChallengeRouter {\n constructor(\n private config: ResolvedNAuthClientConfig,\n private oauthStorage: NAuthStorageAdapter,\n ) {}\n\n /**\n * Handle auth response - either call callback or auto-navigate.\n *\n * @param response - Auth response from backend\n * @param context - Context about the auth operation\n */\n async handleAuthResponse(response: AuthResponse, context: AuthResponseContext): Promise<void> {\n // If custom handler provided, delegate to app\n if (this.config.onAuthResponse) {\n await this.config.onAuthResponse(response, context);\n return;\n }\n\n // Auto-navigate based on response\n if (response.challengeName) {\n await this.navigateToChallenge(response);\n } else {\n // Retrieve stored appState for social redirect flows (when not already in context)\n const queryParams = context.appState ? { appState: context.appState } : await this.getStoredOauthState();\n await this.navigateToSuccess(queryParams, context);\n }\n }\n\n /**\n * Resolve the configured success URL based on context and explicit override keys.\n *\n * IMPORTANT:\n * - `null` explicitly disables auto-navigation (caller should rely on framework events / custom routing).\n * - `undefined` / missing keys fall back to other configured values or defaults.\n * - Falls back to legacy `redirects.success` when new keys are not set.\n *\n * @param context - Auth response context\n * @returns URL string, or null when auto-navigation is disabled\n */\n private resolveSuccessUrl(context?: AuthResponseContext): string | null {\n const redirects = this.config.redirects;\n if (!redirects) {\n return '/';\n }\n\n const isSignup = context?.source === 'signup';\n\n if (isSignup) {\n if (redirects.signupSuccess === null) return null;\n if (typeof redirects.signupSuccess === 'string') return redirects.signupSuccess;\n }\n\n if (!isSignup) {\n if (redirects.loginSuccess === null) return null;\n if (typeof redirects.loginSuccess === 'string') return redirects.loginSuccess;\n }\n\n // Backward-compatible fallback (legacy API)\n if (redirects.success === null) return null;\n if (typeof redirects.success === 'string') return redirects.success;\n\n return '/';\n }\n\n /**\n * Navigate to appropriate challenge route.\n *\n * @param response - Auth response containing challenge info\n */\n async navigateToChallenge(response: AuthResponse): Promise<void> {\n const url = this.buildChallengeUrl(response);\n await this.navigate(url);\n }\n\n /**\n * Navigate to success URL.\n *\n * @param queryParams - Optional query parameters to append to the success URL\n * @param context - Optional auth context for selecting the success route\n *\n * @example\n * ```typescript\n * await router.navigateToSuccess({ appState: 'invite-code-123' });\n * ```\n */\n async navigateToSuccess(\n queryParams?: Record<string, string | null | undefined>,\n context?: AuthResponseContext,\n ): Promise<void> {\n const resolved = this.resolveSuccessUrl(context);\n if (resolved === null) {\n return;\n }\n\n let url = resolved;\n\n // Append query parameters if provided\n // Build path with query string (not full URL) to work with both\n // Angular Router (navigateByUrl) and window.location\n if (queryParams && Object.keys(queryParams).length > 0) {\n const searchParams = new URLSearchParams();\n Object.entries(queryParams).forEach(([key, value]) => {\n if (value) {\n searchParams.set(key, value);\n }\n });\n const queryString = searchParams.toString();\n url = queryString ? `${url}${url.includes('?') ? '&' : '?'}${queryString}` : url;\n }\n\n await this.navigate(url);\n }\n\n /**\n * Retrieve stored OAuth appState from sessionStorage.\n *\n * NOTE: This method does NOT clear the storage. Only the public getLastOauthState() API clears it.\n * This allows the SDK to read appState for auto-navigation while still making it available to\n * consumers via the public API.\n *\n * @returns Query params object with appState if present, undefined otherwise\n */\n private async getStoredOauthState(): Promise<Record<string, string> | undefined> {\n try {\n const stored = await this.oauthStorage.getItem(OAUTH_STATE_KEY);\n if (stored) {\n // Do NOT clear - consumer may want to retrieve it via getLastOauthState()\n return { appState: stored };\n }\n } catch {\n // Ignore storage errors\n }\n return undefined;\n }\n\n /**\n * Navigate to error URL.\n *\n * @param type - Type of error (oauth or session)\n */\n async navigateToError(type: 'oauth' | 'session'): Promise<void> {\n const redirects = this.config.redirects;\n const raw = type === 'oauth' ? redirects?.oauthError : redirects?.sessionExpired;\n if (raw === null) {\n return;\n }\n const url = raw ?? '/login';\n await this.navigate(url);\n }\n\n /**\n * Build challenge URL based on configuration.\n *\n * Priority:\n * 1. Custom route mapping (challengeRoutes)\n * 2. Single route with query param (useSingleChallengeRoute)\n * 3. MFA-specific routes (mfaRoutes) - for MFA_REQUIRED challenge only\n * 4. Default separate routes (challengeBase + kebab-case)\n *\n * @param response - Auth response containing challenge info\n * @returns URL to navigate to\n */\n buildChallengeUrl(response: AuthResponse): string {\n const challengeName = response.challengeName!;\n\n // Priority 1: Custom route mapping\n if (this.config.redirects?.challengeRoutes?.[challengeName]) {\n return this.config.redirects.challengeRoutes[challengeName]!;\n }\n\n const base = this.config.redirects?.challengeBase || '/auth/challenge';\n\n // Priority 2: Single route with query param\n if (this.config.redirects?.useSingleChallengeRoute) {\n return `${base}?challenge=${challengeName}`;\n }\n\n // Priority 3: MFA-specific routes (only for MFA_REQUIRED)\n if (challengeName === AuthChallenge.MFA_REQUIRED) {\n const mfaUrl = this.buildMFAUrl(response);\n if (mfaUrl) {\n return mfaUrl;\n }\n }\n\n // Priority 4: Default separate routes\n const route = this.buildDefaultRouteSegment(challengeName, response);\n return `${base}/${route}`;\n }\n\n /**\n * Build MFA-specific URL if custom mfaRoutes are configured.\n *\n * @param response - Auth response with MFA challenge parameters\n * @returns Custom MFA URL if configured, null otherwise\n */\n private buildMFAUrl(response: AuthResponse): string | null {\n const params = response.challengeParameters;\n const method = params?.['preferredMethod'] || params?.['method'];\n const mfaRoutes = this.config.redirects?.mfaRoutes;\n\n if (!mfaRoutes) {\n return null;\n }\n\n // Passkey verification\n if (method === 'passkey' && mfaRoutes.passkey) {\n return mfaRoutes.passkey;\n }\n\n // MFA method selector (multiple methods available)\n if (\n !method &&\n params?.['availableMethods'] &&\n Array.isArray(params['availableMethods']) &&\n params['availableMethods'].length > 1\n ) {\n if (mfaRoutes.selector) {\n return mfaRoutes.selector;\n }\n }\n\n // Default MFA verification (sms, email, totp)\n if (mfaRoutes.default) {\n return mfaRoutes.default;\n }\n\n return null;\n }\n\n /**\n * Build default route segment for a challenge.\n *\n * @param challengeName - Challenge type\n * @param response - Auth response for extracting challenge parameters (needed for MFA)\n * @returns Route segment (e.g., 'mfa-required/passkey', 'verify-email')\n */\n private buildDefaultRouteSegment(challengeName: AuthChallenge, response?: AuthResponse): string {\n // MFA_REQUIRED needs special handling for backward compatibility\n // with existing apps that have passkey and selector routes\n if (challengeName === AuthChallenge.MFA_REQUIRED && response) {\n const params = response.challengeParameters;\n const method = params?.['preferredMethod'] || params?.['method'];\n\n // Passkey verification\n if (method === 'passkey') {\n return 'mfa-required/passkey';\n }\n\n // MFA method selector (multiple methods available)\n if (\n !method &&\n params?.['availableMethods'] &&\n Array.isArray(params['availableMethods']) &&\n params['availableMethods'].length > 1\n ) {\n return 'mfa-selector';\n }\n\n // Default MFA verification (sms, email, totp)\n return 'mfa-required';\n }\n\n // Generic kebab-case route for other challenges\n return challengeName.toLowerCase().replace(/_/g, '-');\n }\n\n /**\n * Execute navigation using configured handler or default.\n *\n * @param url - URL to navigate to\n */\n private async navigate(url: string): Promise<void> {\n if (this.config.navigationHandler) {\n await this.config.navigationHandler(url);\n } else {\n // Default: use window.location.replace (works in guards and browsers)\n if (typeof window !== 'undefined') {\n window.location.replace(url);\n }\n }\n }\n\n /**\n * Expose URL builder for guards/components that need it.\n *\n * @param response - Auth response containing challenge info\n * @returns URL for the challenge\n */\n getChallengeUrl(response: AuthResponse): string {\n return this.buildChallengeUrl(response);\n }\n\n /**\n * Check if the error redirect is disabled.\n *\n * WHY: Guards need to know if they should return true (activate route) or false\n * (prevent activation) when auto-redirect is disabled via null redirect URLs.\n *\n * @param type - Type of error (oauth or session)\n * @returns True if the error redirect is explicitly null, false otherwise\n */\n isErrorRedirectDisabled(type: 'oauth' | 'session'): boolean {\n const redirects = this.config.redirects;\n if (!redirects) {\n return false;\n }\n\n const redirectUrl = type === 'oauth' ? redirects.oauthError : redirects.sessionExpired;\n return redirectUrl === null;\n }\n\n /**\n * Check if the success redirect is disabled for a given context.\n *\n * WHY: Guards need to know if they should return true (activate route) or false\n * (prevent activation) when auto-redirect is disabled via null redirect URLs.\n *\n * @param context - Optional auth context to determine which success redirect to check\n * @returns True if the success redirect is explicitly null, false otherwise\n */\n isSuccessRedirectDisabled(context?: AuthResponseContext): boolean {\n const resolved = this.resolveSuccessUrl(context);\n return resolved === null;\n }\n}\n","import { ResolvedNAuthClientConfig } from './config';\nimport { NAuthAdminEndpoints } from '../types/config.types';\nimport { NAuthClientError } from './errors';\nimport { NAuthErrorCode } from '../types/error.types';\nimport type {\n AdminSignupRequest,\n AdminSignupResponse,\n AdminSignupSocialRequest,\n AdminSignupSocialResponse,\n GetUsersRequest,\n GetUsersResponse,\n DeleteUserResponse,\n DisableUserResponse,\n EnableUserResponse,\n AdminResetPasswordRequest,\n AdminResetPasswordResponse,\n GetUserSessionsResponse,\n AdminAuditHistoryRequest,\n} from '../types/admin.types';\nimport type { AuthUser } from '../types/user.types';\nimport type { MFAStatus, RemoveMFADeviceResponse, GetMFADevicesResponse } from '../types/mfa.types';\nimport type { AuditHistoryResponse } from '../types/audit.types';\n\nconst hasWindow = (): boolean =>\n typeof globalThis !== 'undefined' && typeof (globalThis as { window?: unknown }).window !== 'undefined';\n\n/**\n * Admin operations for user and system management.\n * Accessed via client.admin.*\n *\n * Provides admin-level operations including:\n * - User CRUD operations\n * - Password management (set, reset)\n * - Session management\n * - MFA management\n * - Audit history\n *\n * @example\n * ```typescript\n * const client = new NAuthClient({\n * baseUrl: 'https://api.example.com/auth',\n * tokenDelivery: 'cookies',\n * admin: {\n * pathPrefix: '/admin'\n * }\n * });\n *\n * // User management\n * const user = await client.admin.createUser({\n * email: 'user@example.com',\n * password: 'SecurePass123!',\n * isEmailVerified: true,\n * });\n *\n * // Get users with filters\n * const users = await client.admin.getUsers({\n * page: 1,\n * limit: 20,\n * mfaEnabled: false\n * });\n *\n * // Delete user (handles :sub path param)\n * await client.admin.deleteUser('user-uuid');\n * ```\n */\nexport class AdminOperations {\n private readonly config: ResolvedNAuthClientConfig;\n private readonly adminEndpoints: NAuthAdminEndpoints;\n private readonly adminPathPrefix: string;\n private readonly adminHeaders: Record<string, string>;\n\n /**\n * Create admin operations instance.\n *\n * @param config - Resolved client configuration\n */\n constructor(config: ResolvedNAuthClientConfig) {\n if (!config.admin) {\n throw new Error('Admin operations require admin configuration');\n }\n\n this.config = config;\n this.adminEndpoints = config.admin.endpoints;\n this.adminPathPrefix = config.admin.pathPrefix;\n this.adminHeaders = config.admin.headers;\n }\n\n // ============================================================================\n // User Management\n // ============================================================================\n\n /**\n * Create a new user (admin operation)\n *\n * Allows creating users with:\n * - Pre-verified email/phone\n * - Auto-generated passwords\n * - Force password change flag\n *\n * @param request - User creation request\n * @returns Created user and optional generated password\n * @throws {NAuthClientError} If creation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.createUser({\n * email: 'user@example.com',\n * password: 'SecurePass123!',\n * isEmailVerified: true,\n * });\n *\n * // With auto-generated password\n * const result = await client.admin.createUser({\n * email: 'user@example.com',\n * generatePassword: true,\n * mustChangePassword: true,\n * });\n * console.log('Generated password:', result.generatedPassword);\n * ```\n */\n async createUser(request: AdminSignupRequest): Promise<AdminSignupResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.signup);\n return this.post<AdminSignupResponse>(path, request);\n }\n\n /**\n * Import social user (admin operation)\n *\n * Imports existing social users from external platforms (e.g., Cognito, Auth0)\n * with social account linkage.\n *\n * @param request - Social user import request\n * @returns Created user and social account info\n * @throws {NAuthClientError} If import fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.importSocialUser({\n * email: 'user@example.com',\n * provider: 'google',\n * providerId: 'google_12345',\n * providerEmail: 'user@gmail.com',\n * });\n * ```\n */\n async importSocialUser(request: AdminSignupSocialRequest): Promise<AdminSignupSocialResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.signupSocial);\n return this.post<AdminSignupSocialResponse>(path, request);\n }\n\n /**\n * Get users with filters and pagination\n *\n * @param params - Filter and pagination params\n * @returns Paginated user list\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.getUsers({\n * page: 1,\n * limit: 20,\n * isEmailVerified: true,\n * mfaEnabled: false,\n * sortBy: 'createdAt',\n * sortOrder: 'DESC',\n * });\n * ```\n */\n async getUsers(params: GetUsersRequest = {}): Promise<GetUsersResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getUsers);\n const queryString = this.buildQueryString(params as unknown as Record<string, unknown>);\n return this.get<GetUsersResponse>(`${path}${queryString}`);\n }\n\n /**\n * Get user by sub (UUID)\n *\n * @param sub - User UUID\n * @returns User object\n * @throws {NAuthClientError} If user not found\n *\n * @example\n * ```typescript\n * const user = await client.admin.getUser('a21b654c-2746-4168-acee-c175083a65cd');\n * ```\n */\n async getUser(sub: string): Promise<AuthUser> {\n const path = this.buildAdminUrl(this.adminEndpoints.getUser, { sub });\n return this.get<AuthUser>(path);\n }\n\n /**\n * Delete user with cascade cleanup\n *\n * @param sub - User UUID\n * @returns Deletion confirmation with cascade counts\n * @throws {NAuthClientError} If deletion fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.deleteUser('user-uuid');\n * console.log('Deleted records:', result.deletedRecords);\n * ```\n */\n async deleteUser(sub: string): Promise<DeleteUserResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.deleteUser, { sub });\n return this.delete<DeleteUserResponse>(path);\n }\n\n /**\n * Disable user account (permanent lock)\n *\n * @param sub - User UUID\n * @param reason - Optional reason for disabling\n * @returns Disable confirmation with revoked session count\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.disableUser(\n * 'user-uuid',\n * 'Account compromised'\n * );\n * console.log('Revoked sessions:', result.revokedSessions);\n * ```\n */\n async disableUser(sub: string, reason?: string): Promise<DisableUserResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.disableUser, { sub });\n return this.post<DisableUserResponse>(path, { reason });\n }\n\n /**\n * Enable (unlock) user account\n *\n * @param sub - User UUID\n * @returns Enable confirmation with updated user\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.enableUser('user-uuid');\n * console.log('User enabled:', result.user);\n * ```\n */\n async enableUser(sub: string): Promise<EnableUserResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.enableUser, { sub });\n return this.post<EnableUserResponse>(path, {});\n }\n\n /**\n * Force password change on next login\n *\n * @param sub - User UUID\n * @returns Success confirmation\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.forcePasswordChange('user-uuid');\n * ```\n */\n async forcePasswordChange(sub: string): Promise<{ success: boolean }> {\n const path = this.buildAdminUrl(this.adminEndpoints.forcePasswordChange, { sub });\n return this.post<{ success: boolean }>(path, {});\n }\n\n // ============================================================================\n // Password Management\n // ============================================================================\n\n /**\n * Set password for any user (admin operation)\n *\n * @param identifier - User email, username, or phone\n * @param newPassword - New password\n * @returns Success confirmation\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.setPassword('user@example.com', 'NewSecurePass123!');\n * ```\n */\n async setPassword(identifier: string, newPassword: string): Promise<{ success: boolean }> {\n const path = this.buildAdminUrl(this.adminEndpoints.setPassword);\n return this.post<{ success: boolean }>(path, { identifier, newPassword });\n }\n\n /**\n * Initiate password reset workflow (sends code/link to user)\n *\n * @param request - Password reset request\n * @returns Reset confirmation with delivery details\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.initiatePasswordReset({\n * sub: 'user-uuid',\n * deliveryMethod: 'email',\n * baseUrl: 'https://myapp.com/reset-password',\n * reason: 'User requested password reset',\n * });\n * console.log('Code sent to:', result.destination);\n * ```\n */\n async initiatePasswordReset(request: AdminResetPasswordRequest): Promise<AdminResetPasswordResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.resetPasswordInitiate);\n return this.post<AdminResetPasswordResponse>(path, request);\n }\n\n // ============================================================================\n // Session Management\n // ============================================================================\n\n /**\n * Get all sessions for a user\n *\n * @param sub - User UUID\n * @returns User sessions\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.getUserSessions('user-uuid');\n * console.log('Active sessions:', result.sessions);\n * ```\n */\n async getUserSessions(sub: string): Promise<GetUserSessionsResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getUserSessions, { sub });\n return this.get<GetUserSessionsResponse>(path);\n }\n\n /**\n * Logout all sessions for a user (admin-initiated)\n *\n * @param sub - User UUID\n * @param forgetDevices - If true, also revokes all trusted devices\n * @returns Number of sessions revoked\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.logoutAllSessions('user-uuid', true);\n * console.log(`Revoked ${result.revokedCount} sessions`);\n * ```\n */\n async logoutAllSessions(sub: string, forgetDevices = false): Promise<{ revokedCount: number }> {\n const path = this.buildAdminUrl(this.adminEndpoints.logoutAll, { sub });\n return this.post<{ revokedCount: number }>(path, { forgetDevices });\n }\n\n // ============================================================================\n // MFA Management\n // ============================================================================\n\n /**\n * Get MFA status for a user\n *\n * @param sub - User UUID\n * @returns MFA status\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const status = await client.admin.getMfaStatus('user-uuid');\n * console.log('MFA enabled:', status.enabled);\n * ```\n */\n async getMfaStatus(sub: string): Promise<MFAStatus> {\n const path = this.buildAdminUrl(this.adminEndpoints.getMfaStatus, { sub });\n return this.get<MFAStatus>(path);\n }\n\n /**\n * Get MFA devices for a user\n *\n * Returns all active MFA devices for a user including device id, name, type, and isPreferred status.\n *\n * @param sub - User UUID\n * @returns Response containing array of MFA devices\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.getMfaDevices('user-uuid');\n * console.log('Devices:', result.devices);\n * // [{ id: 1, name: 'My Authenticator', type: 'totp', isPreferred: true, ... }]\n * ```\n */\n async getMfaDevices(sub: string): Promise<GetMFADevicesResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getMfaDevices, { sub });\n return this.get<GetMFADevicesResponse>(path);\n }\n\n /**\n * Remove a single MFA device by device ID (admin).\n *\n * @param deviceId - MFA device ID\n * @returns Removal result\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * const result = await client.admin.removeMfaDeviceById(123);\n * console.log(result.removedDeviceId);\n * ```\n */\n async removeMfaDeviceById(deviceId: number): Promise<RemoveMFADeviceResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.removeMfaDeviceById, { deviceId: String(deviceId) });\n return this.delete<RemoveMFADeviceResponse>(path);\n }\n\n /**\n * Set preferred MFA device for a user (admin operation).\n *\n * @param sub - User identifier\n * @param deviceId - Device ID to set as preferred\n * @returns Success message\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.setPreferredMfaDevice('user-uuid', 123);\n * ```\n */\n async setPreferredMfaDevice(sub: string, deviceId: number): Promise<{ message: string }> {\n const path = this.buildAdminUrl(this.adminEndpoints.setPreferredMfaDevice, { sub, deviceId: String(deviceId) });\n return this.post<{ message: string }>(path, {});\n }\n\n /**\n * Grant or revoke MFA exemption for a user\n *\n * @param sub - User UUID\n * @param exempt - True to exempt from MFA, false to require\n * @param reason - Optional reason for exemption\n * @returns Success message\n * @throws {NAuthClientError} If operation fails\n *\n * @example\n * ```typescript\n * await client.admin.setMfaExemption('user-uuid', true, 'Service account');\n * ```\n */\n async setMfaExemption(sub: string, exempt: boolean, reason?: string): Promise<{ message: string }> {\n const path = this.buildAdminUrl(this.adminEndpoints.setMfaExemption);\n return this.post<{ message: string }>(path, { sub, exempt, reason });\n }\n\n // ============================================================================\n // Audit\n // ============================================================================\n\n /**\n * Get audit history for a user\n *\n * @param params - Audit history request params\n * @returns Paginated audit events\n * @throws {NAuthClientError} If request fails\n *\n * @example\n * ```typescript\n * const history = await client.admin.getAuditHistory({\n * sub: 'user-uuid',\n * page: 1,\n * limit: 50,\n * eventType: 'LOGIN_SUCCESS',\n * });\n * ```\n */\n async getAuditHistory(params: AdminAuditHistoryRequest): Promise<AuditHistoryResponse> {\n const path = this.buildAdminUrl(this.adminEndpoints.getAuditHistory);\n const queryString = this.buildQueryString(params as unknown as Record<string, unknown>);\n return this.get<AuditHistoryResponse>(`${path}${queryString}`);\n }\n\n // ============================================================================\n // Private Helper Methods\n // ============================================================================\n\n /**\n * Build admin endpoint URL with path parameter replacement\n *\n * Path construction order:\n * 1. Start with endpoint: '/users/:sub'\n * 2. Replace params: '/users/uuid-123'\n * 3. Apply adminPathPrefix: '/admin/users/uuid-123'\n * 4. Apply authPathPrefix if exists: '/auth/admin/users/uuid-123'\n * 5. Combine with baseUrl: 'https://api.example.com/auth/admin/users/uuid-123'\n *\n * @param endpointPath - Endpoint path (may contain :sub, :provider, etc.)\n * @param pathParams - Path parameters to replace\n * @returns Full URL\n * @private\n */\n private buildAdminUrl(endpointPath: string, pathParams?: Record<string, string>): string {\n let path = endpointPath;\n\n // Replace path parameters (e.g., :sub, :provider)\n if (pathParams) {\n Object.entries(pathParams).forEach(([key, value]) => {\n path = path.replace(`:${key}`, encodeURIComponent(value));\n });\n }\n\n // Apply admin path prefix\n const prefix = this.adminPathPrefix.startsWith('/') ? this.adminPathPrefix : `/${this.adminPathPrefix}`;\n path = `${prefix}${path.startsWith('/') ? '' : '/'}${path}`;\n\n // Apply authPathPrefix if configured (e.g., '/auth')\n if (this.config.authPathPrefix) {\n const authPrefix = this.config.authPathPrefix.startsWith('/')\n ? this.config.authPathPrefix\n : `/${this.config.authPathPrefix}`;\n path = `${authPrefix}${path}`;\n }\n\n // Combine with baseUrl\n const normalizedBase = this.config.baseUrl.endsWith('/') ? this.config.baseUrl.slice(0, -1) : this.config.baseUrl;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n\n return `${normalizedBase}${normalizedPath}`;\n }\n\n /**\n * Build query string from params object\n *\n * Handles:\n * - Simple values (string, number, boolean)\n * - Arrays (multiple values for same key)\n * - Nested objects (e.g., createdAt[operator], createdAt[value])\n * - Dates (converted to ISO string)\n *\n * @param params - Query parameters\n * @returns Query string (e.g., '?page=1&limit=20')\n * @private\n */\n private buildQueryString(params: Record<string, unknown>): string {\n const searchParams = new URLSearchParams();\n\n for (const [key, rawValue] of Object.entries(params)) {\n if (rawValue === undefined || rawValue === null) {\n continue;\n }\n\n // Handle arrays\n if (Array.isArray(rawValue)) {\n for (const item of rawValue) {\n searchParams.append(key, String(item));\n }\n continue;\n }\n\n // Handle nested objects (e.g., createdAt: { operator: 'gte', value: Date })\n if (typeof rawValue === 'object' && rawValue !== null && !(rawValue instanceof Date)) {\n const nestedObj = rawValue as Record<string, unknown>;\n for (const [nestedKey, nestedValue] of Object.entries(nestedObj)) {\n const nestedParamKey = `${key}[${nestedKey}]`;\n const valueToAppend = nestedValue instanceof Date ? nestedValue.toISOString() : String(nestedValue);\n searchParams.append(nestedParamKey, valueToAppend);\n }\n continue;\n }\n\n // Handle dates\n if (rawValue instanceof Date) {\n searchParams.append(key, rawValue.toISOString());\n continue;\n }\n\n // Handle simple values\n searchParams.append(key, String(rawValue));\n }\n\n const query = searchParams.toString();\n return query ? `?${query}` : '';\n }\n\n /**\n * Build request headers for authentication\n *\n * @param auth - Whether to include authentication headers\n * @param method - HTTP method\n * @returns Headers object\n * @private\n */\n private async buildHeaders(\n auth: boolean,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' = 'GET',\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n ...this.config.headers,\n ...this.adminHeaders,\n };\n\n // Set Content-Type for mutating requests\n if (method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n // Add access token in JSON mode\n // In cookies mode, tokens are sent automatically via httpOnly cookies\n if (auth && this.config.tokenDelivery === 'json') {\n try {\n const ACCESS_TOKEN_KEY = 'nauth_access_token';\n const token = await this.config.storage.getItem(ACCESS_TOKEN_KEY);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n } catch {\n // Non-fatal: storage can fail in restricted environments\n }\n }\n\n // Add device token header in JSON mode\n // In cookies mode, device token is sent automatically via httpOnly cookie\n if (this.config.tokenDelivery === 'json') {\n try {\n const deviceToken = await this.config.storage.getItem(this.config.deviceTrust.storageKey);\n if (deviceToken) {\n headers[this.config.deviceTrust.headerName] = deviceToken;\n }\n } catch {\n // Non-fatal: storage can fail in restricted environments\n }\n }\n\n // Add CSRF header in cookies mode for mutating requests\n const mutatingMethods: readonly ('POST' | 'PUT' | 'PATCH' | 'DELETE')[] = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (\n this.config.tokenDelivery === 'cookies' &&\n hasWindow() &&\n (mutatingMethods as readonly string[]).includes(method)\n ) {\n const csrfToken = this.getCsrfToken();\n if (csrfToken) {\n headers[this.config.csrf.headerName] = csrfToken;\n }\n }\n\n return headers;\n }\n\n /**\n * Get CSRF token from cookie (browser only)\n *\n * @returns CSRF token or null\n * @private\n */\n private getCsrfToken(): string | null {\n if (!hasWindow() || typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`));\n return match ? decodeURIComponent(match[2]) : null;\n }\n\n /**\n * Execute GET request\n *\n * @param path - Full URL path\n * @returns Response data\n * @private\n */\n private async get<T>(path: string): Promise<T> {\n const headers = await this.buildHeaders(true, 'GET');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n try {\n const response = await this.config.httpAdapter.request<T>({\n method: 'GET',\n url: path,\n headers,\n credentials,\n });\n return response.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Execute POST request\n *\n * @param path - Full URL path\n * @param body - Request body\n * @returns Response data\n * @private\n */\n private async post<T>(path: string, body: unknown): Promise<T> {\n const headers = await this.buildHeaders(true, 'POST');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n try {\n const response = await this.config.httpAdapter.request<T>({\n method: 'POST',\n url: path,\n headers,\n body,\n credentials,\n });\n return response.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Execute DELETE request\n *\n * @param path - Full URL path\n * @returns Response data\n * @private\n */\n private async delete<T>(path: string): Promise<T> {\n const headers = await this.buildHeaders(true, 'DELETE');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n try {\n const response = await this.config.httpAdapter.request<T>({\n method: 'DELETE',\n url: path,\n headers,\n credentials,\n });\n return response.data;\n } catch (error) {\n throw this.handleError(error);\n }\n }\n\n /**\n * Handle HTTP errors and convert to NAuthClientError\n *\n * @param error - Error from HTTP adapter\n * @returns NAuthClientError\n * @private\n */\n private handleError(error: unknown): NAuthClientError {\n if (error instanceof NAuthClientError) {\n return error;\n }\n\n // Try to extract error info from adapter response\n if (error && typeof error === 'object' && 'response' in error) {\n const httpError = error as { response?: { status?: number; data?: unknown } };\n const status = httpError.response?.status ?? 500;\n const errorData =\n typeof httpError.response?.data === 'object' && httpError.response.data !== null\n ? (httpError.response.data as Record<string, unknown>)\n : {};\n\n const code =\n typeof errorData['code'] === 'string' ? (errorData['code'] as NAuthErrorCode) : NAuthErrorCode.INTERNAL_ERROR;\n const message =\n typeof errorData['message'] === 'string'\n ? (errorData['message'] as string)\n : `Request failed with status ${status}`;\n\n return new NAuthClientError(code, message, {\n statusCode: status,\n details: errorData,\n });\n }\n\n return new NAuthClientError(\n NAuthErrorCode.INTERNAL_ERROR,\n error instanceof Error ? error.message : 'Unknown error',\n );\n }\n}\n","import { resolveConfig, ResolvedNAuthClientConfig } from './config';\nimport { TokenManager } from './refresh';\nimport { BrowserStorage } from '../storage/browser';\nimport { InMemoryStorage } from '../storage/memory';\nimport type { NAuthStorageAdapter } from '../storage/interface';\nimport { EventEmitter, AuthEventType, AuthEventListener } from './events';\nimport { NAuthClientError } from './errors';\nimport { NAuthErrorCode } from '../types/error.types';\nimport { FetchAdapter } from '../adapters/fetch-adapter';\nimport {\n AuthChallenge,\n ChallengeResponse,\n GetChallengeDataRequest,\n GetSetupDataRequest,\n LoginRequest,\n LogoutAllRequest,\n AuthResponse,\n ResendCodeRequest,\n SignupRequest,\n TokenResponse,\n MFACodeResponse,\n MFAPasskeyResponse,\n} from '../types/auth.types';\nimport { NAuthClientConfig } from '../types/config.types';\nimport {\n GetChallengeDataResponse,\n GetSetupDataResponse,\n GetMFADevicesResponse,\n MFAStatus,\n RemoveMFADeviceResponse,\n} from '../types/mfa.types';\nimport { AuditHistoryResponse } from '../types/audit.types';\nimport { LinkedAccountsResponse, SocialLoginOptions, SocialVerifyRequest, SocialProvider } from '../types/social.types';\nimport {\n AuthUser,\n ChangePasswordRequest,\n ConfirmForgotPasswordRequest,\n ConfirmForgotPasswordResponse,\n ForgotPasswordRequest,\n ForgotPasswordResponse,\n ResetPasswordWithCodeRequest,\n ResetPasswordWithCodeResponse,\n UpdateProfileRequest,\n} from '../types/user.types';\nimport { ChallengeRouter } from './challenge-router';\nimport { AdminOperations } from './admin-operations';\n\nconst USER_KEY = 'nauth_user';\nconst CHALLENGE_KEY = 'nauth_challenge_session';\nconst OAUTH_STATE_KEY = 'nauth_oauth_state';\nconst hasWindow = (): boolean =>\n typeof globalThis !== 'undefined' && typeof (globalThis as { window?: unknown }).window !== 'undefined';\n\n/**\n * Get sessionStorage-based storage for ephemeral OAuth state.\n * Falls back to main storage if sessionStorage is unavailable.\n */\nconst getOauthStorage = (mainStorage: NAuthStorageAdapter): NAuthStorageAdapter => {\n if (hasWindow() && typeof window.sessionStorage !== 'undefined') {\n return new BrowserStorage(window.sessionStorage);\n }\n // Fallback to main storage (memory storage in SSR, localStorage in browser without sessionStorage)\n return mainStorage;\n};\n\n/**\n * Choose default storage implementation.\n */\nconst defaultStorage = () => {\n if (hasWindow() && typeof window.localStorage !== 'undefined') {\n return new BrowserStorage();\n }\n return new InMemoryStorage();\n};\n\n/**\n * Primary client for interacting with nauth-toolkit backend.\n */\nexport class NAuthClient {\n private readonly config: ResolvedNAuthClientConfig;\n private readonly tokenManager: TokenManager;\n private readonly eventEmitter: EventEmitter;\n private readonly challengeRouter: ChallengeRouter;\n private readonly oauthStorage: NAuthStorageAdapter;\n private currentUser: AuthUser | null = null;\n\n /**\n * Internally tracked MFA device ID\n * When user selects a specific device (e.g., \"Microsoft Authenticator\" vs \"Google Authenticator\"),\n * SDK stores it here and auto-injects into respondToChallenge()\n */\n private selectedDeviceId?: number;\n\n /**\n * Admin operations (available if admin config provided).\n *\n * Provides admin-level user management methods:\n * - User CRUD operations\n * - Password management\n * - Session management\n * - MFA management\n * - Audit history\n *\n * @example\n * ```typescript\n * const client = new NAuthClient({\n * baseUrl: 'https://api.example.com/auth',\n * tokenDelivery: 'cookies',\n * admin: {\n * pathPrefix: '/admin',\n * },\n * });\n *\n * // Use admin operations\n * const users = await client.admin.getUsers({ page: 1 });\n * await client.admin.deleteUser('user-uuid');\n * ```\n */\n public readonly admin?: AdminOperations;\n\n /**\n * Create a new client instance.\n *\n * @param userConfig - Client configuration\n */\n constructor(userConfig: NAuthClientConfig) {\n const storage = userConfig.storage ?? defaultStorage();\n const defaultAdapter = userConfig.httpAdapter ?? new FetchAdapter();\n this.config = resolveConfig({ ...userConfig, storage }, defaultAdapter);\n this.tokenManager = new TokenManager(storage);\n this.eventEmitter = new EventEmitter();\n this.oauthStorage = getOauthStorage(storage);\n this.challengeRouter = new ChallengeRouter(this.config, this.oauthStorage);\n\n // Initialize admin operations if configured\n if (this.config.admin) {\n this.admin = new AdminOperations(this.config);\n }\n\n if (hasWindow()) {\n window.addEventListener('storage', this.handleStorageEvent);\n }\n }\n\n /**\n * Clean up resources.\n */\n dispose(): void {\n if (hasWindow()) {\n window.removeEventListener('storage', this.handleStorageEvent);\n }\n }\n\n /**\n * Login with identifier and password.\n *\n * @param identifier - Username or email\n * @param password - User password\n * @param recaptchaToken - Optional reCAPTCHA token for bot protection\n *\n * @example Basic login\n * ```typescript\n * const response = await client.login('user@example.com', 'password123');\n * ```\n *\n * @example With reCAPTCHA\n * ```typescript\n * const token = await grecaptcha.execute(siteKey, { action: 'login' });\n * const response = await client.login('user@example.com', 'password123', token);\n * ```\n */\n async login(identifier: string, password: string, recaptchaToken?: string): Promise<AuthResponse> {\n const loginEvent = { type: 'auth:login' as const, data: { identifier }, timestamp: Date.now() };\n this.eventEmitter.emit(loginEvent);\n\n try {\n const body: LoginRequest = { identifier, password, recaptchaToken };\n const response = await this.post<AuthResponse>(this.config.endpoints.login, body);\n await this.handleAuthResponse(response);\n\n // Emit success or challenge event\n if (response.challengeName) {\n const challengeEvent = { type: 'auth:challenge' as const, data: response, timestamp: Date.now() };\n this.eventEmitter.emit(challengeEvent);\n } else {\n const successEvent = { type: 'auth:success' as const, data: response, timestamp: Date.now() };\n this.eventEmitter.emit(successEvent);\n }\n\n // Auto-handle navigation\n await this.challengeRouter.handleAuthResponse(response, { source: 'login' });\n\n return response;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(NAuthErrorCode.AUTH_INVALID_CREDENTIALS, (error as Error).message || 'Login failed');\n const errorEvent = { type: 'auth:error' as const, data: authError, timestamp: Date.now() };\n this.eventEmitter.emit(errorEvent);\n throw authError;\n }\n }\n\n /**\n * Signup with credentials.\n */\n async signup(payload: SignupRequest): Promise<AuthResponse> {\n this.eventEmitter.emit({ type: 'auth:signup', data: { email: payload.email }, timestamp: Date.now() });\n\n try {\n const response = await this.post<AuthResponse>(this.config.endpoints.signup, payload);\n await this.handleAuthResponse(response);\n\n // Emit success or challenge event\n if (response.challengeName) {\n this.eventEmitter.emit({ type: 'auth:challenge', data: response, timestamp: Date.now() });\n } else {\n this.eventEmitter.emit({ type: 'auth:success', data: response, timestamp: Date.now() });\n }\n\n // Auto-handle navigation\n await this.challengeRouter.handleAuthResponse(response, { source: 'signup' });\n\n return response;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(NAuthErrorCode.AUTH_INVALID_CREDENTIALS, (error as Error).message || 'Signup failed');\n this.eventEmitter.emit({ type: 'auth:error', data: authError, timestamp: Date.now() });\n throw authError;\n }\n }\n\n /**\n * Refresh tokens manually.\n *\n * @throws {NAuthClientError} When refresh fails (e.g., session expired)\n *\n * @example\n * ```typescript\n * try {\n * await client.refreshTokens();\n * } catch (error) {\n * // Session expired - user is already logged out automatically\n * router.navigate(['/login']);\n * }\n * ```\n */\n async refreshTokens(): Promise<TokenResponse> {\n const tokenDelivery = this.getTokenDeliveryMode();\n\n // Only check for refresh token in localStorage for JSON mode\n // In cookies mode, refresh token is in httpOnly cookie (backend manages it)\n if (tokenDelivery === 'json') {\n await this.tokenManager.assertHasRefreshToken();\n }\n\n // In cookies mode, the refresh token is in the httpOnly cookie -- no need to send it in the body.\n // Sending an empty string is misleading and can confuse backend validation.\n const body = tokenDelivery === 'json' ? { refreshToken: (await this.tokenManager.getTokens()).refreshToken } : {};\n const refreshFn = async () => {\n // In cookies mode, refresh token is sent via httpOnly cookie (no access token needed, auth=false)\n // In JSON mode, refresh token is in body (no access token needed, auth=false)\n // Refresh endpoint is PUBLIC - it doesn't need an access token\n return this.post<TokenResponse>(this.config.endpoints.refresh, body, false);\n };\n\n try {\n // Cookies mode MUST NEVER persist tokens to storage, even if a misconfigured backend\n // accidentally returns tokens in the response body (security footgun).\n const tokens = await this.tokenManager.refreshOnce(refreshFn, { persist: tokenDelivery === 'json' });\n this.config.onTokenRefresh?.();\n this.eventEmitter.emit({ type: 'auth:refresh', data: { success: true }, timestamp: Date.now() });\n return tokens;\n } catch (error) {\n // Handle session expiration (401 error)\n // Clear local auth state to prevent isAuthenticated() from returning true with stale data\n if (error instanceof NAuthClientError && error.statusCode === 401) {\n await this.clearLocalAuthState();\n this.config.onSessionExpired?.();\n this.eventEmitter.emit({ type: 'auth:session_expired', data: {}, timestamp: Date.now() });\n }\n throw error;\n }\n }\n\n // ============================================================================\n // Local state management (no network)\n // ============================================================================\n\n /**\n * Clear all local auth state without making any network requests.\n *\n * WHY:\n * - When refresh fails with 401 (session expired), clients should immediately drop any cached\n * auth state (user + tokens) to prevent \"sticky auth\" across hard reloads.\n * - In cookie delivery modes, httpOnly cookies can only be cleared by the backend; this method\n * only clears client-side state (e.g., cached user + persisted tokens in JSON mode).\n *\n * IMPORTANT: Also clears any pending challenge sessions to prevent ghost states where the UI\n * shows a challenge screen but the backend session is invalid.\n *\n * @param options - Optional behavior flags\n * @returns Promise that resolves when local state is cleared\n *\n * @example\n * ```typescript\n * // Called by framework adapters/interceptors when refresh fails with 401\n * await client.clearLocalAuthState();\n * ```\n */\n async clearLocalAuthState(options?: { forgetDevice?: boolean }): Promise<void> {\n await this.clearAuthState(options?.forgetDevice ?? false);\n // ============================================================================\n // IMPORTANT: Clear challenge session to prevent ghost states\n // ============================================================================\n // WHY: If a user's session expires while they have a pending challenge (MFA, email verification, etc.),\n // the challenge session token becomes invalid. We must clear it to prevent the UI from showing\n // challenge screens that will fail. This fixes the \"ghost session\" issue where users get stuck\n // in challenge flows with stale auth data.\n await this.clearChallenge();\n }\n\n /**\n * Logout current session.\n *\n * Uses GET request to avoid CSRF token issues.\n *\n * @param forgetDevice - If true, also untrust the device (require MFA on next login)\n */\n async logout(forgetDevice?: boolean): Promise<void> {\n const queryParams = forgetDevice ? '?forgetMe=true' : '';\n try {\n await this.get(this.config.endpoints.logout + queryParams, true);\n } catch (error) {\n // Ignore logout errors (session might already be invalid)\n console.warn('[nauth] Logout request failed (session may already be invalid):', error);\n } finally {\n // Always clear local state even if request fails\n // Pass forgetDevice flag to clear device token in JSON mode\n await this.clearAuthState(forgetDevice);\n // Also clear any pending challenge sessions\n await this.clearChallenge();\n this.eventEmitter.emit({\n type: 'auth:logout',\n data: { forgetDevice: !!forgetDevice, global: false },\n timestamp: Date.now(),\n });\n }\n }\n\n /**\n * Logout all sessions.\n *\n * Revokes all active sessions for the current user across all devices.\n * Optionally revokes all trusted devices if forgetDevices is true.\n *\n * @param forgetDevices - If true, also revokes all trusted devices (default: false)\n * @returns Number of sessions revoked\n */\n async logoutAll(forgetDevices?: boolean): Promise<{ revokedCount: number }> {\n try {\n const payload: LogoutAllRequest = {\n forgetDevices: forgetDevices ?? false,\n };\n const result = await this.post<{ message: string; revokedCount: number }>(\n this.config.endpoints.logoutAll,\n payload,\n true,\n );\n // Clear device token in JSON mode if forgetDevices is true\n await this.clearAuthState(forgetDevices);\n // Also clear any pending challenge sessions\n await this.clearChallenge();\n this.eventEmitter.emit({\n type: 'auth:logout',\n data: { forgetDevice: !!forgetDevices, global: true },\n timestamp: Date.now(),\n });\n return { revokedCount: result.revokedCount };\n } catch (error) {\n // If request fails, still clear local state\n await this.clearAuthState(forgetDevices);\n // Also clear any pending challenge sessions\n await this.clearChallenge();\n this.eventEmitter.emit({\n type: 'auth:logout',\n data: { forgetDevice: !!forgetDevices, global: true },\n timestamp: Date.now(),\n });\n throw error;\n }\n }\n\n /**\n * Respond to a challenge.\n *\n * Validates challenge response data before sending to backend.\n * Provides helpful error messages for common validation issues.\n *\n * @param response - Challenge response data\n * @returns Auth response from backend\n * @throws {NAuthClientError} If validation fails\n */\n async respondToChallenge(response: ChallengeResponse): Promise<AuthResponse> {\n // Auto-inject deviceId if SDK has one stored and this is MFA verification\n // This allows SDK to handle device selection internally - consumers don't need to pass deviceId\n if (\n this.selectedDeviceId !== undefined &&\n response.type === AuthChallenge.MFA_REQUIRED &&\n (response.method === 'totp' || response.method === 'passkey')\n ) {\n // TypeScript knows response is MFACodeResponse or MFAPasskeyResponse at this point\n // Both have deviceId?: number property, so we can safely assign it\n const mfaResponse = response as MFACodeResponse | MFAPasskeyResponse;\n mfaResponse.deviceId = this.selectedDeviceId;\n }\n\n // Validate TOTP setup requires both secret and code\n if (response.type === AuthChallenge.MFA_SETUP_REQUIRED && response.method === 'totp') {\n const setupData = response.setupData;\n if (!setupData || typeof setupData !== 'object') {\n throw new NAuthClientError(\n NAuthErrorCode.VALIDATION_FAILED,\n 'TOTP setup requires setupData with both secret and code',\n { details: { field: 'setupData' } },\n );\n }\n\n const secret = setupData['secret'];\n const code = setupData['code'];\n\n if (!secret || typeof secret !== 'string') {\n throw new NAuthClientError(\n NAuthErrorCode.VALIDATION_FAILED,\n 'TOTP setup requires secret in setupData. Make sure to include the secret from getSetupData() response.',\n { details: { field: 'secret' } },\n );\n }\n\n if (!code || typeof code !== 'string') {\n throw new NAuthClientError(\n NAuthErrorCode.VALIDATION_FAILED,\n 'TOTP setup requires code in setupData. Please enter the verification code from your authenticator app.',\n { details: { field: 'code' } },\n );\n }\n }\n\n try {\n const result = await this.post<AuthResponse>(this.config.endpoints.respondChallenge, response);\n await this.handleAuthResponse(result);\n\n // Clear selected device on successful authentication (no more challenges)\n if (result.user && !result.challengeName) {\n this.selectedDeviceId = undefined;\n }\n\n // Emit success or challenge event\n if (result.challengeName) {\n const challengeEvent = { type: 'auth:challenge' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(challengeEvent);\n } else {\n const successEvent = { type: 'auth:success' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(successEvent);\n }\n\n // Auto-handle navigation\n await this.challengeRouter.handleAuthResponse(result, { source: 'challenge' });\n\n return result;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(\n NAuthErrorCode.CHALLENGE_INVALID,\n (error as Error).message || 'Challenge response failed',\n );\n const errorEvent = { type: 'auth:error' as const, data: authError, timestamp: Date.now() };\n this.eventEmitter.emit(errorEvent);\n throw authError;\n }\n }\n\n /**\n * Select an MFA device for verification\n *\n * Call this when user selects a specific device from the MFA selector UI.\n * SDK stores the deviceId internally and auto-injects it when respondToChallenge() is called.\n *\n * @param deviceId - ID of the device user selected\n *\n * @example\n * ```typescript\n * // User clicks \"Microsoft Authenticator\" button\n * client.selectMFADevice(48);\n *\n * // Later, when submitting OTP code:\n * await client.respondToChallenge({\n * type: 'MFA_REQUIRED',\n * session: 'abc123',\n * method: 'totp',\n * code: '123456',\n * // SDK auto-injects deviceId=48 here!\n * });\n * ```\n */\n selectMFADevice(deviceId: number): void {\n this.selectedDeviceId = deviceId;\n }\n\n /**\n * Get available MFA devices from challenge response\n *\n * Returns array of devices for methods that support multiple devices (TOTP, Passkey).\n * Use this to render device selection UI only.\n *\n * @param challenge - Challenge response from login/signup\n * @returns Array of MFA devices with id, name, and type\n *\n * @example\n * ```typescript\n * const devices = client.getMFADevices(challengeResponse);\n * // Returns: [\n * // { id: 48, name: \"Microsoft Authenticator\", type: \"totp\" },\n * // { id: 3, name: \"Google Authenticator\", type: \"totp\" }\n * // ]\n * ```\n */\n getMFADevices(challenge: AuthResponse): Array<{ id: number; name: string; type: string }> {\n const devices = challenge.challengeParameters?.['devices'];\n if (Array.isArray(devices)) {\n return devices as Array<{ id: number; name: string; type: string }>;\n }\n return [];\n }\n\n /**\n * Clear any selected MFA device\n *\n * Useful if user navigates back to device selector or cancels MFA flow.\n */\n clearSelectedDevice(): void {\n this.selectedDeviceId = undefined;\n }\n\n /**\n * Resend a challenge code.\n */\n async resendCode(session: string): Promise<{ destination: string }> {\n const payload: ResendCodeRequest = { session };\n return this.post<{ destination: string }>(this.config.endpoints.resendCode, payload);\n }\n\n /**\n * Get setup data for MFA.\n *\n * Returns method-specific setup information:\n * - TOTP: { secret, qrCode, manualEntryKey }\n * - SMS: { maskedPhone }\n * - Email: { maskedEmail }\n * - Passkey: WebAuthn registration options\n *\n * @param session - Challenge session token\n * @param method - MFA method to set up\n * @returns Setup data wrapped in GetSetupDataResponse\n */\n async getSetupData(session: string, method: GetSetupDataRequest['method']): Promise<GetSetupDataResponse> {\n const payload: GetSetupDataRequest = { session, method };\n return this.post<GetSetupDataResponse>(this.config.endpoints.getSetupData, payload);\n }\n\n /**\n * Get challenge data (e.g., WebAuthn options).\n *\n * Returns challenge-specific data for verification flows.\n *\n * @param session - Challenge session token\n * @param method - Challenge method to get data for\n * @returns Challenge data wrapped in GetChallengeDataResponse\n */\n async getChallengeData(\n session: string,\n method: GetChallengeDataRequest['method'],\n ): Promise<GetChallengeDataResponse> {\n const payload: GetChallengeDataRequest = { session, method };\n return this.post<GetChallengeDataResponse>(this.config.endpoints.getChallengeData, payload);\n }\n\n /**\n * Get current user profile.\n */\n async getProfile(): Promise<AuthUser> {\n const profile = await this.get<AuthUser>(this.config.endpoints.profile, true);\n await this.setUser(profile);\n return profile;\n }\n\n /**\n * Update user profile.\n */\n async updateProfile(updates: UpdateProfileRequest): Promise<AuthUser> {\n const updated = await this.put<AuthUser>(this.config.endpoints.updateProfile, updates, true);\n await this.setUser(updated);\n return updated;\n }\n\n /**\n * Change user password.\n */\n async changePassword(oldPassword: string, newPassword: string): Promise<void> {\n const payload: ChangePasswordRequest = { oldPassword, newPassword };\n await this.post(this.config.endpoints.changePassword, payload, true);\n }\n\n /**\n * Request a password reset code (forgot password).\n */\n async forgotPassword(identifier: string): Promise<ForgotPasswordResponse> {\n const payload: ForgotPasswordRequest = { identifier };\n return this.post<ForgotPasswordResponse>(this.config.endpoints.forgotPassword, payload);\n }\n\n /**\n * Confirm a password reset code and set a new password.\n */\n async confirmForgotPassword(\n identifier: string,\n code: string,\n newPassword: string,\n ): Promise<ConfirmForgotPasswordResponse> {\n const payload: ConfirmForgotPasswordRequest = { identifier, code, newPassword };\n const result = await this.post<ConfirmForgotPasswordResponse>(this.config.endpoints.confirmForgotPassword, payload);\n // ============================================================================\n // IMPORTANT: Password reset revokes all sessions\n // ============================================================================\n // WHY: The backend invalidates all sessions as a security measure. Clearing local auth state avoids\n // stale UI (e.g., still showing social-only/no-password state from cached user) and prevents the\n // client from attempting further authenticated calls with invalid tokens/cookies.\n await this.clearAuthState(false);\n return result;\n }\n\n /**\n * Reset password with verification code (works for both admin-initiated and user-initiated resets).\n *\n * NOTE:\n * - Links (when provided by the backend email provider) include the same verification code as a query param\n * (e.g., `...?code=123456`) so consumer apps stay code-only and consistent.\n *\n * WHY: Generic method that works for both admin-initiated (adminResetPassword) and\n * user-initiated (forgotPassword) password resets. Uses same backend endpoint.\n *\n * @param identifier - User identifier (email, username, phone)\n * @param code - Verification code from email/SMS (6-10 digits)\n * @param newPassword - New password\n * @returns Success response\n * @throws {NAuthClientError} When reset fails\n *\n * @example\n * ```typescript\n * await client.resetPasswordWithCode('user@example.com', '123456', 'NewPass123!');\n * ```\n */\n async resetPasswordWithCode(\n identifier: string,\n code: string,\n newPassword: string,\n ): Promise<ResetPasswordWithCodeResponse> {\n const payload: ResetPasswordWithCodeRequest = {\n identifier,\n code,\n newPassword,\n };\n\n const result = await this.post<ResetPasswordWithCodeResponse>(\n this.config.endpoints.confirmAdminResetPassword,\n payload,\n );\n\n // ============================================================================\n // IMPORTANT: Password reset revokes all sessions\n // ============================================================================\n // WHY: The backend invalidates all sessions as a security measure. Clearing local auth state avoids\n // stale UI (e.g., still showing social-only/no-password state from cached user) and prevents the\n // client from attempting further authenticated calls with invalid tokens/cookies.\n await this.clearAuthState(false);\n\n return result;\n }\n\n /**\n * Get MFA status for current user.\n *\n * @returns Promise of MFA status\n *\n * @example\n * ```typescript\n * const status = await this.client.getMfaStatus();\n * console.log('MFA enabled:', status.enabled);\n * ```\n */\n async getMfaStatus(): Promise<MFAStatus> {\n return this.get<MFAStatus>(this.config.endpoints.mfaStatus, true);\n }\n\n /**\n * Get MFA devices.\n *\n * @returns Promise of MFA devices response\n *\n * @example\n * ```typescript\n * const result = await client.getMfaDevices();\n * console.log('Devices:', result.devices);\n * ```\n */\n async getMfaDevices(): Promise<GetMFADevicesResponse> {\n return this.get<GetMFADevicesResponse>(this.config.endpoints.mfaDevices, true);\n }\n\n /**\n * Setup MFA device (authenticated user).\n *\n * Returns method-specific setup information:\n * - TOTP: { secret, qrCode, manualEntryKey }\n * - SMS: { maskedPhone } or { deviceId, autoCompleted: true }\n * - Email: { maskedEmail } or { deviceId, autoCompleted: true }\n * - Passkey: WebAuthn registration options\n *\n * @param method - MFA method to set up\n * @returns Promise of setup data response\n *\n * @example\n * ```typescript\n * const result = await client.setupMfaDevice('totp');\n * console.log('QR Code:', result.setupData.qrCode);\n * ```\n */\n async setupMfaDevice(method: string): Promise<GetSetupDataResponse> {\n // Backend expects `methodName` (SetupMFADTO). We keep the public SDK method name `method`\n // for ergonomics, but serialize as `methodName` to match the API contract.\n return this.post<GetSetupDataResponse>(this.config.endpoints.mfaSetupData, { methodName: method }, true);\n }\n\n /**\n * Verify MFA setup (authenticated user).\n *\n * Completes MFA device setup by verifying the setup data. The structure of `setupData` varies by method:\n *\n * **TOTP:**\n * - Requires both `secret` (from `getSetupData()` response) and `code` (from authenticator app)\n * - Example: `{ secret: 'JBSWY3DPEHPK3PXP', code: '123456' }`\n *\n * **SMS:**\n * - Requires `phoneNumber` and `code` (verification code sent to phone)\n * - Example: `{ phoneNumber: '+1234567890', code: '123456' }`\n *\n * **Email:**\n * - Requires `code` (verification code sent to email)\n * - Example: `{ code: '123456' }`\n *\n * **Passkey:**\n * - Requires `credential` (WebAuthn credential from registration) and `expectedChallenge`\n * - Example: `{ credential: {...}, expectedChallenge: '...' }`\n *\n * @param method - MFA method ('totp', 'sms', 'email', 'passkey')\n * @param setupData - Method-specific setup verification data\n * @param deviceName - Optional device name (can also be included in setupData for some methods)\n * @returns Promise with device ID of the created MFA device\n *\n * @example TOTP Setup\n * ```typescript\n * // Step 1: Get setup data\n * const setupData = await client.setupMfaDevice('totp');\n * // Returns: { setupData: { secret: 'JBSWY3DPEHPK3PXP', qrCode: '...', ... } }\n *\n * // Step 2: User scans QR code and enters code from authenticator app\n * const code = '123456'; // From authenticator app\n *\n * // Step 3: Verify setup (requires both secret and code)\n * const result = await client.verifyMfaSetup('totp', {\n * secret: setupData.setupData.secret,\n * code: code,\n * }, 'Google Authenticator');\n * // Returns: { deviceId: 123 }\n * ```\n *\n * @example SMS Setup\n * ```typescript\n * const result = await client.verifyMfaSetup('sms', {\n * phoneNumber: '+1234567890', // Phone number receiving the code\n * code: '123456', // Code sent to phone\n * }, 'My iPhone');\n * ```\n *\n * @example Passkey Setup\n * ```typescript\n * const credential = await navigator.credentials.create({\n * publicKey: setupData.setupData.options\n * });\n * const result = await client.verifyMfaSetup('passkey', {\n * credential: credential,\n * expectedChallenge: setupData.setupData.challenge,\n * }, 'MacBook Pro');\n * ```\n */\n async verifyMfaSetup(\n method: string,\n setupData: Record<string, unknown>,\n deviceName?: string,\n ): Promise<{ deviceId: number }> {\n return this.post<{ deviceId: number }>(\n this.config.endpoints.mfaVerifySetup,\n // Backend expects `methodName` (SetupMFADTO). `deviceName` is optional and may be ignored\n // by consumer controllers depending on their DTO/validation strategy.\n { methodName: method, setupData, deviceName },\n true,\n );\n }\n\n /**\n * Remove ALL MFA devices for a specific method type.\n *\n * WARNING: This removes ALL devices of the specified method.\n * For example, if you have 3 TOTP devices, this will remove all 3.\n *\n * **Prefer `removeMfaDeviceById()`** to remove individual devices.\n *\n * @param method - MFA method type ('totp', 'sms', 'email', 'passkey')\n * @returns Success message\n *\n * @example\n * ```typescript\n * // Removes ALL TOTP devices (all authenticator apps)\n * await client.removeMfaDevice('totp');\n * ```\n */\n\n /**\n * Remove a single MFA device by device ID.\n *\n * @param deviceId - MFA device ID\n * @returns Removal response\n */\n async removeMfaDeviceById(deviceId: number): Promise<RemoveMFADeviceResponse> {\n const path = `${this.config.endpoints.mfaDevices}/${deviceId}`;\n return this.delete<RemoveMFADeviceResponse>(path, true);\n }\n\n /**\n * Set a specific MFA device as preferred.\n *\n * This marks the device as preferred and updates the user's preferred MFA method.\n *\n * @param deviceId - MFA device ID\n * @returns Success message\n */\n async setPreferredMfaDevice(deviceId: number): Promise<{ message: string }> {\n const path = `${this.config.endpoints.mfaDevices}/${deviceId}/preferred`;\n return this.post<{ message: string }>(path, {}, true);\n }\n\n /**\n * Generate backup codes.\n */\n async generateBackupCodes(): Promise<string[]> {\n const result = await this.post<{ codes: string[] }>(this.config.endpoints.mfaBackupCodes, {}, true);\n return result.codes;\n }\n\n /**\n * ============================================================================\n * Event System\n * ============================================================================\n */\n\n /**\n * Subscribe to authentication events.\n *\n * Emits events throughout the auth lifecycle for custom logic, analytics, or UI updates.\n *\n * @param event - Event type to listen for, or '*' for all events\n * @param listener - Callback function to handle the event\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * // Listen to successful authentication\n * const unsubscribe = client.on('auth:success', (event) => {\n * console.log('User logged in:', event.data.user);\n * analytics.track('login_success');\n * });\n *\n * // Listen to all events\n * client.on('*', (event) => {\n * console.log('Auth event:', event.type, event.data);\n * });\n *\n * // Unsubscribe when done\n * unsubscribe();\n * ```\n */\n on(event: AuthEventType | '*', listener: AuthEventListener): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Unsubscribe from authentication events.\n *\n * @param event - Event type\n * @param listener - Callback function to remove\n */\n off(event: AuthEventType | '*', listener: AuthEventListener): void {\n this.eventEmitter.off(event, listener);\n }\n\n // ============================================================================\n // Social Authentication\n // ============================================================================\n\n /**\n * Start redirect-first social OAuth flow (web).\n *\n * This performs a browser navigation to:\n * `GET {baseUrl}/social/:provider/redirect?returnTo=...&appState=...`\n *\n * The backend:\n * - generates and stores CSRF state (cluster-safe)\n * - redirects the user to the provider\n * - completes OAuth on callback and sets cookies (or issues an exchange token)\n * - redirects back to `returnTo` with `appState` (and `exchangeToken` for json/hybrid)\n *\n * @param provider - OAuth provider ('google', 'apple', 'facebook')\n * @param options - Optional redirect options\n *\n * @example\n * ```typescript\n * await client.loginWithSocial('google', { returnTo: '/auth/callback', appState: '12345' });\n * ```\n */\n async loginWithSocial(provider: SocialProvider, options?: SocialLoginOptions): Promise<void> {\n // Emit event\n this.eventEmitter.emit({ type: 'oauth:started', data: { provider }, timestamp: Date.now() });\n\n if (hasWindow()) {\n const startPath = this.config.endpoints.socialRedirectStart.replace(':provider', provider);\n // Use buildUrl to ensure authPathPrefix is applied\n const fullUrl = this.buildUrl(startPath);\n const startUrl = new URL(fullUrl);\n\n const redirects = this.config.redirects;\n // Default returnTo to configured post-login success route (best-effort).\n // Consumers are expected to pass an explicit callback route (e.g. '/auth/callback').\n const returnTo = options?.returnTo ?? redirects?.loginSuccess ?? redirects?.success ?? '/';\n\n startUrl.searchParams.set('returnTo', returnTo);\n // Only include action when deviating from the default ('login').\n if (options?.action === 'link') {\n startUrl.searchParams.set('action', 'link');\n }\n if (typeof options?.appState === 'string' && options.appState.trim() !== '') {\n startUrl.searchParams.set('appState', options.appState);\n }\n\n // Serialize oauthParams as JSON string\n if (options?.oauthParams && Object.keys(options.oauthParams).length > 0) {\n startUrl.searchParams.set('oauthParams', JSON.stringify(options.oauthParams));\n }\n\n window.location.href = startUrl.toString();\n }\n }\n\n /**\n * Exchange an `exchangeToken` (from redirect callback URL) into an AuthResponse.\n *\n * Used for `tokenDelivery: 'json'` or hybrid flows where the backend redirects back\n * with `exchangeToken` instead of setting cookies.\n *\n * @param exchangeToken - One-time exchange token from the callback URL\n * @returns AuthResponse\n */\n async exchangeSocialRedirect(exchangeToken: string): Promise<AuthResponse> {\n const token = exchangeToken?.trim();\n if (!token) {\n throw new NAuthClientError(NAuthErrorCode.CHALLENGE_INVALID, 'Missing exchangeToken');\n }\n const result = await this.post<AuthResponse>(this.config.endpoints.socialExchange, { exchangeToken: token });\n await this.handleAuthResponse(result);\n\n // Read appState from sessionStorage WITHOUT clearing (consumer may want to retrieve it later via getLastOauthState())\n const appState = await this.oauthStorage.getItem(OAUTH_STATE_KEY);\n\n // Auto-handle navigation with appState in context\n await this.challengeRouter.handleAuthResponse(result, {\n source: 'social',\n appState: appState ?? undefined,\n });\n\n return result;\n }\n\n /**\n * Verify native social token (mobile).\n */\n async verifyNativeSocial(request: SocialVerifyRequest): Promise<AuthResponse> {\n try {\n const path = this.config.endpoints.socialVerify.replace(':provider', request.provider);\n const result = await this.post<AuthResponse>(path, request);\n await this.handleAuthResponse(result);\n\n // Emit success or challenge event\n if (result.challengeName) {\n const challengeEvent = { type: 'auth:challenge' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(challengeEvent);\n } else {\n const successEvent = { type: 'auth:success' as const, data: result, timestamp: Date.now() };\n this.eventEmitter.emit(successEvent);\n }\n\n return result;\n } catch (error) {\n const authError =\n error instanceof NAuthClientError\n ? error\n : new NAuthClientError(\n NAuthErrorCode.SOCIAL_TOKEN_INVALID,\n (error as Error).message || 'Social verification failed',\n );\n const errorEvent = { type: 'auth:error' as const, data: authError, timestamp: Date.now() };\n this.eventEmitter.emit(errorEvent);\n throw authError;\n }\n }\n\n /**\n * Get linked accounts.\n */\n async getLinkedAccounts(): Promise<LinkedAccountsResponse> {\n return this.get<LinkedAccountsResponse>(this.config.endpoints.socialLinked, true);\n }\n\n /**\n * Link social account.\n */\n async linkSocialAccount(provider: string, code: string, state: string): Promise<{ message: string }> {\n return this.post<{ message: string }>(this.config.endpoints.socialLink, { provider, code, state }, true);\n }\n\n /**\n * Unlink social account.\n */\n async unlinkSocialAccount(provider: string): Promise<{ message: string }> {\n return this.post<{ message: string }>(this.config.endpoints.socialUnlink, { provider }, true);\n }\n\n /**\n * Trust current device.\n */\n async trustDevice(): Promise<{ deviceToken: string }> {\n const result = await this.post<{ deviceToken: string }>(this.config.endpoints.trustDevice, {}, true);\n\n // Only store device token in JSON mode (cookies mode uses httpOnly cookie)\n if (this.config.tokenDelivery === 'json' && result.deviceToken) {\n await this.setDeviceToken(result.deviceToken);\n }\n\n return result;\n }\n\n /**\n * Check if the current device is trusted.\n *\n * Returns whether the current device is trusted based on the device token\n * (from cookie in cookies mode, or header in JSON mode).\n *\n * This performs a server-side validation of the device token and checks:\n * - Device token exists and is valid\n * - Device token matches a trusted device record in the database\n * - Trust has not expired\n *\n * @returns Object with trusted status\n *\n * @example\n * ```typescript\n * const result = await client.isTrustedDevice();\n * if (result.trusted) {\n * console.log('This device is trusted');\n * }\n * ```\n */\n async isTrustedDevice(): Promise<{ trusted: boolean }> {\n return this.get<{ trusted: boolean }>(this.config.endpoints.isTrustedDevice, true);\n }\n\n /**\n * Get authentication audit history for current user.\n *\n * @param params - Optional query parameters (page, limit, eventType, etc.)\n * @returns Paginated audit history\n *\n * @example\n * ```typescript\n * const history = await client.getAuditHistory({\n * page: 1,\n * limit: 20,\n * eventTypes: ['LOGIN_SUCCESS'],\n * eventStatus: ['FAILURE'],\n * });\n * ```\n */\n async getAuditHistory(\n params?: Record<string, string | number | boolean | Array<string | number | boolean>>,\n ): Promise<AuditHistoryResponse> {\n const searchParams = new URLSearchParams();\n\n for (const [key, rawValue] of Object.entries(params ?? {})) {\n if (Array.isArray(rawValue)) {\n for (const item of rawValue) {\n searchParams.append(key, String(item));\n }\n continue;\n }\n\n searchParams.append(key, String(rawValue));\n }\n\n const query = searchParams.toString() ? `?${searchParams.toString()}` : '';\n const path = `${this.config.endpoints.auditHistory}${query}`;\n return this.get<AuditHistoryResponse>(path, true);\n }\n\n /**\n * Initialize client by hydrating state from storage.\n * Call this on app startup to restore auth state.\n */\n async initialize(): Promise<void> {\n const userJson = await this.config.storage.getItem(USER_KEY);\n if (userJson) {\n try {\n this.currentUser = JSON.parse(userJson) as AuthUser;\n this.config.onAuthStateChange?.(this.currentUser);\n } catch {\n // Invalid stored user - clear it\n await this.config.storage.removeItem(USER_KEY);\n }\n }\n }\n\n /**\n * Determine if user is authenticated (async - checks tokens).\n */\n async isAuthenticated(): Promise<boolean> {\n const tokens = await this.tokenManager.getTokens();\n return Boolean(tokens.accessToken);\n }\n\n /**\n * Determine if user is authenticated (sync - checks cached user).\n * Use this for guards and sync checks. Use `isAuthenticated()` for definitive check.\n */\n isAuthenticatedSync(): boolean {\n return this.currentUser !== null;\n }\n\n /**\n * Get current access token (may be null).\n */\n async getAccessToken(): Promise<string | null> {\n const tokens = await this.tokenManager.getTokens();\n return tokens.accessToken ?? null;\n }\n\n /**\n * Get current user (cached, sync).\n */\n getCurrentUser(): AuthUser | null {\n return this.currentUser;\n }\n\n /**\n * Get stored challenge session (for resuming challenge flows).\n */\n async getStoredChallenge(): Promise<AuthResponse | null> {\n const raw = await this.config.storage.getItem(CHALLENGE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as AuthResponse;\n } catch {\n return null;\n }\n }\n\n /**\n * Clear stored challenge session.\n */\n async clearStoredChallenge(): Promise<void> {\n await this.config.storage.removeItem(CHALLENGE_KEY);\n }\n\n /**\n * Internal: handle auth response (tokens or challenge).\n *\n * In cookies mode: Tokens are set as httpOnly cookies by backend, not stored in client storage.\n * In JSON mode: Tokens are stored in tokenManager for Authorization header.\n */\n private async handleAuthResponse(response: AuthResponse): Promise<void> {\n if (response.challengeName) {\n await this.persistChallenge(response);\n return;\n }\n\n // Only store tokens in JSON mode (cookies mode uses httpOnly cookies set by backend)\n if (this.config.tokenDelivery === 'json' && response.accessToken && response.refreshToken) {\n await this.tokenManager.setTokens({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n accessTokenExpiresAt: response.accessTokenExpiresAt ?? 0,\n refreshTokenExpiresAt: response.refreshTokenExpiresAt ?? 0,\n });\n }\n\n // Device token handling (only for JSON mode - cookies mode uses httpOnly cookie)\n if (this.config.tokenDelivery === 'json' && response.deviceToken) {\n await this.setDeviceToken(response.deviceToken);\n }\n\n // Always store user info (needed for both modes)\n if (response.user) {\n const user = response.user as AuthUser;\n // WHY: Consumers often need to know if the current session was created via password\n // or via a specific social provider (google/apple/facebook).\n user.sessionAuthMethod = response.authMethod ?? null;\n await this.setUser(user);\n }\n\n await this.clearChallenge();\n }\n\n /**\n * Persist challenge state.\n */\n private async persistChallenge(challenge: AuthResponse): Promise<void> {\n await this.config.storage.setItem(CHALLENGE_KEY, JSON.stringify(challenge));\n }\n\n /**\n * Clear challenge state.\n */\n private async clearChallenge(): Promise<void> {\n await this.config.storage.removeItem(CHALLENGE_KEY);\n }\n\n /**\n * Persist user.\n */\n private async setUser(user: AuthUser): Promise<void> {\n this.currentUser = user;\n await this.config.storage.setItem(USER_KEY, JSON.stringify(user));\n this.config.onAuthStateChange?.(user);\n }\n\n /**\n * Clear all auth state.\n *\n * @param forgetDevice - If true, also clear device token (for JSON mode)\n */\n private async clearAuthState(forgetDevice?: boolean): Promise<void> {\n this.currentUser = null;\n await this.tokenManager.clearTokens();\n await this.config.storage.removeItem(USER_KEY);\n\n // Clear device token when forgetDevice is true\n if (forgetDevice) {\n // ============================================================================\n // Defensive cleanup: Always attempt to remove device token from localStorage\n // ============================================================================\n // WHY: In JSON mode, device token should be in localStorage and must be cleared.\n // In cookies mode, device token shouldn't be in localStorage, but we attempt cleanup\n // anyway as a defensive measure in case of bugs or mode switches.\n try {\n await this.config.storage.removeItem(this.config.deviceTrust.storageKey);\n } catch {\n // Non-fatal: storage can fail in restricted environments (private mode, SSR, etc.)\n }\n }\n\n // ============================================================================\n // Clear OAuth state to prevent stale appState from being reused\n // ============================================================================\n // WHY: If user logs out or session expires during/after OAuth flow, the stored appState\n // should be cleared to prevent it from being incorrectly applied to a future login.\n try {\n await this.oauthStorage.removeItem(OAUTH_STATE_KEY);\n } catch {\n // Non-fatal: OAuth state is in sessionStorage which might fail in restricted environments\n }\n\n this.config.onAuthStateChange?.(null);\n }\n\n /**\n * Persist device token (json mode mobile).\n */\n private async setDeviceToken(token: string): Promise<void> {\n await this.config.storage.setItem(this.config.deviceTrust.storageKey, token);\n }\n\n /**\n * Determine token delivery mode for this environment.\n */\n private getTokenDeliveryMode(): 'json' | 'cookies' {\n return this.config.tokenDelivery;\n }\n\n /**\n * Build request URL by combining baseUrl with path.\n * Automatically prepends authPathPrefix if configured and not already in path.\n * @private\n */\n private buildUrl(path: string): string {\n // Prepend authPathPrefix if configured and path doesn't already start with it\n // Ensure path starts with '/' for proper prefix concatenation\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n const effectivePath =\n this.config.authPathPrefix && !normalizedPath.startsWith(this.config.authPathPrefix)\n ? `${this.config.authPathPrefix}${normalizedPath}`\n : normalizedPath;\n return `${this.config.baseUrl}${effectivePath}`;\n }\n\n /**\n * Build request headers for authentication.\n *\n * @param auth - Whether to include authentication headers\n * @param method - HTTP method (GET, POST, PUT, DELETE, PATCH)\n * @returns Headers object with auth, CSRF, and device trust headers\n * @private\n */\n private async buildHeaders(\n auth: boolean,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' = 'GET',\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n ...this.config.headers,\n };\n\n // Set Content-Type for mutating requests\n if (method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n // Add access token in JSON mode\n if (auth && this.config.tokenDelivery === 'json') {\n const accessToken = (await this.tokenManager.getTokens()).accessToken;\n if (accessToken) {\n headers['Authorization'] = `Bearer ${accessToken}`;\n }\n }\n\n // ============================================================================\n // Trusted Device Header (JSON mode)\n // ============================================================================\n // In cookies mode the device token is sent automatically via httpOnly cookie.\n // In JSON mode the backend expects the device token via a header (default: X-Device-Token).\n //\n // This is required for:\n // - Checking trust status (`isTrustedDevice`)\n // - Skipping MFA on future logins when a device is trusted\n //\n // We intentionally send it on all requests in JSON mode so the backend can\n // consistently associate requests with a trusted device when present.\n if (this.config.tokenDelivery === 'json') {\n try {\n const deviceToken = await this.config.storage.getItem(this.config.deviceTrust.storageKey);\n if (deviceToken) {\n headers[this.config.deviceTrust.headerName] = deviceToken;\n }\n } catch {\n // Non-fatal: storage can fail in restricted environments (private mode, SSR, etc.).\n }\n }\n\n // ============================================================================\n // CSRF Token (Cookies mode, mutating requests only)\n // ============================================================================\n // CSRF protection is required for mutating HTTP methods (POST, PUT, PATCH, DELETE)\n // to prevent cross-site request forgery attacks when using cookie-based auth.\n const mutatingMethods: readonly ('POST' | 'PUT' | 'PATCH' | 'DELETE')[] = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (\n this.config.tokenDelivery === 'cookies' &&\n hasWindow() &&\n (mutatingMethods as readonly string[]).includes(method)\n ) {\n const csrfToken = this.getCsrfToken();\n if (csrfToken) {\n headers[this.config.csrf.headerName] = csrfToken;\n }\n }\n\n return headers;\n }\n\n /**\n * Get CSRF token from cookie (browser only).\n * @private\n */\n private getCsrfToken(): string | null {\n if (!hasWindow() || typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`));\n return match ? decodeURIComponent(match[2]) : null;\n }\n\n /**\n * Execute GET request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async get<T>(path: string, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'GET');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'GET',\n url,\n headers,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Execute POST request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async post<T>(path: string, body: unknown, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'POST');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'POST',\n url,\n headers,\n body,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Execute PUT request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async put<T>(path: string, body: unknown, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'PUT');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'PUT',\n url,\n headers,\n body,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Execute DELETE request.\n * Note: 401 refresh is handled by framework interceptors (Angular) or manually.\n */\n private async delete<T>(path: string, auth = false): Promise<T> {\n const url = this.buildUrl(path);\n const headers = await this.buildHeaders(auth, 'DELETE');\n const credentials = this.config.tokenDelivery === 'cookies' ? 'include' : 'omit';\n\n const response = await this.config.httpAdapter.request<T>({\n method: 'DELETE',\n url,\n headers,\n credentials,\n });\n return response.data;\n }\n\n /**\n * Handle cross-tab storage updates.\n */\n private readonly handleStorageEvent = (event: StorageEvent): void => {\n if (event.key === 'nauth_sync') {\n // Best-effort reload of user state\n this.config.storage\n .getItem(USER_KEY)\n .then((value: string | null) => (value ? (JSON.parse(value) as AuthUser) : null))\n .then((user: AuthUser | null) => {\n this.currentUser = user;\n this.config.onAuthStateChange?.(user);\n })\n .catch(() => {\n // ignore\n });\n }\n };\n\n /**\n * Get challenge router for manual navigation control.\n * Useful for guards that need to handle errors or build custom URLs.\n *\n * @returns ChallengeRouter instance\n *\n * @example\n * ```typescript\n * const router = client.getChallengeRouter();\n * await router.navigateToError('oauth');\n * ```\n */\n getChallengeRouter(): ChallengeRouter {\n return this.challengeRouter;\n }\n\n /**\n * Store OAuth appState from social redirect callback.\n *\n * This is called automatically by the social redirect callback guard\n * when appState is present in the callback URL. The stored state can\n * be retrieved using getLastOauthState().\n *\n * Stores in sessionStorage (ephemeral) for better security.\n *\n * @param appState - OAuth appState value from callback URL\n *\n * @example\n * ```typescript\n * await client.storeOauthState('invite-code-123');\n * ```\n */\n async storeOauthState(appState: string): Promise<void> {\n if (appState && appState.trim() !== '') {\n await this.oauthStorage.setItem(OAUTH_STATE_KEY, appState);\n }\n }\n\n /**\n * Get the last OAuth appState from social redirect callback.\n *\n * Returns the appState that was stored during the most recent social\n * login redirect callback. This is useful for restoring UI state,\n * applying invite codes, or tracking referral information.\n *\n * The state is automatically cleared after retrieval to prevent reuse.\n * Stored in sessionStorage (ephemeral) for better security.\n *\n * @returns The stored appState, or null if none exists\n *\n * @example\n * ```typescript\n * const appState = await client.getLastOauthState();\n * if (appState) {\n * // Apply invite code or restore UI state\n * console.log('OAuth state:', appState);\n * }\n * ```\n */\n async getLastOauthState(): Promise<string | null> {\n const stored = await this.oauthStorage.getItem(OAUTH_STATE_KEY);\n if (stored) {\n // Clear after retrieval to prevent reuse\n await this.oauthStorage.removeItem(OAUTH_STATE_KEY);\n return stored;\n }\n return null;\n }\n}\n","import { AuthResponse, AuthChallenge } from '../types/auth.types';\nimport { MFAMethod } from '../types/mfa.types';\n\n/**\n * Helper utilities for working with authentication challenges.\n *\n * These utilities reduce boilerplate in consumer applications by providing\n * type-safe access to challenge parameters and state detection.\n */\n\n/**\n * Check if a challenge requires phone collection (user has no phone number).\n *\n * @param challenge - Auth response with challenge\n * @returns True if phone collection is required\n *\n * @example\n * ```typescript\n * const challenge = auth.getCurrentChallenge();\n * if (challenge && requiresPhoneCollection(challenge)) {\n * // Show phone input form\n * }\n * ```\n */\nexport function requiresPhoneCollection(challenge: AuthResponse): boolean {\n if (challenge.challengeName !== AuthChallenge.VERIFY_PHONE) {\n return false;\n }\n\n const params = challenge.challengeParameters;\n return (params?.['requiresPhoneCollection'] as string) === 'true';\n}\n\n/**\n * Get masked destination (email or phone) from challenge parameters.\n *\n * For VERIFY_EMAIL/VERIFY_PHONE challenges, returns `codeDeliveryDestination`.\n * For MFA_REQUIRED challenges, returns `maskedEmail` or `maskedPhone` based on method.\n *\n * @param challenge - Auth response with challenge\n * @returns Masked destination string or null if not available\n *\n * @example\n * ```typescript\n * const destination = getMaskedDestination(challenge);\n * if (destination) {\n * console.log(`Code sent to ${destination}`);\n * }\n * ```\n */\nexport function getMaskedDestination(challenge: AuthResponse): string | null {\n const params = challenge.challengeParameters;\n if (!params) {\n return null;\n }\n\n const challengeName = challenge.challengeName;\n\n if (challengeName === AuthChallenge.MFA_REQUIRED) {\n // MFA_REQUIRED uses maskedEmail or maskedPhone based on preferredMethod\n const method = (params?.['preferredMethod'] || params?.['method']) as MFAMethod | undefined;\n if (method === 'sms') {\n return (params['maskedPhone'] as string) || null;\n }\n if (method === 'email') {\n return (params['maskedEmail'] as string) || null;\n }\n // Fallback: try both if method is not specified\n return (params['maskedPhone'] as string) || (params['maskedEmail'] as string) || null;\n }\n\n // VERIFY_EMAIL and VERIFY_PHONE use codeDeliveryDestination\n return (params['codeDeliveryDestination'] as string) || null;\n}\n\n/**\n * Get preferred MFA method from challenge parameters.\n *\n * @param challenge - Auth response with MFA_REQUIRED challenge\n * @returns MFA method or undefined if not available\n *\n * @example\n * ```typescript\n * const method = getMFAMethod(challenge);\n * if (method === 'totp') {\n * // Show TOTP input\n * }\n * ```\n */\nexport function getMFAMethod(challenge: AuthResponse): MFAMethod | undefined {\n if (challenge.challengeName !== AuthChallenge.MFA_REQUIRED) {\n return undefined;\n }\n\n const params = challenge.challengeParameters;\n return (params?.['preferredMethod'] || params?.['method']) as MFAMethod | undefined;\n}\n\n/**\n * Get challenge instructions from challenge parameters.\n *\n * @param challenge - Auth response with challenge\n * @returns Instructions string or undefined if not available\n *\n * @example\n * ```typescript\n * const instructions = getChallengeInstructions(challenge);\n * if (instructions) {\n * console.log(instructions);\n * }\n * ```\n */\nexport function getChallengeInstructions(challenge: AuthResponse): string | undefined {\n const params = challenge.challengeParameters;\n return params?.['instructions'] as string | undefined;\n}\n\n/**\n * Check if challenge is OTP-based (requires code input).\n *\n * @param challenge - Auth response with challenge\n * @returns True if challenge requires OTP code\n *\n * @example\n * ```typescript\n * if (isOTPChallenge(challenge)) {\n * // Show OTP input component\n * }\n * ```\n */\nexport function isOTPChallenge(challenge: AuthResponse): boolean {\n const challengeName = challenge.challengeName;\n return (\n challengeName === AuthChallenge.VERIFY_EMAIL ||\n challengeName === AuthChallenge.VERIFY_PHONE ||\n challengeName === AuthChallenge.MFA_REQUIRED\n );\n}\n"],"mappings":"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","names":["AuthChallenge","NAuthErrorCode","AuthAuditEventType","ACCESS_TOKEN_KEY","USER_KEY","CHALLENGE_KEY","OAUTH_STATE_KEY","hasWindow"]}
|