{"version":3,"sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * FamilySearch OAuth Authentication Module\n *\n * Provides OAuth 2.0 authentication utilities for FamilySearch API v3.\n * This module is designed to be framework-agnostic and can be used\n * in any JavaScript/TypeScript environment.\n */\n\nimport type {\n\tFamilySearchEnvironment,\n\tOAuthConfig,\n\tOAuthEndpoints,\n\tOAuthStateValidation,\n\tOAuthTokenResponse,\n} from \"../types\";\n\n// OAuth endpoints by environment\nconst OAUTH_ENDPOINTS: Record<FamilySearchEnvironment, OAuthEndpoints> = {\n\tproduction: {\n\t\tauthorization:\n\t\t\t\"https://ident.familysearch.org/cis-web/oauth2/v3/authorization\",\n\t\ttoken: \"https://ident.familysearch.org/cis-web/oauth2/v3/token\",\n\t\tcurrentUser: \"https://api.familysearch.org/platform/users/current\",\n\t},\n\tbeta: {\n\t\tauthorization:\n\t\t\t\"https://identbeta.familysearch.org/cis-web/oauth2/v3/authorization\",\n\t\ttoken: \"https://identbeta.familysearch.org/cis-web/oauth2/v3/token\",\n\t\tcurrentUser: \"https://apibeta.familysearch.org/platform/users/current\",\n\t},\n\tintegration: {\n\t\tauthorization:\n\t\t\t\"https://identint.familysearch.org/cis-web/oauth2/v3/authorization\",\n\t\ttoken: \"https://identint.familysearch.org/cis-web/oauth2/v3/token\",\n\t\tcurrentUser:\n\t\t\t\"https://api-integ.familysearch.org/platform/users/current\",\n\t},\n};\n\n/**\n * Get OAuth endpoints for a specific environment\n */\nexport function getOAuthEndpoints(\n\tenvironment: FamilySearchEnvironment = \"integration\"\n): OAuthEndpoints {\n\treturn OAUTH_ENDPOINTS[environment];\n}\n\n/**\n * Generate a cryptographically secure random state for CSRF protection\n */\nexport function generateOAuthState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n\t\t\"\"\n\t);\n}\n\n/**\n * Build the authorization URL for OAuth flow\n */\nexport function buildAuthorizationUrl(\n\tconfig: OAuthConfig,\n\tstate: string,\n\toptions: {\n\t\tscopes?: string[];\n\t\tprompt?: string;\n\t} = {}\n): string {\n\tconst endpoints = getOAuthEndpoints(config.environment);\n\tconst url = new URL(endpoints.authorization);\n\n\turl.searchParams.set(\"response_type\", \"code\");\n\turl.searchParams.set(\"client_id\", config.clientId);\n\turl.searchParams.set(\"redirect_uri\", config.redirectUri);\n\turl.searchParams.set(\"state\", state);\n\n\tif (options.scopes && options.scopes.length > 0) {\n\t\turl.searchParams.set(\"scope\", options.scopes.join(\" \"));\n\t}\n\n\tif (options.prompt) {\n\t\turl.searchParams.set(\"prompt\", options.prompt);\n\t}\n\n\treturn url.toString();\n}\n\n/**\n * Exchange authorization code for access token\n */\nexport async function exchangeCodeForToken(\n\tcode: string,\n\tconfig: OAuthConfig\n): Promise<OAuthTokenResponse> {\n\tconst endpoints = getOAuthEndpoints(config.environment);\n\n\tconst response = await fetch(endpoints.token, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tcode: code,\n\t\t\tclient_id: config.clientId,\n\t\t\tredirect_uri: config.redirectUri,\n\t\t}),\n\t});\n\n\tif (!response.ok) {\n\t\tconst error = await response.text();\n\t\tthrow new Error(`Failed to exchange code for token: ${error}`);\n\t}\n\n\treturn response.json();\n}\n\n/**\n * Refresh an access token using a refresh token\n */\nexport async function refreshAccessToken(\n\trefreshToken: string,\n\tconfig: OAuthConfig\n): Promise<OAuthTokenResponse> {\n\tconst endpoints = getOAuthEndpoints(config.environment);\n\n\tconst response = await fetch(endpoints.token, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: config.clientId,\n\t\t}),\n\t});\n\n\tif (!response.ok) {\n\t\tconst error = await response.text();\n\t\tthrow new Error(`Failed to refresh token: ${error}`);\n\t}\n\n\treturn response.json();\n}\n\n/**\n * Validate an access token by making a test API call\n */\nexport async function validateAccessToken(\n\taccessToken: string,\n\tenvironment: FamilySearchEnvironment = \"integration\"\n): Promise<boolean> {\n\tconst endpoints = getOAuthEndpoints(environment);\n\n\ttry {\n\t\tconst response = await fetch(endpoints.currentUser, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\tAccept: \"application/json\",\n\t\t\t},\n\t\t});\n\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get user info from access token\n */\nexport async function getUserInfo(\n\taccessToken: string,\n\tenvironment: FamilySearchEnvironment = \"integration\"\n): Promise<{\n\tsub: string;\n\tname?: string;\n\tgiven_name?: string;\n\tfamily_name?: string;\n\temail?: string;\n\temail_verified?: boolean;\n} | null> {\n\tconst endpoints = getOAuthEndpoints(environment);\n\n\ttry {\n\t\tconst response = await fetch(endpoints.currentUser, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\tAccept: \"application/json\",\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = await response.json();\n\t\tconst fsUser = data.users?.[0];\n\n\t\tif (!fsUser || !fsUser.id) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\tsub: fsUser.id,\n\t\t\tname: fsUser.contactName || fsUser.displayName,\n\t\t\tgiven_name: fsUser.givenName,\n\t\t\tfamily_name: fsUser.familyName,\n\t\t\temail: fsUser.email,\n\t\t\temail_verified: fsUser.email ? true : false,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n// ====================================\n// Browser-specific OAuth Helpers\n// ====================================\n\n/**\n * Storage keys for OAuth state management\n */\nexport const OAUTH_STORAGE_KEYS = {\n\tstate: \"fs_oauth_state\",\n\tlinkMode: \"fs_oauth_link_mode\",\n\tlang: \"fs_oauth_lang\",\n\tparentUid: \"fs_oauth_parent_uid\",\n} as const;\n\n/**\n * Store OAuth state in localStorage for popup flow\n * Uses localStorage instead of sessionStorage because popup windows\n * don't share sessionStorage with the parent window\n */\nexport function storeOAuthState(\n\tstate: string,\n\toptions: {\n\t\tisLinkMode?: boolean;\n\t\tlang?: string;\n\t\tparentUid?: string;\n\t} = {}\n): void {\n\tif (typeof localStorage === \"undefined\") {\n\t\t// In server-side or non-browser environments, state storage is not available\n\t\t// Callers should handle this by implementing their own state storage mechanism\n\t\tthrow new Error(\n\t\t\t\"localStorage is not available. For server-side usage, implement custom state storage.\"\n\t\t);\n\t}\n\n\tlocalStorage.setItem(OAUTH_STORAGE_KEYS.state, state);\n\n\tif (options.isLinkMode) {\n\t\tlocalStorage.setItem(OAUTH_STORAGE_KEYS.linkMode, \"true\");\n\t} else {\n\t\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.linkMode);\n\t}\n\n\tif (options.lang) {\n\t\tlocalStorage.setItem(OAUTH_STORAGE_KEYS.lang, options.lang);\n\t} else {\n\t\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.lang);\n\t}\n\n\tif (options.parentUid) {\n\t\tlocalStorage.setItem(OAUTH_STORAGE_KEYS.parentUid, options.parentUid);\n\t} else {\n\t\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.parentUid);\n\t}\n}\n\n/**\n * Validate OAuth state from callback and extract metadata\n * Returns invalid state if localStorage is not available (SSR/Node.js environments)\n */\nexport function validateOAuthState(state: string): OAuthStateValidation {\n\tif (typeof localStorage === \"undefined\") {\n\t\t// In server-side environments, return invalid state\n\t\t// Callers should implement their own state validation for SSR\n\t\treturn { valid: false, isLinkMode: false };\n\t}\n\n\tconst storedState = localStorage.getItem(OAUTH_STORAGE_KEYS.state);\n\tconst isLinkMode =\n\t\tlocalStorage.getItem(OAUTH_STORAGE_KEYS.linkMode) === \"true\";\n\tconst lang = localStorage.getItem(OAUTH_STORAGE_KEYS.lang) || undefined;\n\tconst parentUid =\n\t\tlocalStorage.getItem(OAUTH_STORAGE_KEYS.parentUid) || undefined;\n\n\t// Clean up stored values\n\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.state);\n\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.linkMode);\n\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.lang);\n\tlocalStorage.removeItem(OAUTH_STORAGE_KEYS.parentUid);\n\n\treturn {\n\t\tvalid: storedState === state,\n\t\tisLinkMode,\n\t\tlang,\n\t\tparentUid,\n\t};\n}\n\n/**\n * Open OAuth authorization in a popup window\n */\nexport function openOAuthPopup(\n\tauthUrl: string,\n\toptions: {\n\t\twidth?: number;\n\t\theight?: number;\n\t\twindowName?: string;\n\t} = {}\n): Window | null {\n\tif (typeof window === \"undefined\") {\n\t\tthrow new Error(\"window is not available\");\n\t}\n\n\tconst width = options.width || 500;\n\tconst height = options.height || 600;\n\tconst windowName = options.windowName || \"FamilySearch Login\";\n\n\tconst left = window.screenX + (window.outerWidth - width) / 2;\n\tconst top = window.screenY + (window.outerHeight - height) / 2;\n\n\tconst popup = window.open(\n\t\tauthUrl,\n\t\twindowName,\n\t\t`width=${width},height=${height},left=${left},top=${top},toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes`\n\t);\n\n\tif (popup) {\n\t\tpopup.focus();\n\t}\n\n\treturn popup;\n}\n\n/**\n * Parse OAuth callback parameters from URL\n */\nexport function parseCallbackParams(\n\turl: string = typeof window !== \"undefined\" ? window.location.href : \"\"\n): {\n\tcode?: string;\n\tstate?: string;\n\terror?: string;\n\terror_description?: string;\n} {\n\tconst urlObj = new URL(url);\n\tconst params = urlObj.searchParams;\n\n\treturn {\n\t\tcode: params.get(\"code\") || undefined,\n\t\tstate: params.get(\"state\") || undefined,\n\t\terror: params.get(\"error\") || undefined,\n\t\terror_description: params.get(\"error_description\") || undefined,\n\t};\n}\n\n// ====================================\n// Token Storage Helpers\n// ====================================\n\n/**\n * Generate a storage key scoped to a user ID\n */\nexport function getTokenStorageKey(\n\tuserId: string,\n\ttype: \"access\" | \"expires\" | \"refresh\" | \"environment\"\n): string {\n\treturn `fs_token_${userId}_${type}`;\n}\n\n/**\n * Store access token with expiration\n * Per FamilySearch compatibility requirements:\n * - Access tokens stored in sessionStorage (cleared on browser close)\n * - Refresh tokens stored in localStorage (for re-authentication)\n */\nexport function storeTokens(\n\tuserId: string,\n\ttokens: {\n\t\taccessToken: string;\n\t\texpiresAt?: number;\n\t\trefreshToken?: string;\n\t\tenvironment?: string;\n\t}\n): void {\n\tif (typeof sessionStorage === \"undefined\" || typeof localStorage === \"undefined\") {\n\t\tthrow new Error(\"Storage APIs are not available\");\n\t}\n\n\t// Access tokens in sessionStorage (temporary)\n\tsessionStorage.setItem(\n\t\tgetTokenStorageKey(userId, \"access\"),\n\t\ttokens.accessToken\n\t);\n\n\tif (tokens.expiresAt) {\n\t\tsessionStorage.setItem(\n\t\t\tgetTokenStorageKey(userId, \"expires\"),\n\t\t\ttokens.expiresAt.toString()\n\t\t);\n\t}\n\n\t// Refresh tokens in localStorage (persistent)\n\tif (tokens.refreshToken) {\n\t\tlocalStorage.setItem(\n\t\t\tgetTokenStorageKey(userId, \"refresh\"),\n\t\t\ttokens.refreshToken\n\t\t);\n\t}\n\n\tif (tokens.environment) {\n\t\tlocalStorage.setItem(\n\t\t\tgetTokenStorageKey(userId, \"environment\"),\n\t\t\ttokens.environment\n\t\t);\n\t}\n}\n\n/**\n * Get stored access token\n */\nexport function getStoredAccessToken(userId: string): string | null {\n\tif (typeof sessionStorage === \"undefined\") {\n\t\treturn null;\n\t}\n\n\tconst token = sessionStorage.getItem(getTokenStorageKey(userId, \"access\"));\n\tconst expiresAt = sessionStorage.getItem(\n\t\tgetTokenStorageKey(userId, \"expires\")\n\t);\n\n\tif (!token) {\n\t\treturn null;\n\t}\n\n\t// Check expiration with 5-minute buffer\n\tconst EXPIRATION_BUFFER = 5 * 60 * 1000;\n\tif (expiresAt && Date.now() > parseInt(expiresAt) - EXPIRATION_BUFFER) {\n\t\treturn null;\n\t}\n\n\treturn token;\n}\n\n/**\n * Get stored refresh token\n */\nexport function getStoredRefreshToken(userId: string): string | null {\n\tif (typeof localStorage === \"undefined\") {\n\t\treturn null;\n\t}\n\n\treturn localStorage.getItem(getTokenStorageKey(userId, \"refresh\"));\n}\n\n/**\n * Clear all stored tokens for a user\n */\nexport function clearStoredTokens(userId: string): void {\n\tif (typeof sessionStorage !== \"undefined\") {\n\t\tsessionStorage.removeItem(getTokenStorageKey(userId, \"access\"));\n\t\tsessionStorage.removeItem(getTokenStorageKey(userId, \"expires\"));\n\t}\n\n\tif (typeof localStorage !== \"undefined\") {\n\t\tlocalStorage.removeItem(getTokenStorageKey(userId, \"refresh\"));\n\t\tlocalStorage.removeItem(getTokenStorageKey(userId, \"environment\"));\n\t}\n}\n\n/**\n * Clear all FamilySearch tokens from storage\n */\nexport function clearAllTokens(): void {\n\tif (typeof sessionStorage === \"undefined\" || typeof localStorage === \"undefined\") {\n\t\treturn;\n\t}\n\n\tconst keysToRemove: string[] = [];\n\n\t// Find all fs_token_* keys in sessionStorage\n\tfor (let i = 0; i < sessionStorage.length; i++) {\n\t\tconst key = sessionStorage.key(i);\n\t\tif (key && key.startsWith(\"fs_token_\")) {\n\t\t\tkeysToRemove.push(key);\n\t\t}\n\t}\n\n\t// Find all fs_token_* keys in localStorage\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst key = localStorage.key(i);\n\t\tif (key && key.startsWith(\"fs_token_\")) {\n\t\t\tkeysToRemove.push(key);\n\t\t}\n\t}\n\n\t// Remove from both storages\n\tkeysToRemove.forEach((key) => {\n\t\tsessionStorage.removeItem(key);\n\t\tlocalStorage.removeItem(key);\n\t});\n}\n\nexport { OAUTH_ENDPOINTS };\n"]}