@vunexa/lixa 0.1.6-alpha.12 → 0.1.6-alpha.13
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/README.md +4 -4
- package/dist/credentials/credentials-manager.d.ts +65 -0
- package/dist/credentials/credentials-manager.d.ts.map +1 -0
- package/dist/credentials/hasher.d.ts +88 -0
- package/dist/credentials/hasher.d.ts.map +1 -0
- package/dist/credentials/index.d.ts +6 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/local-storage.d.ts +20 -0
- package/dist/credentials/local-storage.d.ts.map +1 -0
- package/dist/credentials/policy.d.ts +12 -0
- package/dist/credentials/policy.d.ts.map +1 -0
- package/dist/credentials/types.d.ts +314 -0
- package/dist/credentials/types.d.ts.map +1 -0
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/export-types/index.d.ts +651 -13
- package/dist/index.cjs +823 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +635 -13
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +805 -96
- package/dist/index.js.map +1 -1
- package/dist/lixa.d.ts +59 -3
- package/dist/lixa.d.ts.map +1 -1
- package/dist/models/session.d.ts +8 -8
- package/dist/models/session.d.ts.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lixa.ts","../src/types.ts","../src/dao/state-cache.ts","../src/dao/session-cache.ts","../src/utils/user-info.ts","../src/errors.ts","../src/utils/cookies.ts"],"sourcesContent":["/**\n * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library for backend applications.\n * \n * @remarks\n * This package simplifies multi-provider authentication flows (e.g., Google, GitHub), supports extensible session management, and enables custom provider registration.\n * \n * Key features:\n * - OAuth 2.0 authorization code flow with PKCE (RFC 6749, RFC 7636)\n * - OpenID Connect support\n * - Built-in providers available in \\@vunexa/lixa-providers\n * - Custom provider support via IProvider interface\n * - Extensible session management via SessionDao.CreateSession\n * - Pluggable state and session storage via StateDao and SessionDao\n * - TypeScript-first with comprehensive type safety\n * \n * @packageDocumentation\n */\n\nexport { Lixa } from \"./lixa\";\nexport {\n type ProviderConfig,\n type LixaConfig,\n type SafeLixaConfig,\n type IProvider,\n type Session,\n type ConnectedResource,\n type ProviderMetadata,\n type OAuthTokenResponse,\n type StateHandler,\n type StateStorage,\n type SessionHandler,\n type SessionStorage,\n type ResourceHandler,\n type ResourceStorage,\n type StateData,\n AccountLinkingStrategy,\n type AccountLinkingMode,\n type AccountLinkingConfig,\n type LogLevel,\n type LogContext,\n type LixaLogger,\n} from \"./types\";\nexport {\n type UserInfo,\n extractUserInfo,\n decodeIdToken,\n fetchUserInfo,\n determineProviderFromIssuer,\n} from \"./utils/user-info\";\nexport {\n LixaError,\n InvalidStateError,\n ProviderNotConfiguredError,\n InvalidProviderConfigError,\n InvalidOAuthCallbackError,\n TokenExchangeError,\n SessionNotFoundError,\n EmailNotVerifiedError,\n AccountUnlinkError,\n RefreshTokenError,\n} from \"./errors\";\nexport {\n type CookieOptions,\n type CookiePayload,\n DEFAULT_SESSION_COOKIE_NAME,\n DEFAULT_STATE_COOKIE_NAME,\n DEFAULT_SESSION_MAX_AGE_SECONDS,\n DEFAULT_STATE_MAX_AGE_SECONDS,\n isProductionEnvironment,\n serializeCookie,\n createSessionCookie,\n clearSessionCookie,\n createStateCookie,\n clearStateCookie,\n} from \"./utils/cookies\";\n\n\n","import { randomBytes } from \"crypto\";\nimport { AccountLinkingStrategy, type LixaConfig, type ProviderConfig } from \"./types\";\nimport { IProvider } from \"./providers\";\nimport { LocalStateHandler } from \"./dao/state-cache\";\nimport { SessionHandler, StateHandler, ResourceHandler, ResourceStorage } from \"./dao/types\";\nimport crypto from \"crypto\";\nimport { LocalSessionHandler } from \"./dao/session-cache\";\nimport { type Session, type ConnectedResource, type OAuthTokenResponse, type ProviderMetadata } from \"./models/session\";\nimport { extractUserInfo, type UserInfo } from \"./utils/user-info\";\nimport {\n LixaError,\n InvalidStateError,\n ProviderNotConfiguredError,\n InvalidProviderConfigError,\n InvalidOAuthCallbackError,\n TokenExchangeError,\n SessionNotFoundError,\n EmailNotVerifiedError,\n AccountUnlinkError,\n RefreshTokenError,\n} from \"./errors\";\n\n/**\n * Type representing the keys of configured providers\n */\ntype ConfiguredProviderKey<T extends LixaConfig<Record<string, ProviderConfig>>> = keyof T['providers'];\n\n/**\n * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library.\n *\n * @remarks\n * Lixa simplifies multi-provider authentication flows and supports extensible session management.\n * Providers can be passed inline in the configuration, eliminating the need for pre-registration.\n *\n * @example\n * Using built-in providers from \\@vunexa/lixa-providers:\n * ```typescript\n * import { Lixa } from '@vunexa/lixa';\n * import { GoogleProvider } from '@vunexa/lixa-providers';\n * \n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://yourapp.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * }\n * });\n * ```\n * \n * @example\n * Using custom inline providers:\n * ```typescript\n * import { Lixa, IProvider } from '@vunexa/lixa';\n * \n * const customProvider: IProvider = {\n * authorizationEndpoint: 'https://custom.com/oauth/authorize',\n * tokenEndpoint: 'https://custom.com/oauth/token',\n * userInfoEndpoint: 'https://custom.com/api/user'\n * };\n * \n * const lixa = new Lixa({\n * providers: {\n * custom: {\n * provider: customProvider,\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://yourapp.com/auth/custom/callback',\n * scopes: ['read:user']\n * }\n * }\n * });\n * ```\n *\n * @public\n */\nclass Lixa<TConfig extends LixaConfig<Record<string, ProviderConfig>> = LixaConfig> {\n private static DEFAULT_PROVIDERS: Map<string, IProvider> = new Map();\n private static CONFIGURED_PROVIDERS: Map<string, IProvider> = new Map(); // Legacy registry for backward compatibility\n\n // Instance-scoped fallback handlers to ensure no cross-instance state pollution\n private localStateHandler: StateHandler;\n private localSessionHandler: SessionHandler;\n private localResourceHandler: ResourceHandler;\n private userResourceStore: Map<string, Map<string, ConnectedResource>> = new Map();\n private refreshMutexes: Map<string, Promise<ConnectedResource>> = new Map();\n\n private config: TConfig;\n private stateHandler: StateHandler;\n private sessionHandler: SessionHandler;\n private resourceHandler: ResourceHandler;\n private debug: boolean;\n\n /**\n * Creates a new Lixa instance with the provided configuration.\n * \n * @remarks\n * Providers can be passed inline in the configuration using the `provider` field.\n * Provider resolution priority: inline custom provider \\> default providers \\> legacy registry.\n * \n * @param config - The configuration object containing provider settings and optional session strategy\n * \n * @throws Error when provider configuration is missing required fields\n * @throws Error when provider implementation is missing required properties\n * @throws Error when provider is not available and no inline implementation is provided\n */\n constructor(config: TConfig) {\n this.config = config;\n this.debug = config.debug || false;\n\n // Initialize instance-isolated fallback handlers\n this.localStateHandler = new LocalStateHandler();\n this.localSessionHandler = new LocalSessionHandler();\n this.localResourceHandler = {\n resourceStorage: {\n saveResource: async (userId: string, provider: string, resource: ConnectedResource) => {\n let userMap = this.userResourceStore.get(userId);\n if (!userMap) {\n userMap = new Map();\n this.userResourceStore.set(userId, userMap);\n }\n userMap.set(provider.toLowerCase(), resource);\n },\n getResource: async (userId: string, provider: string) => {\n const userMap = this.userResourceStore.get(userId);\n return userMap?.get(provider.toLowerCase()) || null;\n },\n getUserResources: async (userId: string) => {\n const userMap = this.userResourceStore.get(userId);\n const result: Record<string, ConnectedResource> = {};\n if (userMap) {\n for (const [p, r] of userMap.entries()) {\n result[p] = r;\n }\n }\n return result;\n },\n deleteResource: async (userId: string, provider: string) => {\n const userMap = this.userResourceStore.get(userId);\n if (userMap) {\n userMap.delete(provider.toLowerCase());\n }\n },\n },\n };\n\n this.stateHandler = config.stateHandler || this.localStateHandler;\n this.sessionHandler = config.sessionHandler || this.localSessionHandler;\n this.resourceHandler = config.resourceHandler || this.localResourceHandler;\n \n this.log('INFO', 'Init', 'Initializing Lixa instance', { \n providers: Object.keys(config.providers),\n debug: this.debug \n });\n \n // Validate and extract providers from configuration\n for (const [providerName, providerConfig] of Object.entries(config.providers)) {\n const name = providerName.toLowerCase();\n const typedConfig: ProviderConfig = providerConfig;\n \n // Validate provider configuration has required credentials\n this.validateProviderConfig(providerName, typedConfig);\n \n // If provider config includes a custom provider implementation, validate it\n if (typedConfig.provider) {\n this.validateProviderImplementation(providerName, typedConfig.provider);\n this.log('INFO', 'Init', `Registered inline provider: ${providerName}`);\n } else {\n // Check if it's available in default providers or legacy registry\n if (!Lixa.DEFAULT_PROVIDERS.has(name) && !Lixa.CONFIGURED_PROVIDERS.has(name)) {\n this.log('ERROR', 'Init', `Provider '${providerName}' not available`);\n throw new ProviderNotConfiguredError(\n providerName,\n { hint: `Ensure the provider is registered via '@vunexa/lixa-providers' or provided inline.` }\n );\n }\n this.log('INFO', 'Init', `Using registered provider: ${providerName}`);\n }\n }\n \n this.log('INFO', 'Init', 'Lixa instance initialized successfully');\n }\n \n /**\n * Validates that a provider configuration has all required credentials.\n * \n * @param name - The provider name\n * @param config - The provider configuration\n * @throws InvalidProviderConfigError when required fields are missing or invalid\n */\n private validateProviderConfig(name: string, config: ProviderConfig): void {\n const requiredFields: (keyof ProviderConfig)[] = ['clientId', 'clientSecret', 'redirectUri', 'scopes'];\n const missingFields = requiredFields.filter(field => {\n const value = config[field];\n return value === undefined || value === null || (typeof value === 'string' && value.trim() === '');\n });\n \n if (missingFields.length > 0) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' configuration is missing required fields: ${missingFields.join(', ')}`,\n { provider: name, missingFields }\n );\n }\n \n // Validate scopes is an array\n if (!Array.isArray(config.scopes)) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' configuration error: 'scopes' must be an array of strings`,\n { provider: name }\n );\n }\n \n if (config.scopes.length === 0) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' configuration error: 'scopes' array cannot be empty`,\n { provider: name }\n );\n }\n }\n \n /**\n * Validates that a provider implementation has all required properties.\n * \n * @param name - The provider name\n * @param provider - The provider implementation\n * @throws InvalidProviderConfigError when required properties are missing\n */\n private validateProviderImplementation(name: string, provider: IProvider): void {\n const requiredProps: (keyof IProvider)[] = ['authorizationEndpoint', 'tokenEndpoint', 'userInfoEndpoint'];\n const missingProps = requiredProps.filter(prop => {\n const value = provider[prop];\n return !value || typeof value !== 'string' || value.trim() === '';\n });\n \n if (missingProps.length > 0) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' implementation is missing required properties: ${missingProps.join(', ')}. ` +\n `All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`,\n { provider: name, missingProps }\n );\n }\n }\n\n /**\n * Structured logging with standardized format and custom logger support.\n * \n * @param level - Log level (INFO, WARN, ERROR, DEBUG)\n * @param context - Context of the log (Init, Auth, Token, Session, State, AccountLinking, Resource)\n * @param message - Log message\n * @param data - Optional data to log\n */\n private log(\n level: 'INFO' | 'WARN' | 'ERROR' | 'DEBUG',\n context: 'Init' | 'Auth' | 'Token' | 'Session' | 'State' | 'AccountLinking' | 'Resource',\n message: string,\n data?: Record<string, unknown>\n ): void {\n if (this.config.logger) {\n try {\n this.config.logger.log(level, context, message, data);\n } catch {\n // Ignore custom logger exceptions\n }\n }\n\n if (!this.debug) return;\n \n const timestamp = new Date().toISOString();\n const prefix = `[Lixa] [${timestamp}] [${level}] [${context}]`;\n \n if (data !== undefined) {\n console.log(`${prefix} ${message}`, data);\n } else {\n console.log(`${prefix} ${message}`);\n }\n }\n\n /**\n * Checks if a provider is configured for this instance.\n * This is a type guard that narrows the provider type for use with getAuthUrl.\n *\n * @param provider - The provider name to check (case-insensitive)\n * @returns True if the provider is configured, false otherwise\n *\n * @example\n * ```typescript\n * if (lixa.isProviderConfigured(provider)) {\n * // TypeScript now knows provider is a valid ConfiguredProviderKey\n * const authUrl = lixa.getAuthUrl(provider, state);\n * }\n * ```\n */\n public isProviderConfigured<T extends string>(provider: T): provider is T & ConfiguredProviderKey<TConfig> {\n const providerType = provider.toLowerCase();\n return this.config.providers.hasOwnProperty(providerType);\n }\n \n /**\n * Gets a provider implementation by name.\n * Resolution priority: inline custom provider \\> default providers \\> legacy registry\n * \n * @param name - The provider name (case-insensitive)\n * @param config - The provider configuration\n * @returns The provider implementation\n * @throws Error when provider is not found\n */\n private getProvider(name: string, config: ProviderConfig): IProvider {\n // First check if provider is inline in config\n if (config.provider) {\n return config.provider;\n }\n \n // Then check default providers\n const lowerName = name.toLowerCase();\n const defaultProvider = Lixa.DEFAULT_PROVIDERS.get(lowerName);\n if (defaultProvider) {\n return defaultProvider;\n }\n \n // Finally check legacy registry for backward compatibility\n const legacyProvider = Lixa.CONFIGURED_PROVIDERS.get(lowerName);\n if (legacyProvider) {\n return legacyProvider;\n }\n \n throw new ProviderNotConfiguredError(\n name,\n { hint: `Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().` }\n );\n }\n\n /**\n * Registers custom OAuth providers for use with Lixa.\n * \n * @deprecated This method is maintained for backward compatibility.\n * The recommended approach is to pass providers inline in the configuration:\n * ```typescript\n * const lixa = new Lixa({\n * providers: {\n * custom: {\n * provider: new CustomProvider(),\n * clientId: '...',\n * // ...\n * }\n * }\n * });\n * ```\n *\n * @param providerMap - A map of provider names to IProvider implementations\n *\n * @example\n * Legacy usage (still supported):\n * ```typescript\n * class CustomProvider implements IProvider {\n * authorizationEndpoint = 'https://custom.com/oauth/authorize';\n * tokenEndpoint = 'https://custom.com/oauth/token';\n * userInfoEndpoint = 'https://custom.com/api/user';\n * }\n *\n * Lixa.registerProvider({ custom: new CustomProvider() });\n * ```\n */\n public static registerProvider<T extends Record<string, IProvider>>(providerMap: T): void {\n Object.entries(providerMap).forEach(([key, providerImpl]) => {\n Lixa.CONFIGURED_PROVIDERS.set(key.toLowerCase(), providerImpl);\n });\n }\n\n /**\n * Gets the list of registered provider names.\n * \n * @returns Array of registered provider names\n */\n public static getRegisteredProviders(): string[] {\n return Array.from(Lixa.CONFIGURED_PROVIDERS.keys());\n }\n\n /**\n * Creates a type-safe configuration.\n * \n * @deprecated This method is maintained for backward compatibility.\n * You can now pass configuration directly to the Lixa constructor without this helper.\n * \n * @param config - Configuration object with provider settings\n * @returns The same configuration object with type safety\n * \n * @example\n * New approach (recommended):\n * ```typescript\n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: '...',\n * // ...\n * }\n * }\n * });\n * ```\n */\n public static createConfig<T extends Record<string, ProviderConfig>>(\n config: LixaConfig<T> & { providers: T }\n ): LixaConfig<T> {\n return config;\n }\n\n /**\n * Generates a cryptographically secure random state parameter for OAuth flows.\n *\n * @returns A 32-character hexadecimal string\n *\n * @remarks\n * The state parameter is used to prevent CSRF attacks in OAuth flows.\n */\n public static generateRandomState(): string {\n return randomBytes(16).toString(\"hex\");\n }\n\n /**\n * Generates a cryptographically secure code verifier for PKCE flows.\n *\n * @returns A 64-character hexadecimal string (32 random bytes encoded as hex)\n *\n * @remarks\n * This method implements the code verifier generation as specified in RFC 7636 (PKCE).\n * \n * **PKCE (Proof Key for Code Exchange)** is a security extension to OAuth 2.0 that\n * prevents authorization code interception attacks. It's especially important for\n * public clients (mobile apps, SPAs) but is recommended for all OAuth flows.\n * \n * **Generation methodology:**\n * 1. Generate 32 cryptographically random bytes using Node.js crypto.randomBytes()\n * 2. Encode the bytes as a hexadecimal string (64 characters)\n * 3. The verifier is stored securely and used later in the token exchange\n * \n * **RFC 7636 Requirements:**\n * - Minimum length: 43 characters\n * - Maximum length: 128 characters\n * - Character set: [A-Z] / [a-z] / [0-9] / \"-\" / \".\" / \"_\" / \"~\"\n * - This implementation produces 64 hex characters, meeting the requirements\n * \n * The code verifier is:\n * - Generated when creating the authorization URL\n * - Stored in state cache with the state parameter\n * - Retrieved during callback handling\n * - Sent to the token endpoint to prove the client's identity\n * \n * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}\n * @see buildCodeChallenge for the corresponding challenge generation\n * \n * @internal\n */\n private static generateCodeVerifier(): string {\n return randomBytes(32).toString(\"hex\");\n }\n\n /**\n * Generates a code challenge from a code verifier for PKCE flows.\n *\n * @param codeVerifier - The code verifier string (64 hex characters)\n * @returns A base64url-encoded SHA-256 hash of the code verifier\n *\n * @remarks\n * This method implements the code challenge generation as specified in RFC 7636 (PKCE)\n * using the S256 (SHA-256) transformation method.\n * \n * **Challenge generation methodology:**\n * 1. Hash the code verifier using SHA-256\n * 2. Encode the hash as base64\n * 3. Convert to base64url format (RFC 4648):\n * - Replace '+' with '-'\n * - Replace '/' with '_'\n * - Remove trailing '=' padding\n * \n * **PKCE Flow:**\n * 1. Client generates code_verifier (random string)\n * 2. Client creates code_challenge = BASE64URL(SHA256(code_verifier))\n * 3. Client sends code_challenge to authorization endpoint\n * 4. Authorization server stores the code_challenge\n * 5. Client sends code_verifier to token endpoint\n * 6. Authorization server verifies: SHA256(code_verifier) == code_challenge\n * \n * **Security Benefits:**\n * - Prevents authorization code interception attacks\n * - Even if an attacker intercepts the authorization code, they cannot\n * exchange it for tokens without the original code_verifier\n * - The challenge is sent in the authorization request (public)\n * - The verifier is sent in the token request (should be kept secret)\n * \n * **RFC 7636 Transformation Methods:**\n * - plain: code_challenge = code_verifier (not recommended)\n * - S256: code_challenge = BASE64URL(SHA256(code_verifier)) (recommended, used here)\n * \n * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}\n * @see {@link https://datatracker.ietf.org/doc/html/rfc4648#section-5 | RFC 4648 - Base64url Encoding}\n * @see generateCodeVerifier for the verifier generation\n * \n * @internal\n */\n private static buildCodeChallenge(codeVerifier: string): string {\n const hash = crypto\n .createHash(\"sha256\")\n .update(codeVerifier)\n .digest(\"base64\");\n\n // Convert to base64url (RFC 4648 Section 5)\n return hash.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n }\n\n /**\n * Generates the authorization URL for the specified provider.\n *\n * @param provider - The provider name (must be a configured provider key)\n * @param state - The state parameter for CSRF protection\n * @returns The complete authorization URL to redirect users to\n *\n * @throws Error when the provider is not configured\n *\n * @example\n * ```typescript\n * const state = Lixa.generateRandomState();\n * const authUrl = lixa.getAuthUrl('google', state);\n * res.redirect(authUrl);\n * ```\n */\n public async getAuthUrl(provider: ConfiguredProviderKey<TConfig> | string, state?: string): Promise<string> {\n const providerType = String(provider).toLowerCase();\n \n this.log('INFO', 'Auth', `Generating authorization URL for provider: ${providerType}`);\n \n const providerConfig = this.findProviderByType(providerType);\n\n if (!providerConfig) {\n this.log('ERROR', 'Auth', `Provider '${String(provider)}' is not configured`);\n throw new ProviderNotConfiguredError(String(provider), {\n message: `Provider '${String(provider)}' is not configured in this Lixa instance`,\n });\n }\n\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n // Generate state and code verifier\n let stateValue: string;\n let codeVerifier: string;\n \n const generateStateFn = this.stateHandler.generateState || this.stateHandler.GenerateState;\n if (generateStateFn) {\n this.log('INFO', 'State', 'Calling custom generateState');\n const generated = await generateStateFn(providerType);\n stateValue = state || generated.state;\n codeVerifier = generated.data.codeVerifier;\n \n // Save the generated state data\n const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n await storage.saveState(stateValue, generated.data, 300);\n } else {\n this.log('INFO', 'State', 'Using default state generation');\n stateValue = state || randomBytes(16).toString(\"hex\");\n codeVerifier = randomBytes(32).toString(\"hex\");\n \n // Save state with default implementation\n const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n await storage.saveState(\n stateValue,\n {\n createdAt: Date.now(),\n provider: providerType,\n codeVerifier,\n },\n 300 // 5 minutes in seconds (synchronized with cookie TTL)\n );\n }\n\n const codeChallenge = Lixa.buildCodeChallenge(codeVerifier);\n\n this.log('INFO', 'State', `Saved state for provider: ${providerType}`, { state: stateValue });\n\n const authNScopes = this.resolveAuthNScopes(\n providerType,\n providerConfig.scopes,\n providerImpl,\n providerConfig.allowNonAuthScopes\n );\n\n const params = new URLSearchParams({\n client_id: providerConfig.clientId,\n redirect_uri: providerConfig.redirectUri,\n scope: authNScopes.join(\" \"),\n state: stateValue,\n response_type: \"code\",\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n ...providerConfig.extraConfig,\n });\n\n const authUrl = `${providerImpl.authorizationEndpoint}?${params.toString()}`;\n this.log('INFO', 'Auth', `Authorization URL generated successfully`, { \n provider: providerType,\n endpoint: providerImpl.authorizationEndpoint \n });\n\n return authUrl;\n }\n\n /**\n * Restricts primary authentication scopes strictly to AuthN identity scopes unless allowNonAuthScopes is true.\n */\n private resolveAuthNScopes(\n providerType: string,\n configuredScopes: string[],\n providerImpl: IProvider,\n allowNonAuthScopes?: boolean\n ): string[] {\n if (allowNonAuthScopes) {\n return configuredScopes && configuredScopes.length > 0\n ? configuredScopes\n : providerImpl.authScopes || [\"openid\", \"email\", \"profile\"];\n }\n\n const defaultAuthScopes: Record<string, string[]> = {\n google: [\"openid\", \"email\", \"profile\"],\n github: [\"read:user\", \"user:email\"],\n microsoft: [\"openid\", \"email\", \"profile\"],\n };\n\n const standardAuthNScopes = [\n \"openid\",\n \"email\",\n \"profile\",\n \"read:user\",\n \"user:email\",\n \"read:email\",\n \"user:profile\",\n \"user\",\n ];\n\n const allowedAuthNScopes = new Set<string>([\n ...standardAuthNScopes,\n ...(providerImpl.authScopes || []),\n ...(defaultAuthScopes[providerType] || []),\n ]);\n\n const validAuthNScopes = (configuredScopes || []).filter((scope) => allowedAuthNScopes.has(scope));\n const nonAuthNScopes = (configuredScopes || []).filter((scope) => !allowedAuthNScopes.has(scope));\n\n if (nonAuthNScopes.length > 0) {\n this.log(\n \"WARN\",\n \"Auth\",\n `Primary authentication is strictly limited to AuthN scopes. Excluded non-AuthN resource scopes: [${nonAuthNScopes.join(\n \", \"\n )}]. Set 'allowNonAuthScopes: true' on provider config to include them, or use lixa.getResourceAuthUrl() post-login to connect resource providers.`\n );\n }\n\n if (validAuthNScopes.length > 0) {\n return validAuthNScopes;\n }\n\n return providerImpl.authScopes || defaultAuthScopes[providerType] || [\"openid\", \"email\", \"profile\"];\n }\n\n /**\n * Handles the OAuth callback and creates a user session.\n *\n * @param provider - The provider name (must be a configured provider key)\n * @param code - The authorization code from the provider\n * @param state - The state parameter for validation\n * @returns A Promise that resolves to the session ID\n *\n * @throws Error when code or state is missing/invalid, or provider is not configured\n *\n * @example\n * ```typescript\n * const sessionId = await lixa.handleCallback({\n * provider: 'google',\n * code: req.query.code,\n * state: req.query.state\n * });\n * ```\n */\n public async handleCallback({\n provider,\n code,\n state,\n }: {\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n }): Promise<string> {\n const providerType = String(provider).toLowerCase();\n this.log('INFO', 'Auth', `Handling OAuth callback for provider: ${providerType}`);\n\n if (!code || code.trim() === \"\") {\n this.log('ERROR', 'Auth', 'Invalid or missing authorization code in callback');\n throw new InvalidOAuthCallbackError(\"Invalid or missing code in callback\");\n }\n\n if (!state || state.trim() === \"\") {\n this.log('ERROR', 'Auth', 'Invalid or missing state in callback');\n throw new InvalidOAuthCallbackError(\"Invalid or missing state in callback\");\n }\n\n this.log('INFO', 'State', 'Validating state parameter', { state });\n\n //Validate state here\n const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (!cachedState) {\n this.log('ERROR', 'State', 'State validation failed: state not found or expired', { state });\n throw new InvalidStateError(\"Invalid or expired state\", { state });\n }\n \n this.log('INFO', 'State', 'State validated successfully, removing from cache');\n // State is valid, remove it from cache to prevent reuse\n await stateStorage.deleteState(state);\n\n //Get code verifier from cached state\n const codeVerifier = cachedState.codeVerifier;\n\n const providerConfig = this.findProviderByType(providerType);\n\n if (!providerConfig) {\n this.log('ERROR', 'Auth', `Provider '${String(provider)}' is not configured`);\n throw new ProviderNotConfiguredError(String(provider), {\n message: `Provider '${String(provider)}' is not configured in this Lixa instance`,\n });\n }\n\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n this.log('INFO', 'Token', `Exchanging authorization code for tokens`, { provider: providerType });\n\n // Exchange code for tokens and fetch user info here.\n const tokens = await this.exchangeCodeForToken(\n code,\n providerConfig,\n providerImpl,\n codeVerifier\n );\n\n this.log('INFO', 'Token', 'Token exchange successful');\n this.log('INFO', 'Session', 'Generating user session');\n \n // Create provider metadata for session generation\n const providerMetadata: ProviderMetadata = {\n name: providerType,\n endpoints: {\n authorization: providerImpl.authorizationEndpoint,\n token: providerImpl.tokenEndpoint,\n userInfo: providerImpl.userInfoEndpoint\n }\n };\n\n // Extract user info if possible for account linking & session population\n let extractedUserInfo: UserInfo | undefined;\n try {\n const { userInfo } = await extractUserInfo(tokens, providerMetadata);\n extractedUserInfo = userInfo;\n } catch {\n // User info extraction might fail if scopes are limited or provider requires special handling\n }\n \n // Generate session data\n const generateSession =\n this.sessionHandler.generateSession ||\n this.sessionHandler.GenerateSession ||\n (this.localSessionHandler.generateSession ? this.localSessionHandler.generateSession.bind(this.localSessionHandler) : undefined);\n\n if (!generateSession) {\n throw new Error(\"No session generation handler available\");\n }\n \n if (this.sessionHandler.generateSession || this.sessionHandler.GenerateSession) {\n this.log('INFO', 'Session', 'Calling custom generateSession');\n } else {\n this.log('INFO', 'Session', 'Using default session generation');\n }\n \n const session = await generateSession(tokens, providerMetadata);\n session.provider = session.provider || providerType;\n if (extractedUserInfo?.email && !session.email) {\n session.email = extractedUserInfo.email;\n }\n\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n\n // Account Linking Mode: AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL\n const linkingConfig = this.config.accountLinking;\n const mode = String(linkingConfig?.mode || \"\");\n const isLinkByEmail =\n mode === AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL ||\n mode === \"AUTO_LINK_BY_VERIFIED_EMAIL\" ||\n mode === \"linkByEmail\";\n const email = extractedUserInfo?.email;\n const isVerified = extractedUserInfo?.email_verified !== false;\n const requireVerified = linkingConfig?.requireVerifiedEmail ?? true;\n const canLink = isLinkByEmail && email && (!requireVerified || isVerified);\n\n if (canLink && sessionStorage.getSessionByEmail) {\n const existingRecord = await sessionStorage.getSessionByEmail(email);\n if (existingRecord) {\n this.log('INFO', 'AccountLinking', `Linking provider '${providerType}' to existing session for email '${email}'`);\n const { sessionId: existingSessionId, session: existingSession } = existingRecord;\n \n existingSession.accounts = existingSession.accounts || {};\n existingSession.accounts[providerType] = {\n provider: providerType,\n providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,\n email,\n accessToken: tokens.access_token,\n raw: tokens,\n linkedAt: Date.now(),\n };\n existingSession.provider = providerType;\n existingSession.token = tokens.access_token;\n existingSession.raw = tokens;\n\n await sessionStorage.saveSession(existingSessionId, existingSession, 86400);\n return existingSessionId;\n }\n }\n\n // Default / Separate Account Mode\n session.accounts = session.accounts || {};\n session.accounts[providerType] = {\n provider: providerType,\n providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,\n email: extractedUserInfo?.email,\n accessToken: tokens.access_token,\n raw: tokens,\n linkedAt: Date.now(),\n };\n\n // Generate unique session ID\n const sessionId = randomBytes(32).toString(\"hex\");\n session.id = sessionId;\n\n this.log('INFO', 'Session', 'Storing session', { sessionId });\n await sessionStorage.saveSession(sessionId, session, 86400);\n\n this.log('INFO', 'Session', 'Session created successfully', { sessionId });\n\n return sessionId;\n }\n\n /**\n * Explicitly link a new OAuth provider account to an active session.\n * \n * @param params - Object containing sessionId, provider, code, and optional state\n * @returns The active session ID with the newly linked provider\n */\n public async linkAccount(params: {\n sessionId: string;\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n }): Promise<string> {\n const { sessionId, provider, code, state } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const existingSession = await sessionStorage.getSession(sessionId);\n\n if (!existingSession) {\n throw new SessionNotFoundError(\"Invalid session ID. User must be authenticated to link an account.\", { sessionId });\n }\n\n if (!code || code.trim() === \"\") {\n throw new InvalidOAuthCallbackError(\"Invalid or missing code in linkAccount\");\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(String(provider));\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n // Validate state and extract code verifier if state is provided\n let codeVerifier: string | undefined;\n if (state) {\n const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (!cachedState) {\n throw new InvalidStateError(\"Invalid or expired state during account linking\", { state });\n }\n codeVerifier = cachedState.codeVerifier;\n await stateStorage.deleteState(state);\n }\n\n // Exchange authorization code for tokens (Passing code correctly)\n const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier || \"\");\n\n const providerMetadata: ProviderMetadata = {\n name: providerType,\n endpoints: {\n authorization: providerImpl.authorizationEndpoint,\n token: providerImpl.tokenEndpoint,\n userInfo: providerImpl.userInfoEndpoint,\n },\n };\n\n let extractedUserInfo: UserInfo | undefined;\n try {\n const { userInfo } = await extractUserInfo(tokens, providerMetadata);\n extractedUserInfo = userInfo;\n } catch {\n // User info extraction optional\n }\n\n existingSession.accounts = existingSession.accounts || {};\n existingSession.accounts[providerType] = {\n provider: providerType,\n providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,\n email: extractedUserInfo?.email,\n accessToken: tokens.access_token,\n raw: tokens,\n linkedAt: Date.now(),\n };\n\n if (extractedUserInfo?.email && !existingSession.email) {\n existingSession.email = extractedUserInfo.email;\n }\n\n await sessionStorage.saveSession(sessionId, existingSession, 86400);\n return sessionId;\n }\n\n /**\n * Unlinks an OAuth provider account from an active session.\n * \n * @param sessionId - Active session ID\n * @param providerToUnlink - Provider name to unlink (e.g. 'github')\n * @returns Promise resolving to true on successful unlink\n */\n public async unlinkAccount(sessionId: string, providerToUnlink: string): Promise<boolean> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const session = await sessionStorage.getSession(sessionId);\n\n if (!session || !session.accounts) {\n throw new AccountUnlinkError(\"Session not found or has no linked accounts.\", { sessionId });\n }\n\n const linkedProviders = Object.keys(session.accounts);\n if (linkedProviders.length <= 1) {\n throw new AccountUnlinkError(\"Cannot unlink the only authentication provider for this account.\", {\n sessionId,\n provider: providerToUnlink,\n });\n }\n\n delete session.accounts[providerToUnlink.toLowerCase()];\n await sessionStorage.saveSession(sessionId, session, 86400);\n return true;\n }\n\n /**\n * Generates an authorization URL for connecting a resource provider (AuthZ) post-login.\n * \n * @remarks\n * Resource authorization is kept strictly separate from primary authentication (AuthN).\n * Call this method after a user is authenticated to request permissions for external API access\n * (e.g. GitHub repositories, Google Drive, Slack, etc.).\n * \n * @param params - Object containing sessionId, provider, requested resource scopes, and optional state\n * @returns The authorization URL for resource consent\n */\n public async getResourceAuthUrl(params: {\n sessionId: string;\n provider: ConfiguredProviderKey<TConfig> | string;\n scopes: string[];\n state?: string;\n prompt?: string;\n extraConfig?: Record<string, string>;\n }): Promise<string> {\n const { sessionId, provider, scopes, state, prompt, extraConfig } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n\n if (!activeSession) {\n throw new SessionNotFoundError(\"Authentication required. Active session must exist to connect resource providers.\", { sessionId });\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(String(provider));\n }\n\n const providerImpl = this.getProvider(providerType, providerConfig);\n const stateValue = state || randomBytes(16).toString(\"hex\");\n const codeVerifier = randomBytes(32).toString(\"hex\");\n\n const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n await storage.saveState(\n stateValue,\n {\n createdAt: Date.now(),\n provider: providerType,\n codeVerifier,\n },\n 300\n );\n\n const codeChallenge = Lixa.buildCodeChallenge(codeVerifier);\n\n const searchParams = new URLSearchParams({\n client_id: providerConfig.clientId,\n redirect_uri: providerConfig.redirectUri,\n scope: scopes.join(\" \"),\n state: stateValue,\n response_type: \"code\",\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n ...(prompt ? { prompt } : {}),\n ...providerConfig.extraConfig,\n ...extraConfig,\n });\n\n return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;\n }\n\n private getUserKeyFromSession(session: Session): string {\n return session.userId || session.email || session.id || \"anonymous\";\n }\n\n /**\n * Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.\n */\n public async handleResourceCallback(params: {\n sessionId: string;\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n scopes?: string[];\n }): Promise<Session> {\n const { sessionId, provider, code, state, scopes } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n\n if (!activeSession) {\n throw new SessionNotFoundError(\"Authentication required. Active session not found for resource connection.\", { sessionId });\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(String(provider));\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n let codeVerifier = randomBytes(32).toString(\"hex\");\n if (state) {\n const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (!cachedState) {\n throw new InvalidStateError(\"Invalid or expired state during resource connection callback\", { state });\n }\n codeVerifier = cachedState.codeVerifier;\n await stateStorage.deleteState(state);\n }\n\n const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);\n const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined;\n\n const connectedResource: ConnectedResource = {\n provider: providerType,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n expiresAt,\n scopes: scopes || (tokens.scope ? tokens.scope.split(\" \") : []),\n raw: tokens,\n connectedAt: Date.now(),\n };\n\n // Save to persistent ResourceStorage bound to User ID / Email\n const userKey = this.getUserKeyFromSession(activeSession);\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n await resourceStorage.saveResource(userKey, providerType, connectedResource);\n\n // Attach to active session for session context\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = connectedResource;\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n\n return activeSession;\n }\n\n /**\n * Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).\n * Automatically refreshes expired access tokens if a refresh token is present.\n * \n * @param userIdOrEmail - User identifier or email\n * @param provider - Resource provider identifier (e.g. 'github', 'google')\n */\n public async getUserResource(userIdOrEmail: string, provider: string): Promise<ConnectedResource | null> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n const providerType = provider.toLowerCase();\n const resource = await resourceStorage.getResource(userIdOrEmail, providerType);\n if (!resource) return null;\n\n // Auto-refresh token if expired (or expiring in < 60 seconds) and refreshToken is available\n if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 60000) {\n this.log('INFO', 'Token', `Resource access token for user '${userIdOrEmail}' on '${providerType}' is expired. Auto-refreshing...`);\n try {\n return await this.refreshUserResourceToken(userIdOrEmail, providerType);\n } catch (error) {\n this.log('ERROR', 'Token', `Failed to auto-refresh resource token for user '${userIdOrEmail}' on '${providerType}'`, { error: String(error) });\n }\n }\n\n return resource;\n }\n\n /**\n * Retrieves all connected resources for a specific User ID / Email.\n */\n public async getUserResources(userIdOrEmail: string): Promise<Record<string, ConnectedResource>> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n return await resourceStorage.getUserResources(userIdOrEmail);\n }\n\n /**\n * Retrieves a connected resource provider token for an active session, auto-refreshing expired tokens if possible.\n * \n * @param sessionId - Active session ID\n * @param provider - Provider identifier (e.g. 'github', 'google')\n */\n public async getConnectedResource(sessionId: string, provider: string): Promise<ConnectedResource | null> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (!activeSession) return null;\n\n const providerType = provider.toLowerCase();\n const userKey = this.getUserKeyFromSession(activeSession);\n \n // 1. Try to fetch from persistent ResourceStorage by user key\n let resource = await this.getUserResource(userKey, providerType);\n\n // 2. Fallback to active session resource cache if available\n if (!resource && activeSession.resources) {\n resource = activeSession.resources[providerType] || null;\n }\n\n if (resource) {\n // Auto-refresh token if expired (or expiring in < 60 seconds) and refreshToken is available\n if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 60000) {\n this.log('INFO', 'Token', `Resource access token for user '${userKey}' on '${providerType}' is expired. Auto-refreshing...`);\n try {\n resource = await this.refreshResourceToken(sessionId, providerType);\n } catch (error) {\n this.log('ERROR', 'Token', `Failed to auto-refresh resource token for user '${userKey}' on '${providerType}'`, { error: String(error) });\n }\n }\n\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = resource;\n }\n\n return resource;\n }\n\n /**\n * Refreshes a user's resource access token using its refresh token.\n * Deduplicates concurrent refresh requests via an in-flight promise mutex.\n * \n * @param userIdOrEmail - User identifier or email\n * @param provider - Provider identifier (e.g. 'google', 'github')\n */\n public async refreshUserResourceToken(\n userIdOrEmail: string,\n provider: string,\n existingResource?: ConnectedResource\n ): Promise<ConnectedResource> {\n const mutexKey = `${userIdOrEmail.toLowerCase()}:${provider.toLowerCase()}`;\n const existingPromise = this.refreshMutexes.get(mutexKey);\n if (existingPromise) {\n this.log('INFO', 'Token', `Concurrent refresh request detected for '${mutexKey}'. Reusing in-flight promise.`);\n return existingPromise;\n }\n\n const refreshPromise = this.executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource)\n .finally(() => {\n this.refreshMutexes.delete(mutexKey);\n });\n\n this.refreshMutexes.set(mutexKey, refreshPromise);\n return refreshPromise;\n }\n\n /**\n * Internal execution of refresh token exchange.\n */\n private async executeRefreshUserResourceToken(\n userIdOrEmail: string,\n provider: string,\n existingResource?: ConnectedResource\n ): Promise<ConnectedResource> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n const providerType = provider.toLowerCase();\n let resource = existingResource || (await resourceStorage.getResource(userIdOrEmail, providerType));\n\n if (!resource || !resource.refreshToken) {\n throw new RefreshTokenError(\n `No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`,\n { userId: userIdOrEmail, provider }\n );\n }\n\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(provider);\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n const body: Record<string, string> = {\n client_id: providerConfig.clientId,\n client_secret: providerConfig.clientSecret,\n grant_type: \"refresh_token\",\n refresh_token: resource.refreshToken,\n };\n\n const response = await fetch(providerImpl.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: new URLSearchParams(body).toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new RefreshTokenError(\n `Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`,\n { userId: userIdOrEmail, provider, status: response.status }\n );\n }\n\n const tokens: OAuthTokenResponse = await response.json();\n const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined;\n\n resource.accessToken = tokens.access_token;\n if (tokens.refresh_token) {\n resource.refreshToken = tokens.refresh_token;\n }\n if (expiresAt) {\n resource.expiresAt = expiresAt;\n }\n resource.raw = tokens;\n resource.connectedAt = Date.now();\n\n await resourceStorage.saveResource(userIdOrEmail, providerType, resource);\n return resource;\n }\n\n /**\n * Refreshes a connected resource access token for an active session.\n */\n public async refreshResourceToken(sessionId: string, provider: string): Promise<ConnectedResource> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (!activeSession) {\n throw new SessionNotFoundError(\"Active session not found.\", { sessionId });\n }\n const providerType = provider.toLowerCase();\n const userKey = this.getUserKeyFromSession(activeSession);\n const existingResource = activeSession.resources ? activeSession.resources[providerType] : undefined;\n\n const refreshed = await this.refreshUserResourceToken(userKey, providerType, existingResource);\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = refreshed;\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n return refreshed;\n }\n\n /**\n * Disconnects a resource provider for a specific User ID / Email.\n */\n public async disconnectUserResource(userIdOrEmail: string, provider: string): Promise<boolean> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n await resourceStorage.deleteResource(userIdOrEmail, provider.toLowerCase());\n return true;\n }\n\n /**\n * Disconnects a resource provider from an active session and user account.\n */\n public async disconnectResource(sessionId: string, provider: string): Promise<boolean> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (activeSession) {\n const userKey = this.getUserKeyFromSession(activeSession);\n await this.disconnectUserResource(userKey, provider);\n if (activeSession.resources) {\n delete activeSession.resources[provider.toLowerCase()];\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n }\n return true;\n }\n return false;\n }\n\n /**\n * Retrieves active session details from session storage.\n */\n public async fetchSessionInfo(sessionId: string): Promise<Session | null> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n return await sessionStorage.getSession<Session>(sessionId);\n }\n\n /**\n * Deletes a session from session storage (e.g. on logout).\n * \n * @param sessionId - Active session identifier\n */\n public async deleteSession(sessionId: string): Promise<void> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n await sessionStorage.deleteSession(sessionId);\n }\n\n private async exchangeCodeForToken(\n code: string,\n providerConfig: ProviderConfig,\n providerImpl: IProvider,\n codeVerifier: string\n ): Promise<OAuthTokenResponse> {\n // Build the request body\n const body: Record<string, string> = {\n client_id: providerConfig.clientId,\n client_secret: providerConfig.clientSecret,\n code,\n redirect_uri: providerConfig.redirectUri,\n grant_type: \"authorization_code\",\n };\n\n if (codeVerifier) {\n body.code_verifier = codeVerifier;\n }\n const params = new URLSearchParams(body);\n\n this.log('INFO', 'Token', 'Sending token exchange request', { \n endpoint: providerImpl.tokenEndpoint \n });\n \n const response = await fetch(providerImpl.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n this.log('ERROR', 'Token', 'Token exchange failed', { \n status: response.status, \n statusText: response.statusText,\n error: errorBody \n });\n throw new TokenExchangeError(\n `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`,\n response.status,\n { statusText: response.statusText, errorBody }\n );\n }\n\n this.log('INFO', 'Token', 'Token exchange response received successfully');\n return response.json();\n }\n\n private findProviderByType(providerType: string): ProviderConfig | undefined {\n // Use Object.entries to safely iterate and find the provider\n for (const [key, value] of Object.entries(this.config.providers)) {\n if (key.toLowerCase() === providerType.toLowerCase()) {\n return value;\n }\n }\n return undefined;\n }\n}\n\nexport { Lixa };\n","import { SessionHandler, SessionStorage, ResourceHandler, ResourceStorage, StateHandler, StateStorage, StateData } from \"./dao/types\";\nimport { Session, ProviderMetadata } from \"./models/session\";\nimport { IProvider } from \"./providers/IProvider\";\n\n// Re-export types for external use\nexport { Session, ConnectedResource } from \"./models/session\";\nexport { ProviderMetadata };\nexport { OAuthTokenResponse } from \"./models/session\";\nexport { IProvider };\nexport { StateHandler };\nexport { StateStorage };\nexport { SessionHandler };\nexport { SessionStorage };\nexport { ResourceHandler };\nexport { ResourceStorage };\nexport { StateData };\n\n/**\n * Configuration for an OAuth provider instance.\n * \n * @remarks\n * For built-in providers (google, github), just provide credentials.\n * For custom providers, include the provider implementation.\n * \n * The provider field uses a discriminated union to ensure type safety:\n * - When omitted or undefined: assumes a built-in provider\n * - When provided: must be a valid IProvider implementation\n * \n * @example\n * Built-in provider configuration:\n * ```typescript\n * {\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://app.com/callback',\n * scopes: ['openid', 'email']\n * }\n * ```\n * \n * @example\n * Custom provider configuration:\n * ```typescript\n * {\n * provider: new CustomProvider(),\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://app.com/callback',\n * scopes: ['read:user']\n * }\n * ```\n *\n * @public\n */\nexport type ProviderConfig = {\n /** The OAuth client ID provided by the provider */\n clientId: string;\n \n /** The OAuth client secret provided by the provider */\n clientSecret: string;\n \n /** The redirect URI registered with the provider */\n redirectUri: string;\n \n /** Array of OAuth scopes to request */\n scopes: string[];\n\n /** \n * Set to true to allow non-identity (resource) scopes during primary authentication flow.\n * By default (false), Lixa restricts primary AuthN scopes to identity scopes to maintain\n * clean AuthN vs AuthZ separation.\n */\n allowNonAuthScopes?: boolean;\n \n /** Additional provider-specific configuration parameters */\n extraConfig?: Record<string, string>;\n} & (\n | { provider?: never } // Built-in provider (no provider field needed)\n | { provider: IProvider } // Custom provider (provider field required)\n);\n\n/**\n * Main configuration object for Lixa.\n * Provides type-safe provider name inference.\n * \n * @remarks\n * The generic type parameter TProviders enables TypeScript to infer provider names\n * from the configuration object, providing autocomplete and type checking for\n * provider names in methods like getAuthUrl() and handleCallback().\n * \n * @typeParam TProviders - The provider configuration map type, defaults to a generic record\n * \n * @example\n * Basic configuration with built-in providers:\n * ```typescript\n * import { Lixa } from '@vunexa/lixa';\n * import { GoogleProvider } from '@vunexa/lixa-providers';\n * \n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: process.env.GOOGLE_CLIENT_ID!,\n * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n * redirectUri: 'https://app.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * }\n * });\n * ```\n * \n * @example\n * Configuration with custom session and state handlers:\n * ```typescript\n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: process.env.GOOGLE_CLIENT_ID!,\n * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n * redirectUri: 'https://app.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * },\n * stateHandler: {\n * storage: {\n * saveState: async (state, data, ttl) => await redis.setex(state, ttl, JSON.stringify(data)),\n * getState: async (state) => JSON.parse(await redis.get(state) || 'null'),\n * deleteState: async (state) => await redis.del(state)\n * }\n * },\n * sessionHandler: {\n * GenerateSession: async (tokenData, providerMetadata) => {\n * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);\n * const user = await db.users.upsert({ email: userInfo.email });\n * return { token: tokenData.access_token, raw: { ...tokenData, userId: user.id } };\n * },\n * storage: {\n * saveSession: async (id, session, ttl) => await db.sessions.create({ id, session, ttl }),\n * getSession: async (id) => await db.sessions.findOne({ id }),\n * deleteSession: async (id) => await db.sessions.delete({ id })\n * }\n * },\n * debug: true\n * });\n * ```\n *\n/**\n * Strategy mode for multi-SSO identity account linking.\n * \n * @public\n */\nexport enum AccountLinkingStrategy {\n /** Automatically merge identities matching the same verified primary email address */\n AUTO_LINK_BY_VERIFIED_EMAIL = \"AUTO_LINK_BY_VERIFIED_EMAIL\",\n\n /** Keep identity profiles isolated per provider (no automatic account merging) */\n ISOLATED = \"ISOLATED\",\n}\n\n/**\n * Supported mode values for account linking configuration.\n * \n * @public\n */\nexport type AccountLinkingMode =\n | AccountLinkingStrategy\n | \"AUTO_LINK_BY_VERIFIED_EMAIL\"\n | \"ISOLATED\"\n | \"linkByEmail\"\n | \"separate\";\n\n/**\n * Account linking settings for Lixa.\n * \n * @public\n */\nexport interface AccountLinkingConfig {\n /**\n * Account linking mode strategy:\n * - AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL (\"AUTO_LINK_BY_VERIFIED_EMAIL\" / \"linkByEmail\"): Auto-link accounts sharing same verified email.\n * - AccountLinkingStrategy.ISOLATED (\"ISOLATED\" / \"separate\"): Keep provider accounts isolated (default).\n * \n * @default AccountLinkingStrategy.ISOLATED\n */\n mode?: AccountLinkingMode;\n\n /**\n * Whether to require that the email address is verified by the provider before linking.\n * \n * @default true\n */\n requireVerifiedEmail?: boolean;\n}\n\n/**\n * Main configuration object for Lixa.\n * Provides type-safe provider name inference.\n * \n * @public\n */\nexport interface LixaConfig<TProviders extends Record<string, ProviderConfig> = Record<string, ProviderConfig>> {\n /** \n * Map of provider names to their configurations.\n * Provider names will be available for autocomplete in getAuthUrl() and handleCallback().\n */\n providers: TProviders;\n\n /**\n * Account linking configuration for multi-SSO user linking.\n */\n accountLinking?: AccountLinkingConfig;\n /** \n * Optional custom state handler.\n * Handles state generation and storage during OAuth authorization flow.\n * \n * - GenerateState: Customizes how state parameters and PKCE verifiers are generated\n * - storage: Provides persistent state storage (save/get/delete operations)\n * \n * Defaults to in-memory cache if not provided (not suitable for production).\n * \n * @see {@link StateHandler}\n */\n stateHandler?: StateHandler;\n \n /** \n * Optional custom session handler.\n * Handles session generation and storage after authentication.\n * \n * - GenerateSession: Customizes how OAuth tokens are converted into session data\n * - storage: Provides persistent session storage (save/get/delete operations)\n * \n * Defaults to in-memory cache if not provided (not suitable for production).\n * \n * @see {@link SessionHandler}\n */\n sessionHandler?: SessionHandler;\n\n /**\n * Optional custom resource handler.\n * Handles storage and management of long-lived third-party resource provider tokens (AuthZ)\n * bound directly to user accounts (User ID or Email), independent of transient session IDs.\n * \n * @see {@link ResourceHandler}\n */\n resourceHandler?: import(\"./dao/types\").ResourceHandler;\n \n /** \n * Enable debug logging.\n * When enabled, outputs structured logs for initialization, auth flow, and errors.\n * Format: [Lixa] [timestamp] [level] [context] message\n */\n debug?: boolean;\n\n /**\n * Optional custom structured logger implementation.\n * If provided, all Lixa logs will be routed through this logger.\n */\n logger?: LixaLogger;\n}\n\n/**\n * Log level for Lixa structured logging.\n * @public\n */\nexport type LogLevel = \"INFO\" | \"WARN\" | \"ERROR\" | \"DEBUG\";\n\n/**\n * Log context for Lixa structured logging.\n * @public\n */\nexport type LogContext = \"Init\" | \"Auth\" | \"Token\" | \"Session\" | \"State\" | \"AccountLinking\" | \"Resource\";\n\n/**\n * Custom logger interface for Lixa.\n * @public\n */\nexport interface LixaLogger {\n log(level: LogLevel, context: LogContext, message: string, data?: Record<string, unknown>): void;\n}\n\n/**\n * Helper type to create a configuration with only registered providers.\n * Use this with Lixa.createConfig() for type safety.\n * \n * @deprecated This type is maintained for backward compatibility.\n * The new inline provider configuration pattern makes this unnecessary.\n *\n * @public\n */\nexport type SafeLixaConfig<TProviders extends Record<string, ProviderConfig>> = LixaConfig<TProviders> & {\n providers: TProviders;\n};\n\n","import NodeCache from 'node-cache';\nimport { randomBytes } from 'crypto';\nimport { StateHandler, StateStorage, StateData } from \"./types\";\n\nclass LocalStateHandler implements StateHandler {\n private cache: NodeCache;\n public stateStorage: StateStorage;\n\n constructor(defaultTtlSeconds: number = 600) {\n this.cache = new NodeCache({ stdTTL: defaultTtlSeconds });\n \n // Provide storage implementation\n this.stateStorage = {\n saveState: async (state: string, data: StateData, expiresInSeconds: number): Promise<void> => {\n this.cache.set(state, data, expiresInSeconds);\n },\n\n getState: async (state: string): Promise<StateData | null> => {\n return this.cache.get<StateData>(state) || null;\n },\n\n deleteState: async (state: string): Promise<void> => {\n this.cache.del(state);\n }\n };\n }\n\n // Default generateState implementation\n async generateState(provider: string): Promise<{ state: string; data: StateData }> {\n const state = randomBytes(16).toString(\"hex\"); // 32 characters\n const codeVerifier = randomBytes(32).toString(\"hex\"); // 64 characters\n \n return {\n state,\n data: {\n provider,\n codeVerifier,\n createdAt: Date.now()\n }\n };\n }\n\n // PascalCase alias for backward compatibility\n async GenerateState(provider: string): Promise<{ state: string; data: StateData }> {\n return this.generateState(provider);\n }\n}\n\nexport { LocalStateHandler };\n\n","import NodeCache from 'node-cache';\nimport { SessionHandler, SessionStorage } from \"./types\";\nimport type { Session, OAuthTokenResponse, ProviderMetadata } from \"../models/session\";\n\nclass LocalSessionHandler implements SessionHandler {\n private cache: NodeCache;\n private emailToSessionMap: Map<string, string> = new Map();\n public sessionStorage: SessionStorage;\n\n constructor(defaultTtlSeconds: number = 600) {\n this.cache = new NodeCache({ stdTTL: defaultTtlSeconds, checkperiod: 60 });\n \n // Automatically purge email map entries when cache keys expire or are deleted to prevent memory leaks\n this.cache.on(\"expired\", (_key: string, value: any) => {\n if (value && typeof value === \"object\" && value.email) {\n this.emailToSessionMap.delete(String(value.email).toLowerCase());\n }\n });\n\n this.cache.on(\"del\", (_key: string, value: any) => {\n if (value && typeof value === \"object\" && value.email) {\n this.emailToSessionMap.delete(String(value.email).toLowerCase());\n }\n });\n\n this.cache.on(\"flush\", () => {\n this.emailToSessionMap.clear();\n });\n\n // Provide storage implementation\n this.sessionStorage = {\n saveSession: async <T extends Session>(sessionId: string, session: T, expiresInSeconds: number): Promise<void> => {\n this.cache.set(sessionId, session, expiresInSeconds);\n if (session.email) {\n this.emailToSessionMap.set(session.email.toLowerCase(), sessionId);\n }\n },\n\n getSession: async <T extends Session>(sessionId: string): Promise<T | null> => {\n return this.cache.get<T>(sessionId) || null;\n },\n\n deleteSession: async (sessionId: string): Promise<void> => {\n const session = this.cache.get<Session>(sessionId);\n if (session?.email) {\n this.emailToSessionMap.delete(session.email.toLowerCase());\n }\n this.cache.del(sessionId);\n },\n\n getSessionByEmail: async <T extends Session>(email: string): Promise<{ sessionId: string; session: T } | null> => {\n const normalizedEmail = email.toLowerCase();\n const sessionId = this.emailToSessionMap.get(normalizedEmail);\n if (!sessionId) return null;\n const session = this.cache.get<T>(sessionId);\n if (!session) {\n this.emailToSessionMap.delete(normalizedEmail);\n return null;\n }\n return { sessionId, session };\n }\n };\n }\n\n // Default generateSession implementation\n async generateSession<T extends Session>(\n tokenData: OAuthTokenResponse,\n _providerMetadata?: ProviderMetadata\n ): Promise<T> {\n if (!tokenData.access_token || typeof tokenData.access_token !== 'string') {\n throw new Error('No valid access token found in OAuth response');\n }\n\n const session: Session = {\n token: tokenData.access_token,\n raw: tokenData,\n };\n\n return session as T;\n }\n\n // PascalCase alias for backward compatibility\n async GenerateSession<T extends Session>(\n tokenData: OAuthTokenResponse,\n providerMetadata?: ProviderMetadata\n ): Promise<T> {\n return this.generateSession(tokenData, providerMetadata);\n }\n}\n\nexport { LocalSessionHandler };\n\n","import type { OAuthTokenResponse, ProviderMetadata } from \"../models/session\";\n\n/**\n * User information extracted from OAuth provider\n * \n * @public\n */\nexport interface UserInfo {\n email: string;\n id?: string | undefined;\n sub?: string | undefined;\n given_name?: string | undefined;\n family_name?: string | undefined;\n name?: string | undefined;\n picture?: string | undefined;\n email_verified?: boolean | undefined;\n iss?: string | undefined;\n}\n\n/**\n * Decode JWT ID token to extract user information\n * \n * @public\n */\nexport function decodeIdToken(idToken: string): UserInfo {\n const parts = idToken.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid ID token format: expected 3 parts separated by dots');\n }\n \n const base64Payload = parts[1];\n if (!base64Payload) {\n throw new Error('Invalid ID token: missing payload section');\n }\n const payload = Buffer.from(base64Payload, 'base64').toString();\n \n try {\n return JSON.parse(payload);\n } catch (error) {\n throw new Error('Invalid ID token: failed to parse payload JSON');\n }\n}\n\n/**\n * Determine OAuth provider from ID token issuer\n * \n * @public\n */\nexport function determineProviderFromIssuer(userInfo: UserInfo): string | null {\n if (!userInfo.iss) {\n return null;\n }\n \n const issuer = userInfo.iss.toLowerCase();\n \n if (issuer.includes('accounts.google.com')) {\n return 'google';\n }\n \n if (issuer.includes('github')) {\n return 'github';\n }\n \n // Unknown issuer\n return null;\n}\n\n/**\n * Fetch user info from OAuth provider's userinfo endpoint\n * \n * @param accessToken - OAuth access token\n * @param userInfoEndpoint - The provider's userinfo endpoint URL\n * @param providerName - Provider name for error messages (optional)\n * @returns User information from the provider\n * \n * @throws Error if the request fails or response is invalid\n * \n * @public\n */\nexport async function fetchUserInfo(\n accessToken: string, \n userInfoEndpoint: string\n): Promise<UserInfo> {\n const response = await fetch(userInfoEndpoint, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch user info: ${response.status} ${response.statusText}`);\n }\n \n const data = await response.json();\n if (!data || typeof data !== 'object' || !('email' in data) || typeof data.email !== 'string') {\n throw new Error(`Invalid user info response: missing or invalid email`);\n }\n \n return {\n email: data.email,\n id: 'id' in data ? String(data.id) : undefined,\n sub: 'sub' in data ? String(data.sub) : undefined,\n given_name: 'given_name' in data ? String(data.given_name) : undefined,\n family_name: 'family_name' in data ? String(data.family_name) : undefined,\n name: 'name' in data ? String(data.name) : undefined,\n picture: 'picture' in data ? String(data.picture) : undefined,\n email_verified: 'email_verified' in data ? Boolean(data.email_verified) : undefined,\n iss: 'iss' in data ? String(data.iss) : undefined,\n };\n}\n\n/**\n * Extract user info from OAuth token data\n * \n * @param tokenData - OAuth token response from provider\n * @param providerMetadata - Provider metadata containing endpoints configuration\n * @returns User info extracted from token or fetched from provider\n * \n * @remarks\n * This function attempts to extract user information in the following order:\n * 1. Decode ID token if present (preferred method for OIDC providers)\n * 2. Fetch from userinfo endpoint using access token (uses providerMetadata.endpoints.userInfo)\n * \n * The function automatically determines the best method based on available token data.\n * For OIDC providers (like Google), it decodes the JWT ID token.\n * For OAuth-only providers (like GitHub), it fetches from the userinfo endpoint.\n * \n * @throws Error if no ID token or access token is available\n * @throws Error if userinfo endpoint is required but not provided in providerMetadata\n * \n * @example\n * With ID token (OIDC provider like Google):\n * ```typescript\n * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);\n * console.log(`User ${userInfo.email} authenticated`);\n * ```\n * \n * @example\n * Without ID token (OAuth provider like GitHub):\n * ```typescript\n * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);\n * // Automatically fetches from providerMetadata.endpoints.userInfo\n * console.log(`User ${userInfo.email} authenticated`);\n * ```\n * \n * @public\n */\nexport async function extractUserInfo(\n tokenData: OAuthTokenResponse,\n providerMetadata: ProviderMetadata\n): Promise<{ userInfo: UserInfo }> {\n let userInfo: UserInfo;\n const userInfoEndpoint = providerMetadata.endpoints.userInfo;\n \n if (tokenData.id_token) {\n // Decode ID token to get user info\n userInfo = decodeIdToken(tokenData.id_token);\n } else if (tokenData.access_token) {\n // Fetch user info from provider's API\n userInfo = await fetchUserInfo(tokenData.access_token, userInfoEndpoint);\n } else {\n throw new Error('No ID token or access token available to fetch user info');\n }\n \n return { userInfo };\n}\n","/**\n * Base error class for all Lixa authentication and authorization errors.\n * \n * @public\n */\nexport class LixaError extends Error {\n /**\n * Standard error code string.\n */\n public readonly code: string;\n\n /**\n * Additional error context data.\n */\n public readonly details: Record<string, unknown> | undefined;\n\n constructor(message: string, code: string = \"LIXA_ERROR\", details?: Record<string, unknown>) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.details = details;\n\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when an OAuth state parameter is invalid, missing, or has expired.\n * \n * @public\n */\nexport class InvalidStateError extends LixaError {\n constructor(message: string = \"Invalid or expired state\", details?: Record<string, unknown>) {\n super(message, \"INVALID_STATE\", details);\n }\n}\n\n/**\n * Thrown when attempting to use a provider that is not configured in the Lixa instance.\n * \n * @public\n */\nexport class ProviderNotConfiguredError extends LixaError {\n constructor(provider: string, details?: Record<string, unknown>) {\n const message = details?.message && typeof details.message === \"string\"\n ? details.message\n : `Provider '${provider}' is not available. Either import it from '@vunexa/lixa-providers' and include it in the configuration, or provide a custom implementation using the 'provider' field: { provider: new CustomProvider(), clientId: '...', ... }`;\n super(message, \"PROVIDER_NOT_CONFIGURED\", {\n provider,\n ...details,\n });\n }\n}\n\n/**\n * Thrown when a provider configuration is invalid or missing required credentials.\n * \n * @public\n */\nexport class InvalidProviderConfigError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"INVALID_PROVIDER_CONFIG\", details);\n }\n}\n\n/**\n * Thrown when OAuth callback parameters (e.g. authorization code or state) are missing or malformed.\n * \n * @public\n */\nexport class InvalidOAuthCallbackError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"INVALID_OAUTH_CALLBACK\", details);\n }\n}\n\n/**\n * Thrown when exchanging an authorization code for OAuth tokens fails at the provider endpoint.\n * \n * @public\n */\nexport class TokenExchangeError extends LixaError {\n public readonly status: number | undefined;\n\n constructor(message: string, status?: number, details?: Record<string, unknown>) {\n super(message, \"TOKEN_EXCHANGE_FAILED\", { status, ...details });\n this.status = status;\n }\n}\n\n/**\n * Thrown when a user session is not found or has expired.\n * \n * @public\n */\nexport class SessionNotFoundError extends LixaError {\n constructor(message: string = \"Active session not found or has expired\", details?: Record<string, unknown>) {\n super(message, \"SESSION_NOT_FOUND\", details);\n }\n}\n\n/**\n * Thrown when account linking fails, e.g. when unverified email linking is rejected.\n * \n * @public\n */\nexport class EmailNotVerifiedError extends LixaError {\n constructor(email?: string, details?: Record<string, unknown>) {\n super(\n `Cannot link account: email '${email || \"unknown\"}' is not verified by the identity provider`,\n \"EMAIL_NOT_VERIFIED\",\n { email, ...details }\n );\n }\n}\n\n/**\n * Thrown when unlinking an account violates security constraints (e.g. unlinking the only login provider).\n * \n * @public\n */\nexport class AccountUnlinkError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"ACCOUNT_UNLINK_ERROR\", details);\n }\n}\n\n/**\n * Thrown when a refresh token is missing or token refresh fails for a connected resource.\n * \n * @public\n */\nexport class RefreshTokenError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"REFRESH_TOKEN_ERROR\", details);\n }\n}\n","/**\n * Sensible default cookie configuration options.\n * Follows RFC 6265bis and OAuth 2.0 security best practices.\n * \n * @public\n */\nexport interface CookieOptions {\n /**\n * Cookie name.\n * @default 'lixa_session'\n */\n name?: string | undefined;\n\n /**\n * Cookie path.\n * @default '/'\n */\n path?: string | undefined;\n\n /**\n * Maximum age of the cookie in seconds.\n */\n maxAge?: number | undefined;\n\n /**\n * Prevents client-side scripts from accessing the cookie (XSS protection).\n * @default true\n */\n httpOnly?: boolean | undefined;\n\n /**\n * Ensures the cookie is only transmitted over secure HTTPS connections.\n * @default false in development, true in production\n */\n secure?: boolean | undefined;\n\n /**\n * Controls whether the cookie is sent with cross-site requests (CSRF protection).\n * @default 'lax'\n */\n sameSite?: \"lax\" | \"strict\" | \"none\" | undefined;\n\n /**\n * Cookie domain.\n */\n domain?: string | undefined;\n}\n\n/**\n * Cookie payload containing name, value, options, and formatted header.\n * \n * @public\n */\nexport interface CookiePayload {\n name: string;\n value: string;\n options: CookieOptions;\n /**\n * Formatted `Set-Cookie` header value string.\n */\n header: string;\n}\n\n/**\n * Default session cookie name.\n * @public\n */\nexport const DEFAULT_SESSION_COOKIE_NAME = \"lixa_session\";\n\n/**\n * Default OAuth state cookie name.\n * @public\n */\nexport const DEFAULT_STATE_COOKIE_NAME = \"lixa_oauth_state\";\n\n/**\n * Default session max age in seconds (24 hours).\n * @public\n */\nexport const DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60; // 24 hours\n\n/**\n * Default OAuth state max age in seconds (5 minutes / 300 seconds).\n * @public\n */\nexport const DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60; // 5 minutes (synchronized with state storage TTL)\n\n/**\n * Checks if the runtime environment is production.\n * @public\n */\nexport function isProductionEnvironment(): boolean {\n return typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n}\n\n/**\n * Serializes a cookie name, value, and options into a standard `Set-Cookie` header string.\n * \n * @param name - Cookie name\n * @param value - Cookie value\n * @param options - Cookie attributes\n * @returns Formatted `Set-Cookie` string\n * \n * @public\n */\nexport function serializeCookie(name: string, value: string, options?: CookieOptions): string {\n const isProd = isProductionEnvironment();\n const path = options?.path ?? \"/\";\n const httpOnly = options?.httpOnly ?? true;\n const secure = options?.secure ?? isProd;\n const sameSite = options?.sameSite ?? \"lax\";\n\n const parts: string[] = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];\n\n if (path) {\n parts.push(`Path=${path}`);\n }\n\n if (typeof options?.maxAge === \"number\") {\n parts.push(`Max-Age=${Math.floor(options.maxAge)}`);\n // Also include Expires for legacy browser compatibility\n const expires = new Date(Date.now() + options.maxAge * 1000).toUTCString();\n parts.push(`Expires=${expires}`);\n }\n\n if (options?.domain) {\n parts.push(`Domain=${options.domain}`);\n }\n\n if (httpOnly) {\n parts.push(\"HttpOnly\");\n }\n\n if (secure) {\n parts.push(\"Secure\");\n }\n\n if (sameSite) {\n const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();\n parts.push(`SameSite=${capitalized}`);\n }\n\n return parts.join(\"; \");\n}\n\n/**\n * Generates a session cookie payload with secure default options.\n * \n * @param sessionId - The session identifier string\n * @param options - Optional overrides for cookie attributes\n * \n * @example\n * ```typescript\n * const cookie = createSessionCookie(sessionId);\n * res.setHeader(\"Set-Cookie\", cookie.header);\n * // or with Express:\n * res.cookie(cookie.name, cookie.value, cookie.options);\n * ```\n * \n * @public\n */\nexport function createSessionCookie(sessionId: string, options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_SESSION_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, sessionId, resolvedOptions);\n\n return {\n name,\n value: sessionId,\n options: resolvedOptions,\n header,\n };\n}\n\n/**\n * Generates an expired session cookie payload to clear the session on logout.\n * \n * @param options - Optional overrides for cookie name or attributes\n * \n * @example\n * ```typescript\n * const cookie = clearSessionCookie();\n * res.setHeader(\"Set-Cookie\", cookie.header);\n * // or with Express:\n * res.clearCookie(cookie.name, cookie.options);\n * ```\n * \n * @public\n */\nexport function clearSessionCookie(options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_SESSION_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: 0,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, \"\", resolvedOptions);\n\n return {\n name,\n value: \"\",\n options: resolvedOptions,\n header,\n };\n}\n\n/**\n * Generates an OAuth CSRF state cookie payload for in-flight authorization flows.\n * \n * @param state - The random state string\n * @param options - Optional overrides for cookie attributes\n * \n * @public\n */\nexport function createStateCookie(state: string, options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_STATE_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, state, resolvedOptions);\n\n return {\n name,\n value: state,\n options: resolvedOptions,\n header,\n };\n}\n\n/**\n * Generates an expired OAuth state cookie payload to clean up the state cookie after callback.\n * \n * @param options - Optional overrides for cookie attributes\n * \n * @public\n */\nexport function clearStateCookie(options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_STATE_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: 0,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, \"\", resolvedOptions);\n\n return {\n name,\n value: \"\",\n options: resolvedOptions,\n header,\n };\n}\n"],"mappings":"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","names":["import_crypto","AccountLinkingStrategy","NodeCache","import_crypto","import_node_cache","NodeCache","crypto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lixa.ts","../src/credentials/hasher.ts","../src/credentials/policy.ts","../src/credentials/local-storage.ts","../src/credentials/credentials-manager.ts","../src/errors.ts","../src/types.ts","../src/dao/state-cache.ts","../src/dao/session-cache.ts","../src/utils/user-info.ts","../src/utils/cookies.ts","../src/credentials/index.ts"],"sourcesContent":["/**\n * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library for backend applications.\n * \n * @remarks\n * This package simplifies multi-provider authentication flows (e.g., Google, GitHub), supports extensible session management, and enables custom provider registration.\n * \n * Key features:\n * - OAuth 2.0 authorization code flow with PKCE (RFC 6749, RFC 7636)\n * - OpenID Connect support\n * - Built-in providers available in \\@vunexa/lixa-extensions/providers\n * - Custom provider support via IProvider interface\n * - Extensible session management via SessionDao.CreateSession\n * - Pluggable state and session storage via StateDao and SessionDao\n * - TypeScript-first with comprehensive type safety\n * \n * @packageDocumentation\n */\n\nexport { Lixa } from \"./lixa\";\nexport {\n type ProviderConfig,\n type LixaConfig,\n type SafeLixaConfig,\n type IProvider,\n type Session,\n type ConnectedResource,\n type ProviderMetadata,\n type OAuthTokenResponse,\n type StateHandler,\n type StateStorage,\n type SessionHandler,\n type SessionStorage,\n type ResourceHandler,\n type ResourceStorage,\n type StateData,\n AccountLinkingStrategy,\n type AccountLinkingMode,\n type AccountLinkingConfig,\n type LogLevel,\n type LogContext,\n type LixaLogger,\n type UserCredentials,\n type CredentialsStorage,\n type IPasswordHasher,\n type PasswordPolicyConfig,\n type PasswordPolicyResult,\n type CredentialsConfig,\n type SignUpParams,\n type SignUpResult,\n type SignInParams,\n type SignInResult,\n type VerifyCredentialsParams,\n type ChangePasswordParams,\n ScryptPasswordHasher,\n type ScryptHasherOptions,\n Pbkdf2PasswordHasher,\n type Pbkdf2HasherOptions,\n LocalCredentialsStorage,\n CredentialsManager,\n validatePasswordPolicy,\n} from \"./types\";\nexport {\n type UserInfo,\n extractUserInfo,\n decodeIdToken,\n fetchUserInfo,\n determineProviderFromIssuer,\n} from \"./utils/user-info\";\nexport {\n LixaError,\n InvalidStateError,\n ProviderNotConfiguredError,\n InvalidProviderConfigError,\n InvalidOAuthCallbackError,\n TokenExchangeError,\n SessionNotFoundError,\n EmailNotVerifiedError,\n AccountUnlinkError,\n RefreshTokenError,\n InvalidCredentialsError,\n UserAlreadyExistsError,\n UserNotFoundError,\n WeakPasswordError,\n CredentialsNotConfiguredError,\n} from \"./errors\";\nexport {\n type CookieOptions,\n type CookiePayload,\n DEFAULT_SESSION_COOKIE_NAME,\n DEFAULT_STATE_COOKIE_NAME,\n DEFAULT_SESSION_MAX_AGE_SECONDS,\n DEFAULT_STATE_MAX_AGE_SECONDS,\n isProductionEnvironment,\n serializeCookie,\n createSessionCookie,\n clearSessionCookie,\n createStateCookie,\n clearStateCookie,\n} from \"./utils/cookies\";\nexport * as credentials from \"./credentials\";\n\n\n\n","import { randomBytes } from \"crypto\";\nimport {\n AccountLinkingStrategy,\n type LixaConfig,\n type ProviderConfig,\n type SignUpParams,\n type SignUpResult,\n type SignInParams,\n type SignInResult,\n type VerifyCredentialsParams,\n type ChangePasswordParams,\n type UserCredentials,\n type LogContext,\n} from \"./types\";\nimport { IProvider } from \"./providers\";\nimport { LocalStateHandler } from \"./dao/state-cache\";\nimport { SessionHandler, StateHandler, ResourceHandler, ResourceStorage } from \"./dao/types\";\nimport crypto from \"crypto\";\nimport { LocalSessionHandler } from \"./dao/session-cache\";\nimport {\n type Session,\n type ConnectedResource,\n type OAuthTokenResponse,\n type ProviderMetadata,\n type LinkedAccount,\n} from \"./models/session\";\nimport { CredentialsManager } from \"./credentials/credentials-manager\";\nimport { extractUserInfo, type UserInfo } from \"./utils/user-info\";\nimport {\n LixaError,\n InvalidStateError,\n ProviderNotConfiguredError,\n InvalidProviderConfigError,\n InvalidOAuthCallbackError,\n TokenExchangeError,\n SessionNotFoundError,\n EmailNotVerifiedError,\n AccountUnlinkError,\n RefreshTokenError,\n InvalidCredentialsError,\n UserAlreadyExistsError,\n UserNotFoundError,\n WeakPasswordError,\n CredentialsNotConfiguredError,\n} from \"./errors\";\n\n/**\n * Type representing the keys of configured providers\n */\ntype ConfiguredProviderKey<T extends LixaConfig<Record<string, ProviderConfig>>> = keyof T['providers'];\n\n/**\n * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library.\n *\n * @remarks\n * Lixa simplifies multi-provider authentication flows and supports extensible session management.\n * Providers can be passed inline in the configuration, eliminating the need for pre-registration.\n *\n * @example\n * Using built-in providers from \\@vunexa/lixa-providers:\n * ```typescript\n * import { Lixa } from '@vunexa/lixa';\n * import { GoogleProvider } from '@vunexa/lixa-providers';\n * \n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://yourapp.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * }\n * });\n * ```\n * \n * @example\n * Using custom inline providers:\n * ```typescript\n * import { Lixa, IProvider } from '@vunexa/lixa';\n * \n * const customProvider: IProvider = {\n * authorizationEndpoint: 'https://custom.com/oauth/authorize',\n * tokenEndpoint: 'https://custom.com/oauth/token',\n * userInfoEndpoint: 'https://custom.com/api/user'\n * };\n * \n * const lixa = new Lixa({\n * providers: {\n * custom: {\n * provider: customProvider,\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://yourapp.com/auth/custom/callback',\n * scopes: ['read:user']\n * }\n * }\n * });\n * ```\n *\n * @public\n */\nclass Lixa<TConfig extends LixaConfig<Record<string, ProviderConfig>> = LixaConfig> {\n private static DEFAULT_PROVIDERS: Map<string, IProvider> = new Map();\n private static CONFIGURED_PROVIDERS: Map<string, IProvider> = new Map(); // Legacy registry for backward compatibility\n\n // Instance-scoped fallback handlers to ensure no cross-instance state pollution\n private localStateHandler: StateHandler;\n private localSessionHandler: SessionHandler;\n private localResourceHandler: ResourceHandler;\n private userResourceStore: Map<string, Map<string, ConnectedResource>> = new Map();\n private refreshMutexes: Map<string, Promise<ConnectedResource>> = new Map();\n private credentialsManager?: CredentialsManager;\n\n private config: TConfig;\n private stateHandler: StateHandler;\n private sessionHandler: SessionHandler;\n private resourceHandler: ResourceHandler;\n private debug: boolean;\n\n /**\n * Creates a new Lixa instance with the provided configuration.\n * \n * @remarks\n * Providers can be passed inline in the configuration using the `provider` field.\n * Provider resolution priority: inline custom provider \\> default providers \\> legacy registry.\n * \n * @param config - The configuration object containing provider settings and optional session strategy\n * \n * @throws Error when provider configuration is missing required fields\n * @throws Error when provider implementation is missing required properties\n * @throws Error when provider is not available and no inline implementation is provided\n */\n constructor(config: TConfig) {\n this.config = config;\n this.debug = config.debug || false;\n\n // Initialize instance-isolated fallback handlers\n this.localStateHandler = new LocalStateHandler();\n this.localSessionHandler = new LocalSessionHandler();\n this.localResourceHandler = {\n resourceStorage: {\n saveResource: async (userId: string, provider: string, resource: ConnectedResource) => {\n let userMap = this.userResourceStore.get(userId);\n if (!userMap) {\n userMap = new Map();\n this.userResourceStore.set(userId, userMap);\n }\n userMap.set(provider.toLowerCase(), resource);\n },\n getResource: async (userId: string, provider: string) => {\n const userMap = this.userResourceStore.get(userId);\n return userMap?.get(provider.toLowerCase()) || null;\n },\n getUserResources: async (userId: string) => {\n const userMap = this.userResourceStore.get(userId);\n const result: Record<string, ConnectedResource> = {};\n if (userMap) {\n for (const [p, r] of userMap.entries()) {\n result[p] = r;\n }\n }\n return result;\n },\n deleteResource: async (userId: string, provider: string) => {\n const userMap = this.userResourceStore.get(userId);\n if (userMap) {\n userMap.delete(provider.toLowerCase());\n }\n },\n },\n };\n\n this.stateHandler = config.stateHandler || this.localStateHandler;\n this.sessionHandler = config.sessionHandler || this.localSessionHandler;\n this.resourceHandler = config.resourceHandler || this.localResourceHandler;\n\n // Initialize credentials manager if credentials config is provided\n if (config.credentials !== undefined && config.credentials.enabled !== false) {\n this.credentialsManager = new CredentialsManager(config.credentials);\n }\n \n this.log('INFO', 'Init', 'Initializing Lixa instance', { \n providers: config.providers ? Object.keys(config.providers) : [],\n credentialsEnabled: this.credentialsManager !== undefined,\n debug: this.debug \n });\n \n // Validate and extract providers from configuration\n if (config.providers) {\n for (const [providerName, providerConfig] of Object.entries(config.providers)) {\n const name = providerName.toLowerCase();\n const typedConfig: ProviderConfig = providerConfig;\n \n // Validate provider configuration has required credentials\n this.validateProviderConfig(providerName, typedConfig);\n \n // If provider config includes a custom provider implementation, validate it\n if (typedConfig.provider) {\n this.validateProviderImplementation(providerName, typedConfig.provider);\n this.log('INFO', 'Init', `Registered inline provider: ${providerName}`);\n } else {\n // Check if it's available in default providers or legacy registry\n if (!Lixa.DEFAULT_PROVIDERS.has(name) && !Lixa.CONFIGURED_PROVIDERS.has(name)) {\n this.log('ERROR', 'Init', `Provider '${providerName}' not available`);\n throw new ProviderNotConfiguredError(\n providerName,\n { hint: `Ensure the provider is registered via '@vunexa/lixa-providers' or provided inline.` }\n );\n }\n this.log('INFO', 'Init', `Using registered provider: ${providerName}`);\n }\n }\n }\n \n this.log('INFO', 'Init', 'Lixa instance initialized successfully');\n }\n \n /**\n * Validates that a provider configuration has all required credentials.\n * \n * @param name - The provider name\n * @param config - The provider configuration\n * @throws InvalidProviderConfigError when required fields are missing or invalid\n */\n private validateProviderConfig(name: string, config: ProviderConfig): void {\n const requiredFields: (keyof ProviderConfig)[] = ['clientId', 'clientSecret', 'redirectUri', 'scopes'];\n const missingFields = requiredFields.filter(field => {\n const value = config[field];\n return value === undefined || value === null || (typeof value === 'string' && value.trim() === '');\n });\n \n if (missingFields.length > 0) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' configuration is missing required fields: ${missingFields.join(', ')}`,\n { provider: name, missingFields }\n );\n }\n \n // Validate scopes is an array\n if (!Array.isArray(config.scopes)) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' configuration error: 'scopes' must be an array of strings`,\n { provider: name }\n );\n }\n \n if (config.scopes.length === 0) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' configuration error: 'scopes' array cannot be empty`,\n { provider: name }\n );\n }\n }\n \n /**\n * Validates that a provider implementation has all required properties.\n * \n * @param name - The provider name\n * @param provider - The provider implementation\n * @throws InvalidProviderConfigError when required properties are missing\n */\n private validateProviderImplementation(name: string, provider: IProvider): void {\n const requiredProps: (keyof IProvider)[] = ['authorizationEndpoint', 'tokenEndpoint', 'userInfoEndpoint'];\n const missingProps = requiredProps.filter(prop => {\n const value = provider[prop];\n return !value || typeof value !== 'string' || value.trim() === '';\n });\n \n if (missingProps.length > 0) {\n throw new InvalidProviderConfigError(\n `Provider '${name}' implementation is missing required properties: ${missingProps.join(', ')}. ` +\n `All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`,\n { provider: name, missingProps }\n );\n }\n }\n\n /**\n * Structured logging with standardized format and custom logger support.\n * \n * @param level - Log level (INFO, WARN, ERROR, DEBUG)\n * @param context - Context of the log (Init, Auth, Token, Session, State, AccountLinking, Resource, Credentials)\n * @param message - Log message\n * @param data - Optional data to log\n */\n private log(\n level: 'INFO' | 'WARN' | 'ERROR' | 'DEBUG',\n context: LogContext,\n message: string,\n data?: Record<string, unknown>\n ): void {\n if (this.config.logger) {\n try {\n this.config.logger.log(level, context, message, data);\n } catch {\n // Ignore custom logger exceptions\n }\n }\n\n if (!this.debug) return;\n \n const timestamp = new Date().toISOString();\n const prefix = `[Lixa] [${timestamp}] [${level}] [${context}]`;\n \n if (data !== undefined) {\n console.log(`${prefix} ${message}`, data);\n } else {\n console.log(`${prefix} ${message}`);\n }\n }\n\n /**\n * Checks if a provider is configured for this instance.\n * This is a type guard that narrows the provider type for use with getAuthUrl.\n *\n * @param provider - The provider name to check (case-insensitive)\n * @returns True if the provider is configured, false otherwise\n *\n * @example\n * ```typescript\n * if (lixa.isProviderConfigured(provider)) {\n * // TypeScript now knows provider is a valid ConfiguredProviderKey\n * const authUrl = lixa.getAuthUrl(provider, state);\n * }\n * ```\n */\n public isProviderConfigured<T extends string>(provider: T): provider is T & ConfiguredProviderKey<TConfig> {\n const providerType = provider.toLowerCase();\n return Boolean(this.config.providers && this.config.providers.hasOwnProperty(providerType));\n }\n \n /**\n * Gets a provider implementation by name.\n * Resolution priority: inline custom provider \\> default providers \\> legacy registry\n * \n * @param name - The provider name (case-insensitive)\n * @param config - The provider configuration\n * @returns The provider implementation\n * @throws Error when provider is not found\n */\n private getProvider(name: string, config: ProviderConfig): IProvider {\n // First check if provider is inline in config\n if (config.provider) {\n return config.provider;\n }\n \n // Then check default providers\n const lowerName = name.toLowerCase();\n const defaultProvider = Lixa.DEFAULT_PROVIDERS.get(lowerName);\n if (defaultProvider) {\n return defaultProvider;\n }\n \n // Finally check legacy registry for backward compatibility\n const legacyProvider = Lixa.CONFIGURED_PROVIDERS.get(lowerName);\n if (legacyProvider) {\n return legacyProvider;\n }\n \n throw new ProviderNotConfiguredError(\n name,\n { hint: `Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().` }\n );\n }\n\n /**\n * Registers custom OAuth providers for use with Lixa.\n * \n * @deprecated This method is maintained for backward compatibility.\n * The recommended approach is to pass providers inline in the configuration:\n * ```typescript\n * const lixa = new Lixa({\n * providers: {\n * custom: {\n * provider: new CustomProvider(),\n * clientId: '...',\n * // ...\n * }\n * }\n * });\n * ```\n *\n * @param providerMap - A map of provider names to IProvider implementations\n *\n * @example\n * Legacy usage (still supported):\n * ```typescript\n * class CustomProvider implements IProvider {\n * authorizationEndpoint = 'https://custom.com/oauth/authorize';\n * tokenEndpoint = 'https://custom.com/oauth/token';\n * userInfoEndpoint = 'https://custom.com/api/user';\n * }\n *\n * Lixa.registerProvider({ custom: new CustomProvider() });\n * ```\n */\n public static registerProvider<T extends Record<string, IProvider>>(providerMap: T): void {\n Object.entries(providerMap).forEach(([key, providerImpl]) => {\n Lixa.CONFIGURED_PROVIDERS.set(key.toLowerCase(), providerImpl);\n });\n }\n\n /**\n * Gets the list of registered provider names.\n * \n * @returns Array of registered provider names\n */\n public static getRegisteredProviders(): string[] {\n return Array.from(Lixa.CONFIGURED_PROVIDERS.keys());\n }\n\n /**\n * Creates a type-safe configuration.\n * \n * @deprecated This method is maintained for backward compatibility.\n * You can now pass configuration directly to the Lixa constructor without this helper.\n * \n * @param config - Configuration object with provider settings\n * @returns The same configuration object with type safety\n * \n * @example\n * New approach (recommended):\n * ```typescript\n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: '...',\n * // ...\n * }\n * }\n * });\n * ```\n */\n public static createConfig<T extends Record<string, ProviderConfig>>(\n config: LixaConfig<T> & { providers: T }\n ): LixaConfig<T> & { providers: T } {\n return config;\n }\n\n /**\n * Generates a cryptographically secure random state parameter for OAuth flows.\n *\n * @returns A 32-character hexadecimal string\n *\n * @remarks\n * The state parameter is used to prevent CSRF attacks in OAuth flows.\n */\n public static generateRandomState(): string {\n return randomBytes(16).toString(\"hex\");\n }\n\n /**\n * Generates a cryptographically secure code verifier for PKCE flows.\n *\n * @returns A 64-character hexadecimal string (32 random bytes encoded as hex)\n *\n * @remarks\n * This method implements the code verifier generation as specified in RFC 7636 (PKCE).\n * \n * **PKCE (Proof Key for Code Exchange)** is a security extension to OAuth 2.0 that\n * prevents authorization code interception attacks. It's especially important for\n * public clients (mobile apps, SPAs) but is recommended for all OAuth flows.\n * \n * **Generation methodology:**\n * 1. Generate 32 cryptographically random bytes using Node.js crypto.randomBytes()\n * 2. Encode the bytes as a hexadecimal string (64 characters)\n * 3. The verifier is stored securely and used later in the token exchange\n * \n * **RFC 7636 Requirements:**\n * - Minimum length: 43 characters\n * - Maximum length: 128 characters\n * - Character set: [A-Z] / [a-z] / [0-9] / \"-\" / \".\" / \"_\" / \"~\"\n * - This implementation produces 64 hex characters, meeting the requirements\n * \n * The code verifier is:\n * - Generated when creating the authorization URL\n * - Stored in state cache with the state parameter\n * - Retrieved during callback handling\n * - Sent to the token endpoint to prove the client's identity\n * \n * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}\n * @see buildCodeChallenge for the corresponding challenge generation\n * \n * @internal\n */\n private static generateCodeVerifier(): string {\n return randomBytes(32).toString(\"hex\");\n }\n\n /**\n * Generates a code challenge from a code verifier for PKCE flows.\n *\n * @param codeVerifier - The code verifier string (64 hex characters)\n * @returns A base64url-encoded SHA-256 hash of the code verifier\n *\n * @remarks\n * This method implements the code challenge generation as specified in RFC 7636 (PKCE)\n * using the S256 (SHA-256) transformation method.\n * \n * **Challenge generation methodology:**\n * 1. Hash the code verifier using SHA-256\n * 2. Encode the hash as base64\n * 3. Convert to base64url format (RFC 4648):\n * - Replace '+' with '-'\n * - Replace '/' with '_'\n * - Remove trailing '=' padding\n * \n * **PKCE Flow:**\n * 1. Client generates code_verifier (random string)\n * 2. Client creates code_challenge = BASE64URL(SHA256(code_verifier))\n * 3. Client sends code_challenge to authorization endpoint\n * 4. Authorization server stores the code_challenge\n * 5. Client sends code_verifier to token endpoint\n * 6. Authorization server verifies: SHA256(code_verifier) == code_challenge\n * \n * **Security Benefits:**\n * - Prevents authorization code interception attacks\n * - Even if an attacker intercepts the authorization code, they cannot\n * exchange it for tokens without the original code_verifier\n * - The challenge is sent in the authorization request (public)\n * - The verifier is sent in the token request (should be kept secret)\n * \n * **RFC 7636 Transformation Methods:**\n * - plain: code_challenge = code_verifier (not recommended)\n * - S256: code_challenge = BASE64URL(SHA256(code_verifier)) (recommended, used here)\n * \n * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}\n * @see {@link https://datatracker.ietf.org/doc/html/rfc4648#section-5 | RFC 4648 - Base64url Encoding}\n * @see generateCodeVerifier for the verifier generation\n * \n * @internal\n */\n private static buildCodeChallenge(codeVerifier: string): string {\n const hash = crypto\n .createHash(\"sha256\")\n .update(codeVerifier)\n .digest(\"base64\");\n\n // Convert to base64url (RFC 4648 Section 5)\n return hash.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n }\n\n /**\n * Generates the authorization URL for the specified provider.\n *\n * @param provider - The provider name (must be a configured provider key)\n * @param state - The state parameter for CSRF protection\n * @returns The complete authorization URL to redirect users to\n *\n * @throws Error when the provider is not configured\n *\n * @example\n * ```typescript\n * const state = Lixa.generateRandomState();\n * const authUrl = lixa.getAuthUrl('google', state);\n * res.redirect(authUrl);\n * ```\n */\n public async getAuthUrl(provider: ConfiguredProviderKey<TConfig> | string, state?: string): Promise<string> {\n const providerType = String(provider).toLowerCase();\n \n this.log('INFO', 'Auth', `Generating authorization URL for provider: ${providerType}`);\n \n const providerConfig = this.findProviderByType(providerType);\n\n if (!providerConfig) {\n this.log('ERROR', 'Auth', `Provider '${String(provider)}' is not configured`);\n throw new ProviderNotConfiguredError(String(provider), {\n message: `Provider '${String(provider)}' is not configured in this Lixa instance`,\n });\n }\n\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n // Generate state and code verifier\n let stateValue: string;\n let codeVerifier: string;\n \n const generateStateFn = this.stateHandler.generateState || this.stateHandler.GenerateState;\n if (generateStateFn) {\n this.log('INFO', 'State', 'Calling custom generateState');\n const generated = await generateStateFn(providerType);\n stateValue = state || generated.state;\n codeVerifier = generated.data.codeVerifier;\n \n // Save the generated state data\n const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n await storage.saveState(stateValue, generated.data, 300);\n } else {\n this.log('INFO', 'State', 'Using default state generation');\n stateValue = state || randomBytes(16).toString(\"hex\");\n codeVerifier = randomBytes(32).toString(\"hex\");\n \n // Save state with default implementation\n const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n await storage.saveState(\n stateValue,\n {\n createdAt: Date.now(),\n provider: providerType,\n codeVerifier,\n },\n 300 // 5 minutes in seconds (synchronized with cookie TTL)\n );\n }\n\n const codeChallenge = Lixa.buildCodeChallenge(codeVerifier);\n\n this.log('INFO', 'State', `Saved state for provider: ${providerType}`, { state: stateValue });\n\n const authNScopes = this.resolveAuthNScopes(\n providerType,\n providerConfig.scopes,\n providerImpl,\n providerConfig.allowNonAuthScopes\n );\n\n const params = new URLSearchParams({\n client_id: providerConfig.clientId,\n redirect_uri: providerConfig.redirectUri,\n scope: authNScopes.join(\" \"),\n state: stateValue,\n response_type: \"code\",\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n ...providerConfig.extraConfig,\n });\n\n const authUrl = `${providerImpl.authorizationEndpoint}?${params.toString()}`;\n this.log('INFO', 'Auth', `Authorization URL generated successfully`, { \n provider: providerType,\n endpoint: providerImpl.authorizationEndpoint \n });\n\n return authUrl;\n }\n\n /**\n * Restricts primary authentication scopes strictly to AuthN identity scopes unless allowNonAuthScopes is true.\n */\n private resolveAuthNScopes(\n providerType: string,\n configuredScopes: string[],\n providerImpl: IProvider,\n allowNonAuthScopes?: boolean\n ): string[] {\n if (allowNonAuthScopes) {\n return configuredScopes && configuredScopes.length > 0\n ? configuredScopes\n : providerImpl.authScopes || [\"openid\", \"email\", \"profile\"];\n }\n\n const defaultAuthScopes: Record<string, string[]> = {\n google: [\"openid\", \"email\", \"profile\"],\n github: [\"read:user\", \"user:email\"],\n microsoft: [\"openid\", \"email\", \"profile\"],\n };\n\n const standardAuthNScopes = [\n \"openid\",\n \"email\",\n \"profile\",\n \"read:user\",\n \"user:email\",\n \"read:email\",\n \"user:profile\",\n \"user\",\n ];\n\n const allowedAuthNScopes = new Set<string>([\n ...standardAuthNScopes,\n ...(providerImpl.authScopes || []),\n ...(defaultAuthScopes[providerType] || []),\n ]);\n\n const validAuthNScopes = (configuredScopes || []).filter((scope) => allowedAuthNScopes.has(scope));\n const nonAuthNScopes = (configuredScopes || []).filter((scope) => !allowedAuthNScopes.has(scope));\n\n if (nonAuthNScopes.length > 0) {\n this.log(\n \"WARN\",\n \"Auth\",\n `Primary authentication is strictly limited to AuthN scopes. Excluded non-AuthN resource scopes: [${nonAuthNScopes.join(\n \", \"\n )}]. Set 'allowNonAuthScopes: true' on provider config to include them, or use lixa.getResourceAuthUrl() post-login to connect resource providers.`\n );\n }\n\n if (validAuthNScopes.length > 0) {\n return validAuthNScopes;\n }\n\n return providerImpl.authScopes || defaultAuthScopes[providerType] || [\"openid\", \"email\", \"profile\"];\n }\n\n /**\n * Handles the OAuth callback and creates a user session.\n *\n * @param provider - The provider name (must be a configured provider key)\n * @param code - The authorization code from the provider\n * @param state - The state parameter for validation\n * @returns A Promise that resolves to the session ID\n *\n * @throws Error when code or state is missing/invalid, or provider is not configured\n *\n * @example\n * ```typescript\n * const sessionId = await lixa.handleCallback({\n * provider: 'google',\n * code: req.query.code,\n * state: req.query.state\n * });\n * ```\n */\n public async handleCallback({\n provider,\n code,\n state,\n }: {\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n }): Promise<string> {\n const providerType = String(provider).toLowerCase();\n this.log('INFO', 'Auth', `Handling OAuth callback for provider: ${providerType}`);\n\n if (!code || code.trim() === \"\") {\n this.log('ERROR', 'Auth', 'Invalid or missing authorization code in callback');\n throw new InvalidOAuthCallbackError(\"Invalid or missing code in callback\");\n }\n\n if (!state || state.trim() === \"\") {\n this.log('ERROR', 'Auth', 'Invalid or missing state in callback');\n throw new InvalidOAuthCallbackError(\"Invalid or missing state in callback\");\n }\n\n this.log('INFO', 'State', 'Validating state parameter', { state });\n\n //Validate state here\n const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (!cachedState) {\n this.log('ERROR', 'State', 'State validation failed: state not found or expired', { state });\n throw new InvalidStateError(\"Invalid or expired state\", { state });\n }\n \n this.log('INFO', 'State', 'State validated successfully, removing from cache');\n // State is valid, remove it from cache to prevent reuse\n await stateStorage.deleteState(state);\n\n //Get code verifier from cached state\n const codeVerifier = cachedState.codeVerifier;\n\n const providerConfig = this.findProviderByType(providerType);\n\n if (!providerConfig) {\n this.log('ERROR', 'Auth', `Provider '${String(provider)}' is not configured`);\n throw new ProviderNotConfiguredError(String(provider), {\n message: `Provider '${String(provider)}' is not configured in this Lixa instance`,\n });\n }\n\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n this.log('INFO', 'Token', `Exchanging authorization code for tokens`, { provider: providerType });\n\n // Exchange code for tokens and fetch user info here.\n const tokens = await this.exchangeCodeForToken(\n code,\n providerConfig,\n providerImpl,\n codeVerifier\n );\n\n this.log('INFO', 'Token', 'Token exchange successful');\n this.log('INFO', 'Session', 'Generating user session');\n \n // Create provider metadata for session generation\n const providerMetadata: ProviderMetadata = {\n name: providerType,\n endpoints: {\n authorization: providerImpl.authorizationEndpoint,\n token: providerImpl.tokenEndpoint,\n userInfo: providerImpl.userInfoEndpoint\n }\n };\n\n // Extract user info if possible for account linking & session population\n let extractedUserInfo: UserInfo | undefined;\n try {\n const { userInfo } = await extractUserInfo(tokens, providerMetadata);\n extractedUserInfo = userInfo;\n } catch {\n // User info extraction might fail if scopes are limited or provider requires special handling\n }\n \n // Generate session data\n const generateSession =\n this.sessionHandler.generateSession ||\n this.sessionHandler.GenerateSession ||\n (this.localSessionHandler.generateSession ? this.localSessionHandler.generateSession.bind(this.localSessionHandler) : undefined);\n\n if (!generateSession) {\n throw new Error(\"No session generation handler available\");\n }\n \n if (this.sessionHandler.generateSession || this.sessionHandler.GenerateSession) {\n this.log('INFO', 'Session', 'Calling custom generateSession');\n } else {\n this.log('INFO', 'Session', 'Using default session generation');\n }\n \n const session = await generateSession(tokens, providerMetadata);\n session.provider = session.provider || providerType;\n if (extractedUserInfo?.email && !session.email) {\n session.email = extractedUserInfo.email;\n }\n\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n\n // Account Linking Mode: AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL\n const linkingConfig = this.config.accountLinking;\n const mode = String(linkingConfig?.mode || \"\");\n const isLinkByEmail =\n mode === AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL ||\n mode === \"AUTO_LINK_BY_VERIFIED_EMAIL\" ||\n mode === \"linkByEmail\";\n const email = extractedUserInfo?.email;\n const isVerified = extractedUserInfo?.email_verified !== false;\n const requireVerified = linkingConfig?.requireVerifiedEmail ?? true;\n const canLink = isLinkByEmail && email && (!requireVerified || isVerified);\n\n if (canLink && sessionStorage.getSessionByEmail) {\n const existingRecord = await sessionStorage.getSessionByEmail(email);\n if (existingRecord) {\n this.log('INFO', 'AccountLinking', `Linking provider '${providerType}' to existing session for email '${email}'`);\n const { sessionId: existingSessionId, session: existingSession } = existingRecord;\n \n existingSession.accounts = existingSession.accounts || {};\n existingSession.accounts[providerType] = {\n provider: providerType,\n providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,\n email,\n accessToken: tokens.access_token,\n raw: tokens,\n linkedAt: Date.now(),\n };\n existingSession.provider = providerType;\n existingSession.token = tokens.access_token;\n existingSession.raw = tokens;\n\n await sessionStorage.saveSession(existingSessionId, existingSession, 86400);\n return existingSessionId;\n }\n }\n\n // Default / Separate Account Mode\n session.accounts = session.accounts || {};\n session.accounts[providerType] = {\n provider: providerType,\n providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,\n email: extractedUserInfo?.email,\n accessToken: tokens.access_token,\n raw: tokens,\n linkedAt: Date.now(),\n };\n\n // Generate unique session ID\n const sessionId = randomBytes(32).toString(\"hex\");\n session.id = sessionId;\n\n this.log('INFO', 'Session', 'Storing session', { sessionId });\n await sessionStorage.saveSession(sessionId, session, 86400);\n\n this.log('INFO', 'Session', 'Session created successfully', { sessionId });\n\n return sessionId;\n }\n\n /**\n * Explicitly link a new OAuth provider account to an active session.\n * \n * @param params - Object containing sessionId, provider, code, and optional state\n * @returns The active session ID with the newly linked provider\n */\n public async linkAccount(params: {\n sessionId: string;\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n }): Promise<string> {\n const { sessionId, provider, code, state } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const existingSession = await sessionStorage.getSession(sessionId);\n\n if (!existingSession) {\n throw new SessionNotFoundError(\"Invalid session ID. User must be authenticated to link an account.\", { sessionId });\n }\n\n if (!code || code.trim() === \"\") {\n throw new InvalidOAuthCallbackError(\"Invalid or missing code in linkAccount\");\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(String(provider));\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n // Validate state and extract code verifier if state is provided\n let codeVerifier: string | undefined;\n if (state) {\n const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (!cachedState) {\n throw new InvalidStateError(\"Invalid or expired state during account linking\", { state });\n }\n codeVerifier = cachedState.codeVerifier;\n await stateStorage.deleteState(state);\n }\n\n // Exchange authorization code for tokens (Passing code correctly)\n const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier || \"\");\n\n const providerMetadata: ProviderMetadata = {\n name: providerType,\n endpoints: {\n authorization: providerImpl.authorizationEndpoint,\n token: providerImpl.tokenEndpoint,\n userInfo: providerImpl.userInfoEndpoint,\n },\n };\n\n let extractedUserInfo: UserInfo | undefined;\n try {\n const { userInfo } = await extractUserInfo(tokens, providerMetadata);\n extractedUserInfo = userInfo;\n } catch {\n // User info extraction optional\n }\n\n existingSession.accounts = existingSession.accounts || {};\n existingSession.accounts[providerType] = {\n provider: providerType,\n providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,\n email: extractedUserInfo?.email,\n accessToken: tokens.access_token,\n raw: tokens,\n linkedAt: Date.now(),\n };\n\n if (extractedUserInfo?.email && !existingSession.email) {\n existingSession.email = extractedUserInfo.email;\n }\n\n await sessionStorage.saveSession(sessionId, existingSession, 86400);\n return sessionId;\n }\n\n /**\n * Unlinks an OAuth provider account from an active session.\n * \n * @param sessionId - Active session ID\n * @param providerToUnlink - Provider name to unlink (e.g. 'github')\n * @returns Promise resolving to true on successful unlink\n */\n public async unlinkAccount(sessionId: string, providerToUnlink: string): Promise<boolean> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const session = await sessionStorage.getSession(sessionId);\n\n if (!session || !session.accounts) {\n throw new AccountUnlinkError(\"Session not found or has no linked accounts.\", { sessionId });\n }\n\n const linkedProviders = Object.keys(session.accounts);\n if (linkedProviders.length <= 1) {\n throw new AccountUnlinkError(\"Cannot unlink the only authentication provider for this account.\", {\n sessionId,\n provider: providerToUnlink,\n });\n }\n\n delete session.accounts[providerToUnlink.toLowerCase()];\n await sessionStorage.saveSession(sessionId, session, 86400);\n return true;\n }\n\n /**\n * Generates an authorization URL for connecting a resource provider (AuthZ) post-login.\n * \n * @remarks\n * Resource authorization is kept strictly separate from primary authentication (AuthN).\n * Call this method after a user is authenticated to request permissions for external API access\n * (e.g. GitHub repositories, Google Drive, Slack, etc.).\n * \n * @param params - Object containing sessionId, provider, requested resource scopes, and optional state\n * @returns The authorization URL for resource consent\n */\n public async getResourceAuthUrl(params: {\n sessionId: string;\n provider: ConfiguredProviderKey<TConfig> | string;\n scopes: string[];\n state?: string;\n prompt?: string;\n extraConfig?: Record<string, string>;\n }): Promise<string> {\n const { sessionId, provider, scopes, state, prompt, extraConfig } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n\n if (!activeSession) {\n throw new SessionNotFoundError(\"Authentication required. Active session must exist to connect resource providers.\", { sessionId });\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(String(provider));\n }\n\n const providerImpl = this.getProvider(providerType, providerConfig);\n const stateValue = state || randomBytes(16).toString(\"hex\");\n const codeVerifier = randomBytes(32).toString(\"hex\");\n\n const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n await storage.saveState(\n stateValue,\n {\n createdAt: Date.now(),\n provider: providerType,\n codeVerifier,\n },\n 300\n );\n\n const codeChallenge = Lixa.buildCodeChallenge(codeVerifier);\n\n const searchParams = new URLSearchParams({\n client_id: providerConfig.clientId,\n redirect_uri: providerConfig.redirectUri,\n scope: scopes.join(\" \"),\n state: stateValue,\n response_type: \"code\",\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n ...(prompt ? { prompt } : {}),\n ...providerConfig.extraConfig,\n ...extraConfig,\n });\n\n return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;\n }\n\n private getUserKeyFromSession(session: Session): string {\n return session.userId || session.email || session.id || \"anonymous\";\n }\n\n /**\n * Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.\n */\n public async handleResourceCallback(params: {\n sessionId: string;\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n scopes?: string[];\n }): Promise<Session> {\n const { sessionId, provider, code, state, scopes } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n\n if (!activeSession) {\n throw new SessionNotFoundError(\"Authentication required. Active session not found for resource connection.\", { sessionId });\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(String(provider));\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n let codeVerifier = randomBytes(32).toString(\"hex\");\n if (state) {\n const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (!cachedState) {\n throw new InvalidStateError(\"Invalid or expired state during resource connection callback\", { state });\n }\n codeVerifier = cachedState.codeVerifier;\n await stateStorage.deleteState(state);\n }\n\n const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);\n const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined;\n\n const connectedResource: ConnectedResource = {\n provider: providerType,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n expiresAt,\n scopes: scopes || (tokens.scope ? tokens.scope.split(\" \") : []),\n raw: tokens,\n connectedAt: Date.now(),\n };\n\n // Save to persistent ResourceStorage bound to User ID / Email\n const userKey = this.getUserKeyFromSession(activeSession);\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n await resourceStorage.saveResource(userKey, providerType, connectedResource);\n\n // Attach to active session for session context\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = connectedResource;\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n\n return activeSession;\n }\n\n /**\n * Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).\n * Automatically refreshes expired access tokens if a refresh token is present.\n * \n * @param userIdOrEmail - User identifier or email\n * @param provider - Resource provider identifier (e.g. 'github', 'google')\n */\n public async getUserResource(userIdOrEmail: string, provider: string): Promise<ConnectedResource | null> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n const providerType = provider.toLowerCase();\n const resource = await resourceStorage.getResource(userIdOrEmail, providerType);\n if (!resource) return null;\n\n // Auto-refresh token if expired (or expiring in < 60 seconds) and refreshToken is available\n if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 60000) {\n this.log('INFO', 'Token', `Resource access token for user '${userIdOrEmail}' on '${providerType}' is expired. Auto-refreshing...`);\n try {\n return await this.refreshUserResourceToken(userIdOrEmail, providerType);\n } catch (error) {\n this.log('ERROR', 'Token', `Failed to auto-refresh resource token for user '${userIdOrEmail}' on '${providerType}'`, { error: String(error) });\n }\n }\n\n return resource;\n }\n\n /**\n * Retrieves all connected resources for a specific User ID / Email.\n */\n public async getUserResources(userIdOrEmail: string): Promise<Record<string, ConnectedResource>> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n return await resourceStorage.getUserResources(userIdOrEmail);\n }\n\n /**\n * Retrieves a connected resource provider token for an active session, auto-refreshing expired tokens if possible.\n * \n * @param sessionId - Active session ID\n * @param provider - Provider identifier (e.g. 'github', 'google')\n */\n public async getConnectedResource(sessionId: string, provider: string): Promise<ConnectedResource | null> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (!activeSession) return null;\n\n const providerType = provider.toLowerCase();\n const userKey = this.getUserKeyFromSession(activeSession);\n \n // 1. Try to fetch from persistent ResourceStorage by user key\n let resource = await this.getUserResource(userKey, providerType);\n\n // 2. Fallback to active session resource cache if available\n if (!resource && activeSession.resources) {\n resource = activeSession.resources[providerType] || null;\n }\n\n if (resource) {\n // Auto-refresh token if expired (or expiring in < 60 seconds) and refreshToken is available\n if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 60000) {\n this.log('INFO', 'Token', `Resource access token for user '${userKey}' on '${providerType}' is expired. Auto-refreshing...`);\n try {\n resource = await this.refreshResourceToken(sessionId, providerType);\n } catch (error) {\n this.log('ERROR', 'Token', `Failed to auto-refresh resource token for user '${userKey}' on '${providerType}'`, { error: String(error) });\n }\n }\n\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = resource;\n }\n\n return resource;\n }\n\n /**\n * Refreshes a user's resource access token using its refresh token.\n * Deduplicates concurrent refresh requests via an in-flight promise mutex.\n * \n * @param userIdOrEmail - User identifier or email\n * @param provider - Provider identifier (e.g. 'google', 'github')\n */\n public async refreshUserResourceToken(\n userIdOrEmail: string,\n provider: string,\n existingResource?: ConnectedResource\n ): Promise<ConnectedResource> {\n const mutexKey = `${userIdOrEmail.toLowerCase()}:${provider.toLowerCase()}`;\n const existingPromise = this.refreshMutexes.get(mutexKey);\n if (existingPromise) {\n this.log('INFO', 'Token', `Concurrent refresh request detected for '${mutexKey}'. Reusing in-flight promise.`);\n return existingPromise;\n }\n\n const refreshPromise = this.executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource)\n .finally(() => {\n this.refreshMutexes.delete(mutexKey);\n });\n\n this.refreshMutexes.set(mutexKey, refreshPromise);\n return refreshPromise;\n }\n\n /**\n * Internal execution of refresh token exchange.\n */\n private async executeRefreshUserResourceToken(\n userIdOrEmail: string,\n provider: string,\n existingResource?: ConnectedResource\n ): Promise<ConnectedResource> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n const providerType = provider.toLowerCase();\n let resource = existingResource || (await resourceStorage.getResource(userIdOrEmail, providerType));\n\n if (!resource || !resource.refreshToken) {\n throw new RefreshTokenError(\n `No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`,\n { userId: userIdOrEmail, provider }\n );\n }\n\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new ProviderNotConfiguredError(provider);\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n const body: Record<string, string> = {\n client_id: providerConfig.clientId,\n client_secret: providerConfig.clientSecret,\n grant_type: \"refresh_token\",\n refresh_token: resource.refreshToken,\n };\n\n const response = await fetch(providerImpl.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: new URLSearchParams(body).toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new RefreshTokenError(\n `Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`,\n { userId: userIdOrEmail, provider, status: response.status }\n );\n }\n\n const tokens: OAuthTokenResponse = await response.json();\n const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined;\n\n resource.accessToken = tokens.access_token;\n if (tokens.refresh_token) {\n resource.refreshToken = tokens.refresh_token;\n }\n if (expiresAt) {\n resource.expiresAt = expiresAt;\n }\n resource.raw = tokens;\n resource.connectedAt = Date.now();\n\n await resourceStorage.saveResource(userIdOrEmail, providerType, resource);\n return resource;\n }\n\n /**\n * Refreshes a connected resource access token for an active session.\n */\n public async refreshResourceToken(sessionId: string, provider: string): Promise<ConnectedResource> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (!activeSession) {\n throw new SessionNotFoundError(\"Active session not found.\", { sessionId });\n }\n const providerType = provider.toLowerCase();\n const userKey = this.getUserKeyFromSession(activeSession);\n const existingResource = activeSession.resources ? activeSession.resources[providerType] : undefined;\n\n const refreshed = await this.refreshUserResourceToken(userKey, providerType, existingResource);\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = refreshed;\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n return refreshed;\n }\n\n /**\n * Disconnects a resource provider for a specific User ID / Email.\n */\n public async disconnectUserResource(userIdOrEmail: string, provider: string): Promise<boolean> {\n const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage!;\n await resourceStorage.deleteResource(userIdOrEmail, provider.toLowerCase());\n return true;\n }\n\n /**\n * Disconnects a resource provider from an active session and user account.\n */\n public async disconnectResource(sessionId: string, provider: string): Promise<boolean> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (activeSession) {\n const userKey = this.getUserKeyFromSession(activeSession);\n await this.disconnectUserResource(userKey, provider);\n if (activeSession.resources) {\n delete activeSession.resources[provider.toLowerCase()];\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n }\n return true;\n }\n return false;\n }\n\n /**\n * Retrieves active session details from session storage.\n */\n public async fetchSessionInfo(sessionId: string): Promise<Session | null> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n return await sessionStorage.getSession<Session>(sessionId);\n }\n\n /**\n * Deletes a session from session storage (e.g. on logout).\n * \n * @param sessionId - Active session identifier\n */\n public async deleteSession(sessionId: string): Promise<void> {\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n await sessionStorage.deleteSession(sessionId);\n }\n\n private async exchangeCodeForToken(\n code: string,\n providerConfig: ProviderConfig,\n providerImpl: IProvider,\n codeVerifier: string\n ): Promise<OAuthTokenResponse> {\n // Build the request body\n const body: Record<string, string> = {\n client_id: providerConfig.clientId,\n client_secret: providerConfig.clientSecret,\n code,\n redirect_uri: providerConfig.redirectUri,\n grant_type: \"authorization_code\",\n };\n\n if (codeVerifier) {\n body.code_verifier = codeVerifier;\n }\n const params = new URLSearchParams(body);\n\n this.log('INFO', 'Token', 'Sending token exchange request', { \n endpoint: providerImpl.tokenEndpoint \n });\n \n const response = await fetch(providerImpl.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n this.log('ERROR', 'Token', 'Token exchange failed', { \n status: response.status, \n statusText: response.statusText,\n error: errorBody \n });\n throw new TokenExchangeError(\n `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`,\n response.status,\n { statusText: response.statusText, errorBody }\n );\n }\n\n this.log('INFO', 'Token', 'Token exchange response received successfully');\n return response.json();\n }\n\n /**\n * Checks if credentials (username and password) authentication is configured and enabled.\n * \n * @returns True if credentials authentication is available\n */\n public isCredentialsEnabled(): boolean {\n return this.credentialsManager !== undefined;\n }\n\n /**\n * Returns the underlying CredentialsManager instance if configured.\n */\n public getCredentialsManager(): CredentialsManager | undefined {\n return this.credentialsManager;\n }\n\n /**\n * Registers a new user with username/email and password.\n * Automatically enforces password policy, hashes password with Scrypt/configured hasher,\n * stores credentials, and creates a session (unless autoCreateSessionOnSignUp is false).\n * \n * @param params - Registration parameters (identifier, password, email, username, metadata)\n * @returns Created user (without password hash) and optional active session\n * @throws WeakPasswordError if password does not meet policy requirements\n * @throws UserAlreadyExistsError if identifier is already registered\n * @throws CredentialsNotConfiguredError if credentials auth is not configured\n */\n public async signUp(params: SignUpParams): Promise<SignUpResult> {\n if (!this.credentialsManager) {\n throw new CredentialsNotConfiguredError();\n }\n\n this.log(\"INFO\", \"Credentials\", \"Processing signUp request\", { identifier: params.identifier });\n const user = await this.credentialsManager.signUp(params);\n\n const autoSession = this.config.credentials?.autoCreateSessionOnSignUp ?? true;\n if (!autoSession) {\n return { user };\n }\n\n const sessionId = randomBytes(16).toString(\"hex\");\n const token = randomBytes(32).toString(\"hex\");\n const ttl = this.config.credentials?.sessionTtlSeconds ?? 86400;\n\n const credentialsAccount: LinkedAccount = {\n provider: \"credentials\",\n providerUserId: user.id,\n email: user.email,\n accessToken: token,\n raw: {\n access_token: token,\n token_type: \"Bearer\",\n expires_in: ttl,\n },\n linkedAt: Date.now(),\n };\n\n const session: Session = {\n id: sessionId,\n userId: user.id,\n email: user.email,\n token,\n provider: \"credentials\",\n accounts: {\n credentials: credentialsAccount,\n },\n };\n\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n await sessionStorage.saveSession(sessionId, session, ttl);\n\n this.log(\"INFO\", \"Credentials\", \"Created session on signUp\", { userId: user.id, sessionId });\n return { user, sessionId, session };\n }\n\n /**\n * Authenticates a user with username/email and password.\n * Performs constant-time verification with timing attack mitigation, and creates an active session.\n * If account linking is configured with AUTO_LINK_BY_VERIFIED_EMAIL, merges with existing session.\n * \n * @param params - Sign-in parameters (identifier, password)\n * @returns Authenticated user, session ID, and session object\n * @throws InvalidCredentialsError if authentication fails\n * @throws CredentialsNotConfiguredError if credentials auth is not configured\n */\n public async signIn(params: SignInParams): Promise<SignInResult> {\n if (!this.credentialsManager) {\n throw new CredentialsNotConfiguredError();\n }\n\n const lookupKey = params.username || params.identifier;\n this.log(\"INFO\", \"Credentials\", \"Processing signIn request\", { identifier: lookupKey });\n const user = await this.credentialsManager.verifyCredentials(params);\n if (!user) {\n this.log(\"WARN\", \"Credentials\", \"Invalid credentials provided\", { identifier: lookupKey });\n throw new InvalidCredentialsError();\n }\n\n const ttl = this.config.credentials?.sessionTtlSeconds ?? 86400;\n const token = randomBytes(32).toString(\"hex\");\n\n const credentialsAccount: LinkedAccount = {\n provider: \"credentials\",\n providerUserId: user.id,\n email: user.email,\n accessToken: token,\n raw: {\n access_token: token,\n token_type: \"Bearer\",\n expires_in: ttl,\n },\n linkedAt: Date.now(),\n };\n\n const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage!;\n\n // Check account linking if AUTO_LINK_BY_VERIFIED_EMAIL is configured and user has an email\n const linkingMode = this.config.accountLinking?.mode as string | undefined;\n const isAutoLink =\n linkingMode === AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL ||\n linkingMode === \"AUTO_LINK_BY_VERIFIED_EMAIL\" ||\n linkingMode === \"linkByEmail\";\n\n if (isAutoLink && user.email && sessionStorage.getSessionByEmail) {\n const existing = await sessionStorage.getSessionByEmail<Session>(user.email);\n if (existing) {\n const mergedSession: Session = { ...existing.session };\n mergedSession.accounts = { ...(mergedSession.accounts || {}), credentials: credentialsAccount };\n if (!mergedSession.userId) mergedSession.userId = user.id;\n if (!mergedSession.email) mergedSession.email = user.email;\n\n await sessionStorage.saveSession(existing.sessionId, mergedSession, ttl);\n this.log(\"INFO\", \"AccountLinking\", \"Linked credentials to existing session by email\", {\n sessionId: existing.sessionId,\n email: user.email,\n });\n\n return {\n user,\n sessionId: existing.sessionId,\n session: mergedSession,\n };\n }\n }\n\n const sessionId = randomBytes(16).toString(\"hex\");\n const session: Session = {\n id: sessionId,\n userId: user.id,\n email: user.email,\n token,\n provider: \"credentials\",\n accounts: {\n credentials: credentialsAccount,\n },\n };\n\n await sessionStorage.saveSession(sessionId, session, ttl);\n\n this.log(\"INFO\", \"Credentials\", \"User signed in successfully\", { userId: user.id, sessionId });\n return { user, sessionId, session };\n }\n\n /**\n * Verifies username/email and password credentials without generating a session.\n * \n * @param params - Verification parameters (identifier, password)\n * @returns User credentials (without password hash) or null if invalid\n * @throws CredentialsNotConfiguredError if credentials auth is not configured\n */\n public async verifyCredentials(\n params: VerifyCredentialsParams\n ): Promise<Omit<UserCredentials, \"passwordHash\"> | null> {\n if (!this.credentialsManager) {\n throw new CredentialsNotConfiguredError();\n }\n return this.credentialsManager.verifyCredentials(params);\n }\n\n /**\n * Updates a user's password after verifying the current password and enforcing policy on the new password.\n * \n * @param params - Change password parameters (userId/identifier, oldPassword, newPassword)\n * @returns True if password was updated successfully\n * @throws UserNotFoundError if user is not found\n * @throws InvalidCredentialsError if current password is incorrect\n * @throws WeakPasswordError if new password does not meet policy requirements\n * @throws CredentialsNotConfiguredError if credentials auth is not configured\n */\n public async changePassword(params: ChangePasswordParams): Promise<boolean> {\n if (!this.credentialsManager) {\n throw new CredentialsNotConfiguredError();\n }\n this.log(\"INFO\", \"Credentials\", \"Processing changePassword request\", {\n userId: params.userId,\n identifier: params.identifier,\n });\n return this.credentialsManager.changePassword(params);\n }\n\n private findProviderByType(providerType: string): ProviderConfig | undefined {\n // Use Object.entries to safely iterate and find the provider\n for (const [key, value] of Object.entries(this.config.providers || {})) {\n if (key.toLowerCase() === providerType.toLowerCase()) {\n return value;\n }\n }\n return undefined;\n }\n}\n\nexport { Lixa };\n\n","import crypto from \"crypto\";\nimport type { IPasswordHasher } from \"./types\";\n\n/**\n * Options for Scrypt password hashing.\n * \n * @public\n */\nexport interface ScryptHasherOptions {\n /** CPU/memory cost parameter (must be power of 2). Default: 16384 (2^14) */\n cost?: number;\n\n /** Block size parameter. Default: 8 */\n blockSize?: number;\n\n /** Parallelization parameter. Default: 1 */\n parallelization?: number;\n\n /** Salt length in bytes. Default: 16 */\n saltLength?: number;\n\n /** Derived key length in bytes. Default: 64 */\n keyLength?: number;\n\n /** Max memory allocated in bytes. Default: 32MB */\n maxmem?: number;\n}\n\n/**\n * Default secure password hasher utilizing Node.js native `crypto.scrypt`.\n * \n * @remarks\n * Produces PHC-formatted strings: `$scrypt$N=16384,r=8,p=1$<saltHex>$<keyHex>`.\n * Verification uses constant-time `crypto.timingSafeEqual` to prevent timing attacks.\n * \n * @public\n */\nexport class ScryptPasswordHasher implements IPasswordHasher {\n private readonly cost: number;\n private readonly blockSize: number;\n private readonly parallelization: number;\n private readonly saltLength: number;\n private readonly keyLength: number;\n private readonly maxmem: number;\n\n constructor(options: ScryptHasherOptions = {}) {\n this.cost = options.cost ?? 16384;\n this.blockSize = options.blockSize ?? 8;\n this.parallelization = options.parallelization ?? 1;\n this.saltLength = options.saltLength ?? 16;\n this.keyLength = options.keyLength ?? 64;\n this.maxmem = options.maxmem ?? 32 * 1024 * 1024;\n }\n\n /**\n * Hashes a plaintext password using Scrypt.\n * \n * @param password - Plaintext password\n * @returns Formatted Scrypt hash string\n */\n public async hash(password: string): Promise<string> {\n const salt = crypto.randomBytes(this.saltLength).toString(\"hex\");\n const derivedKey = await this.deriveKey(\n password,\n salt,\n this.keyLength,\n this.cost,\n this.blockSize,\n this.parallelization\n );\n\n return `$scrypt$N=${this.cost},r=${this.blockSize},p=${this.parallelization}$${salt}$${derivedKey.toString(\"hex\")}`;\n }\n\n /**\n * Verifies a password against a stored Scrypt hash.\n * \n * @param password - Plaintext password\n * @param hash - Formatted Scrypt hash string\n * @returns True if password matches hash\n */\n public async verify(password: string, hash: string): Promise<boolean> {\n if (!hash || typeof hash !== \"string\" || !hash.startsWith(\"$scrypt$\")) {\n return false;\n }\n\n const parts = hash.split(\"$\");\n // parts[0] is empty, parts[1] is 'scrypt', parts[2] is 'N=...,r=...,p=...', parts[3] is salt, parts[4] is key\n if (parts.length !== 5) {\n return false;\n }\n\n const paramsStr = parts[2];\n const salt = parts[3];\n const storedKeyHex = parts[4];\n\n if (!paramsStr || !salt || !storedKeyHex) {\n return false;\n }\n\n const params = new Map<string, number>();\n for (const param of paramsStr.split(\",\")) {\n const [k, v] = param.split(\"=\");\n if (k && v) {\n params.set(k.trim(), parseInt(v.trim(), 10));\n }\n }\n\n const cost = params.get(\"N\") ?? this.cost;\n const blockSize = params.get(\"r\") ?? this.blockSize;\n const parallelization = params.get(\"p\") ?? this.parallelization;\n\n const storedKeyBuffer = Buffer.from(storedKeyHex, \"hex\");\n const derivedKeyBuffer = await this.deriveKey(\n password,\n salt,\n storedKeyBuffer.length,\n cost,\n blockSize,\n parallelization\n );\n\n if (storedKeyBuffer.length !== derivedKeyBuffer.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(storedKeyBuffer, derivedKeyBuffer);\n }\n\n private deriveKey(\n password: string,\n salt: string,\n keyLength: number,\n cost: number,\n blockSize: number,\n parallelization: number\n ): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n crypto.scrypt(\n password,\n salt,\n keyLength,\n {\n N: cost,\n r: blockSize,\n p: parallelization,\n maxmem: this.maxmem,\n },\n (err, derivedKey) => {\n if (err) {\n reject(err);\n } else {\n resolve(derivedKey);\n }\n }\n );\n });\n }\n}\n\n/**\n * Options for PBKDF2 password hashing.\n * \n * @public\n */\nexport interface Pbkdf2HasherOptions {\n /** Iteration count. Default: 100000 */\n iterations?: number;\n\n /** HMAC digest algorithm. Default: 'sha512' */\n digest?: string;\n\n /** Salt length in bytes. Default: 16 */\n saltLength?: number;\n\n /** Derived key length in bytes. Default: 64 */\n keyLength?: number;\n}\n\n/**\n * Alternative password hasher using Node.js native `crypto.pbkdf2`.\n * \n * @remarks\n * Produces PHC-formatted strings: `$pbkdf2$i=100000,d=sha512$<saltHex>$<keyHex>`.\n * \n * @public\n */\nexport class Pbkdf2PasswordHasher implements IPasswordHasher {\n private readonly iterations: number;\n private readonly digest: string;\n private readonly saltLength: number;\n private readonly keyLength: number;\n\n constructor(options: Pbkdf2HasherOptions = {}) {\n this.iterations = options.iterations ?? 100000;\n this.digest = options.digest ?? \"sha512\";\n this.saltLength = options.saltLength ?? 16;\n this.keyLength = options.keyLength ?? 64;\n }\n\n public async hash(password: string): Promise<string> {\n const salt = crypto.randomBytes(this.saltLength).toString(\"hex\");\n const derivedKey = await this.deriveKey(\n password,\n salt,\n this.iterations,\n this.keyLength,\n this.digest\n );\n\n return `$pbkdf2$i=${this.iterations},d=${this.digest}$${salt}$${derivedKey.toString(\"hex\")}`;\n }\n\n public async verify(password: string, hash: string): Promise<boolean> {\n if (!hash || typeof hash !== \"string\" || !hash.startsWith(\"$pbkdf2$\")) {\n return false;\n }\n\n const parts = hash.split(\"$\");\n if (parts.length !== 5) {\n return false;\n }\n\n const paramsStr = parts[2];\n const salt = parts[3];\n const storedKeyHex = parts[4];\n\n if (!paramsStr || !salt || !storedKeyHex) {\n return false;\n }\n\n let iterations = this.iterations;\n let digest = this.digest;\n\n for (const param of paramsStr.split(\",\")) {\n const [k, v] = param.split(\"=\");\n if (k === \"i\" && v) {\n iterations = parseInt(v, 10);\n } else if (k === \"d\" && v) {\n digest = v;\n }\n }\n\n const storedKeyBuffer = Buffer.from(storedKeyHex, \"hex\");\n const derivedKeyBuffer = await this.deriveKey(\n password,\n salt,\n iterations,\n storedKeyBuffer.length,\n digest\n );\n\n if (storedKeyBuffer.length !== derivedKeyBuffer.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(storedKeyBuffer, derivedKeyBuffer);\n }\n\n private deriveKey(\n password: string,\n salt: string,\n iterations: number,\n keyLength: number,\n digest: string\n ): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n crypto.pbkdf2(password, salt, iterations, keyLength, digest, (err, derivedKey) => {\n if (err) {\n reject(err);\n } else {\n resolve(derivedKey);\n }\n });\n });\n }\n}\n","import type { PasswordPolicyConfig, PasswordPolicyResult } from \"./types\";\n\n/**\n * Validates a plaintext password against configured password policy rules.\n * \n * @param password - Plaintext password to evaluate\n * @param policy - Optional custom policy configuration\n * @returns PasswordPolicyResult containing boolean valid flag and error messages\n * \n * @public\n */\nexport async function validatePasswordPolicy(\n password: string,\n policy: PasswordPolicyConfig = {}\n): Promise<PasswordPolicyResult> {\n const errors: string[] = [];\n\n if (typeof password !== \"string\") {\n return {\n valid: false,\n errors: [\"Password must be a string\"],\n };\n }\n\n const minLength = policy.minLength ?? 8;\n const maxLength = policy.maxLength ?? 128;\n\n if (password.length < minLength) {\n errors.push(`Password must be at least ${minLength} characters long`);\n }\n\n if (password.length > maxLength) {\n errors.push(`Password must not exceed ${maxLength} characters`);\n }\n\n if (policy.requireUppercase && !/[A-Z]/.test(password)) {\n errors.push(\"Password must contain at least one uppercase letter (A-Z)\");\n }\n\n if (policy.requireLowercase && !/[a-z]/.test(password)) {\n errors.push(\"Password must contain at least one lowercase letter (a-z)\");\n }\n\n if (policy.requireNumbers && !/[0-9]/.test(password)) {\n errors.push(\"Password must contain at least one number (0-9)\");\n }\n\n if (policy.requireSpecialChars && !/[!@#$%^&*()_+\\-=[\\]{};':\"\\\\|,.<>/?~`]/.test(password)) {\n errors.push(\"Password must contain at least one special character\");\n }\n\n if (policy.customValidator) {\n try {\n const customResult = await policy.customValidator(password);\n if (customResult === false) {\n errors.push(\"Password failed custom validation rule\");\n } else if (typeof customResult === \"string\" && customResult.trim().length > 0) {\n errors.push(customResult);\n }\n } catch (err: any) {\n errors.push(`Custom password validator failed: ${err?.message || \"Unknown error\"}`);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import type { CredentialsStorage, UserCredentials } from \"./types\";\n\n/**\n * In-memory credentials storage for development, testing, and isolated environments.\n * \n * @remarks\n * Uses instance-isolated Maps to prevent state leakage across tests or Lixa instances.\n * \n * @public\n */\nexport class LocalCredentialsStorage implements CredentialsStorage {\n private usersById: Map<string, UserCredentials> = new Map();\n private identifierToId: Map<string, string> = new Map();\n\n public async saveUser(user: UserCredentials): Promise<void> {\n const normalizedIdentifier = user.identifier.trim().toLowerCase();\n this.usersById.set(user.id, { ...user, identifier: normalizedIdentifier });\n this.identifierToId.set(normalizedIdentifier, user.id);\n\n if (user.email) {\n this.identifierToId.set(user.email.trim().toLowerCase(), user.id);\n }\n if (user.username) {\n this.identifierToId.set(user.username.trim().toLowerCase(), user.id);\n }\n }\n\n public async findUserByIdentifier(identifier: string): Promise<UserCredentials | null> {\n const normalized = identifier.trim().toLowerCase();\n const id = this.identifierToId.get(normalized);\n if (!id) {\n return null;\n }\n const user = this.usersById.get(id);\n return user ? { ...user } : null;\n }\n\n public async findUserById(id: string): Promise<UserCredentials | null> {\n const user = this.usersById.get(id);\n return user ? { ...user } : null;\n }\n\n public async updatePassword(id: string, newPasswordHash: string): Promise<void> {\n const user = this.usersById.get(id);\n if (user) {\n user.passwordHash = newPasswordHash;\n user.updatedAt = Date.now();\n this.usersById.set(id, user);\n }\n }\n\n public async deleteUser(id: string): Promise<void> {\n const user = this.usersById.get(id);\n if (user) {\n this.identifierToId.delete(user.identifier.toLowerCase());\n if (user.email) this.identifierToId.delete(user.email.toLowerCase());\n if (user.username) this.identifierToId.delete(user.username.toLowerCase());\n this.usersById.delete(id);\n }\n }\n\n public clear(): void {\n this.usersById.clear();\n this.identifierToId.clear();\n }\n}\n","import crypto from \"crypto\";\nimport type {\n CredentialsConfig,\n CredentialsStorage,\n IPasswordHasher,\n PasswordPolicyConfig,\n UserCredentials,\n SignUpParams,\n VerifyCredentialsParams,\n ChangePasswordParams,\n} from \"./types\";\nimport { ScryptPasswordHasher } from \"./hasher\";\nimport { LocalCredentialsStorage } from \"./local-storage\";\nimport { validatePasswordPolicy } from \"./policy\";\nimport {\n InvalidCredentialsError,\n UserAlreadyExistsError,\n UserNotFoundError,\n WeakPasswordError,\n} from \"../errors\";\n\n/**\n * Coordinates user registration, authentication, password verification, policy enforcement,\n * and timing attack protection.\n * \n * @public\n */\nexport class CredentialsManager {\n private readonly storage: CredentialsStorage;\n private readonly hasher: IPasswordHasher;\n private readonly policy: PasswordPolicyConfig;\n private readonly identifierType: \"email\" | \"username\" | \"both\";\n private readonly requireUsername: boolean;\n private readonly requireEmail: boolean;\n private readonly timingAttackProtection: boolean;\n private dummyHash: string = \"\";\n\n constructor(config: CredentialsConfig = {}) {\n this.storage = config.storage || new LocalCredentialsStorage();\n this.hasher = config.hasher || new ScryptPasswordHasher();\n this.policy = {\n minLength: 8,\n maxLength: 128,\n ...config.policy,\n };\n this.identifierType = config.identifierType || \"both\";\n this.requireUsername = config.requireUsername ?? false;\n this.requireEmail = config.requireEmail ?? false;\n this.timingAttackProtection = config.timingAttackProtection ?? true;\n\n // Initialize dummy hash asynchronously for constant-time comparison on missing users\n this.initDummyHash().catch(() => {\n // Fallback pre-computed scrypt dummy hash\n this.dummyHash =\n \"$scrypt$N=16384,r=8,p=1$0123456789abcdef0123456789abcdef$0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\";\n });\n }\n\n private async initDummyHash(): Promise<void> {\n this.dummyHash = await this.hasher.hash(\"dummy_constant_password_12345\");\n }\n\n /**\n * Normalizes an identifier string.\n */\n private normalizeIdentifier(identifier: string): string {\n return (identifier || \"\").trim().toLowerCase();\n }\n\n /**\n * Validates identifier format based on configured identifierType.\n */\n private validateIdentifierFormat(identifier: string): void {\n const trimmed = (identifier || \"\").trim();\n if (!trimmed) {\n throw new InvalidCredentialsError(\"Identifier cannot be empty\");\n }\n\n if (this.identifierType === \"email\") {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(trimmed)) {\n throw new InvalidCredentialsError(\"Identifier must be a valid email address\");\n }\n }\n }\n\n /**\n * Registers a new user with password hashing and policy enforcement.\n * \n * @param params - Registration parameters\n * @returns Created user credentials without password hash\n */\n public async signUp(params: SignUpParams): Promise<Omit<UserCredentials, \"passwordHash\">> {\n const rawIdentifier = params.username || params.identifier || params.email || \"\";\n this.validateIdentifierFormat(rawIdentifier);\n const normalized = this.normalizeIdentifier(rawIdentifier);\n\n // Auto-detect email / username if not explicitly specified\n const isEmail = normalized.includes(\"@\");\n const email = params.email ? params.email.trim().toLowerCase() : isEmail ? normalized : undefined;\n const username = params.username ? params.username.trim() : !isEmail ? normalized : undefined;\n\n // Enforce username requirement if configured\n if (this.requireUsername && (!username || username.trim() === \"\")) {\n throw new InvalidCredentialsError(\"Username is required\");\n }\n\n // Enforce email requirement if configured\n if (this.requireEmail && (!email || !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email))) {\n throw new InvalidCredentialsError(\"A valid email address is required\");\n }\n\n // Check if user already exists\n const existing = await this.storage.findUserByIdentifier(normalized);\n if (existing) {\n throw new UserAlreadyExistsError(normalized);\n }\n\n // Validate password against policy\n const policyResult = await validatePasswordPolicy(params.password, this.policy);\n if (!policyResult.valid) {\n throw new WeakPasswordError(policyResult.errors);\n }\n\n // Hash password\n const passwordHash = await this.hasher.hash(params.password);\n const now = Date.now();\n const id = typeof crypto.randomUUID === \"function\" ? crypto.randomUUID() : crypto.randomBytes(16).toString(\"hex\");\n\n const user: UserCredentials = {\n id,\n identifier: normalized,\n email,\n username,\n passwordHash,\n createdAt: now,\n updatedAt: now,\n metadata: params.metadata,\n };\n\n await this.storage.saveUser(user);\n\n const { passwordHash: _, ...safeUser } = user;\n return safeUser;\n }\n\n /**\n * Verifies credentials against storage with timing attack protection.\n * \n * @param params - Verification parameters\n * @returns User credentials without password hash, or null if verification fails\n */\n public async verifyCredentials(params: VerifyCredentialsParams): Promise<Omit<UserCredentials, \"passwordHash\"> | null> {\n const rawIdentifier = params.username || params.identifier;\n if (!rawIdentifier || typeof params.password !== \"string\") {\n return null;\n }\n\n const normalized = this.normalizeIdentifier(rawIdentifier);\n const user = await this.storage.findUserByIdentifier(normalized);\n\n if (!user) {\n if (this.timingAttackProtection) {\n // Execute dummy verification to normalize response duration\n try {\n if (!this.dummyHash) {\n await this.initDummyHash();\n }\n await this.hasher.verify(params.password, this.dummyHash);\n } catch {\n // Ignore dummy verification error\n }\n }\n return null;\n }\n\n const isMatch = await this.hasher.verify(params.password, user.passwordHash);\n if (!isMatch) {\n return null;\n }\n\n const { passwordHash: _, ...safeUser } = user;\n return safeUser;\n }\n\n /**\n * Changes a user's password with old password verification and new password policy enforcement.\n * \n * @param params - Change password parameters\n * @returns True if password was successfully updated\n */\n public async changePassword(params: ChangePasswordParams): Promise<boolean> {\n let user: UserCredentials | null = null;\n const lookupKey = params.username || params.identifier;\n\n if (params.userId) {\n user = await this.storage.findUserById(params.userId);\n } else if (lookupKey) {\n user = await this.storage.findUserByIdentifier(this.normalizeIdentifier(lookupKey));\n }\n\n if (!user) {\n if (this.timingAttackProtection) {\n try {\n if (!this.dummyHash) await this.initDummyHash();\n await this.hasher.verify(params.oldPassword, this.dummyHash);\n } catch {\n // Ignore dummy error\n }\n }\n throw new UserNotFoundError(params.userId || lookupKey || \"unknown\");\n }\n\n const isOldValid = await this.hasher.verify(params.oldPassword, user.passwordHash);\n if (!isOldValid) {\n throw new InvalidCredentialsError(\"Current password is incorrect\");\n }\n\n const policyResult = await validatePasswordPolicy(params.newPassword, this.policy);\n if (!policyResult.valid) {\n throw new WeakPasswordError(policyResult.errors);\n }\n\n const newHash = await this.hasher.hash(params.newPassword);\n await this.storage.updatePassword(user.id, newHash);\n return true;\n }\n\n /**\n * Finds a user by ID and returns safe user data.\n */\n public async getUserById(id: string): Promise<Omit<UserCredentials, \"passwordHash\"> | null> {\n const user = await this.storage.findUserById(id);\n if (!user) return null;\n const { passwordHash: _, ...safeUser } = user;\n return safeUser;\n }\n\n /**\n * Finds a user by identifier and returns safe user data.\n */\n public async getUserByIdentifier(identifier: string): Promise<Omit<UserCredentials, \"passwordHash\"> | null> {\n const user = await this.storage.findUserByIdentifier(this.normalizeIdentifier(identifier));\n if (!user) return null;\n const { passwordHash: _, ...safeUser } = user;\n return safeUser;\n }\n\n /**\n * Gets the underlying storage instance.\n */\n public getStorage(): CredentialsStorage {\n return this.storage;\n }\n\n /**\n * Gets the underlying hasher instance.\n */\n public getHasher(): IPasswordHasher {\n return this.hasher;\n }\n}\n","/**\n * Base error class for all Lixa authentication and authorization errors.\n * \n * @public\n */\nexport class LixaError extends Error {\n /**\n * Standard error code string.\n */\n public readonly code: string;\n\n /**\n * Additional error context data.\n */\n public readonly details: Record<string, unknown> | undefined;\n\n constructor(message: string, code: string = \"LIXA_ERROR\", details?: Record<string, unknown>) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.details = details;\n\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when an OAuth state parameter is invalid, missing, or has expired.\n * \n * @public\n */\nexport class InvalidStateError extends LixaError {\n constructor(message: string = \"Invalid or expired state\", details?: Record<string, unknown>) {\n super(message, \"INVALID_STATE\", details);\n }\n}\n\n/**\n * Thrown when attempting to use a provider that is not configured in the Lixa instance.\n * \n * @public\n */\nexport class ProviderNotConfiguredError extends LixaError {\n constructor(provider: string, details?: Record<string, unknown>) {\n const message = details?.message && typeof details.message === \"string\"\n ? details.message\n : `Provider '${provider}' is not available. Either import it from '@vunexa/lixa-providers' and include it in the configuration, or provide a custom implementation using the 'provider' field: { provider: new CustomProvider(), clientId: '...', ... }`;\n super(message, \"PROVIDER_NOT_CONFIGURED\", {\n provider,\n ...details,\n });\n }\n}\n\n/**\n * Thrown when a provider configuration is invalid or missing required credentials.\n * \n * @public\n */\nexport class InvalidProviderConfigError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"INVALID_PROVIDER_CONFIG\", details);\n }\n}\n\n/**\n * Thrown when OAuth callback parameters (e.g. authorization code or state) are missing or malformed.\n * \n * @public\n */\nexport class InvalidOAuthCallbackError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"INVALID_OAUTH_CALLBACK\", details);\n }\n}\n\n/**\n * Thrown when exchanging an authorization code for OAuth tokens fails at the provider endpoint.\n * \n * @public\n */\nexport class TokenExchangeError extends LixaError {\n public readonly status: number | undefined;\n\n constructor(message: string, status?: number, details?: Record<string, unknown>) {\n super(message, \"TOKEN_EXCHANGE_FAILED\", { status, ...details });\n this.status = status;\n }\n}\n\n/**\n * Thrown when a user session is not found or has expired.\n * \n * @public\n */\nexport class SessionNotFoundError extends LixaError {\n constructor(message: string = \"Active session not found or has expired\", details?: Record<string, unknown>) {\n super(message, \"SESSION_NOT_FOUND\", details);\n }\n}\n\n/**\n * Thrown when account linking fails, e.g. when unverified email linking is rejected.\n * \n * @public\n */\nexport class EmailNotVerifiedError extends LixaError {\n constructor(email?: string, details?: Record<string, unknown>) {\n super(\n `Cannot link account: email '${email || \"unknown\"}' is not verified by the identity provider`,\n \"EMAIL_NOT_VERIFIED\",\n { email, ...details }\n );\n }\n}\n\n/**\n * Thrown when unlinking an account violates security constraints (e.g. unlinking the only login provider).\n * \n * @public\n */\nexport class AccountUnlinkError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"ACCOUNT_UNLINK_ERROR\", details);\n }\n}\n\n/**\n * Thrown when a refresh token is missing or token refresh fails for a connected resource.\n * \n * @public\n */\nexport class RefreshTokenError extends LixaError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, \"REFRESH_TOKEN_ERROR\", details);\n }\n}\n\n/**\n * Thrown when user credentials (username/email and password) are invalid during authentication.\n * \n * @public\n */\nexport class InvalidCredentialsError extends LixaError {\n constructor(message: string = \"Invalid identifier or password\", details?: Record<string, unknown>) {\n super(message, \"INVALID_CREDENTIALS\", details);\n }\n}\n\n/**\n * Thrown when attempting to register a user with an identifier that already exists.\n * \n * @public\n */\nexport class UserAlreadyExistsError extends LixaError {\n constructor(identifier: string, details?: Record<string, unknown>) {\n super(`User with identifier '${identifier}' already exists`, \"USER_ALREADY_EXISTS\", {\n identifier,\n ...details,\n });\n }\n}\n\n/**\n * Thrown when a user account cannot be found for credential verification or password change.\n * \n * @public\n */\nexport class UserNotFoundError extends LixaError {\n constructor(identifierOrId?: string, details?: Record<string, unknown>) {\n super(\n `User not found${identifierOrId ? `: '${identifierOrId}'` : \"\"}`,\n \"USER_NOT_FOUND\",\n { identifierOrId, ...details }\n );\n }\n}\n\n/**\n * Thrown when a password does not satisfy configured password policy rules.\n * \n * @public\n */\nexport class WeakPasswordError extends LixaError {\n public readonly validationErrors: string[];\n\n constructor(validationErrors: string[] = [], details?: Record<string, unknown>) {\n const message =\n validationErrors.length > 0\n ? `Password does not meet security requirements: ${validationErrors.join(\"; \")}`\n : \"Password does not meet security requirements\";\n super(message, \"WEAK_PASSWORD\", { validationErrors, ...details });\n this.validationErrors = validationErrors;\n }\n}\n\n/**\n * Thrown when credentials operations (signUp, signIn, etc.) are invoked on a Lixa instance\n * where credentials authentication is not enabled.\n * \n * @public\n */\nexport class CredentialsNotConfiguredError extends LixaError {\n constructor(\n message: string = \"Credentials authentication is not configured in this Lixa instance. Pass credentials: {} in LixaConfig to enable it.\",\n details?: Record<string, unknown>\n ) {\n super(message, \"CREDENTIALS_NOT_CONFIGURED\", details);\n }\n}\n\n","import { SessionHandler, SessionStorage, ResourceHandler, ResourceStorage, StateHandler, StateStorage, StateData } from \"./dao/types\";\nimport { Session, ProviderMetadata } from \"./models/session\";\nimport { IProvider } from \"./providers/IProvider\";\nimport {\n UserCredentials,\n CredentialsStorage,\n IPasswordHasher,\n PasswordPolicyConfig,\n PasswordPolicyResult,\n CredentialsConfig,\n SignUpParams,\n SignUpResult,\n SignInParams,\n SignInResult,\n VerifyCredentialsParams,\n ChangePasswordParams,\n} from \"./credentials/types\";\nimport { ScryptPasswordHasher, ScryptHasherOptions, Pbkdf2PasswordHasher, Pbkdf2HasherOptions } from \"./credentials/hasher\";\nimport { validatePasswordPolicy } from \"./credentials/policy\";\nimport { LocalCredentialsStorage } from \"./credentials/local-storage\";\nimport { CredentialsManager } from \"./credentials/credentials-manager\";\n\n// Re-export types for external use\nexport { Session, ConnectedResource } from \"./models/session\";\nexport { ProviderMetadata };\nexport { OAuthTokenResponse } from \"./models/session\";\nexport { IProvider };\nexport { StateHandler };\nexport { StateStorage };\nexport { SessionHandler };\nexport { SessionStorage };\nexport { ResourceHandler };\nexport { ResourceStorage };\nexport { StateData };\nexport {\n UserCredentials,\n CredentialsStorage,\n IPasswordHasher,\n PasswordPolicyConfig,\n PasswordPolicyResult,\n CredentialsConfig,\n SignUpParams,\n SignUpResult,\n SignInParams,\n SignInResult,\n VerifyCredentialsParams,\n ChangePasswordParams,\n};\nexport { ScryptPasswordHasher, ScryptHasherOptions, Pbkdf2PasswordHasher, Pbkdf2HasherOptions };\nexport { validatePasswordPolicy };\nexport { LocalCredentialsStorage };\nexport { CredentialsManager };\n\n\n/**\n * Configuration for an OAuth provider instance.\n * \n * @remarks\n * For built-in providers (google, github), just provide credentials.\n * For custom providers, include the provider implementation.\n * \n * The provider field uses a discriminated union to ensure type safety:\n * - When omitted or undefined: assumes a built-in provider\n * - When provided: must be a valid IProvider implementation\n * \n * @example\n * Built-in provider configuration:\n * ```typescript\n * {\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://app.com/callback',\n * scopes: ['openid', 'email']\n * }\n * ```\n * \n * @example\n * Custom provider configuration:\n * ```typescript\n * {\n * provider: new CustomProvider(),\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://app.com/callback',\n * scopes: ['read:user']\n * }\n * ```\n *\n * @public\n */\nexport type ProviderConfig = {\n /** The OAuth client ID provided by the provider */\n clientId: string;\n \n /** The OAuth client secret provided by the provider */\n clientSecret: string;\n \n /** The redirect URI registered with the provider */\n redirectUri: string;\n \n /** Array of OAuth scopes to request */\n scopes: string[];\n\n /** \n * Set to true to allow non-identity (resource) scopes during primary authentication flow.\n * By default (false), Lixa restricts primary AuthN scopes to identity scopes to maintain\n * clean AuthN vs AuthZ separation.\n */\n allowNonAuthScopes?: boolean;\n \n /** Additional provider-specific configuration parameters */\n extraConfig?: Record<string, string>;\n} & (\n | { provider?: never } // Built-in provider (no provider field needed)\n | { provider: IProvider } // Custom provider (provider field required)\n);\n\n/**\n * Main configuration object for Lixa.\n * Provides type-safe provider name inference.\n * \n * @remarks\n * The generic type parameter TProviders enables TypeScript to infer provider names\n * from the configuration object, providing autocomplete and type checking for\n * provider names in methods like getAuthUrl() and handleCallback().\n * \n * @typeParam TProviders - The provider configuration map type, defaults to a generic record\n * \n * @example\n * Basic configuration with built-in providers:\n * ```typescript\n * import { Lixa } from '@vunexa/lixa';\n * import { GoogleProvider } from '@vunexa/lixa-providers';\n * \n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: process.env.GOOGLE_CLIENT_ID!,\n * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n * redirectUri: 'https://app.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * }\n * });\n * ```\n * \n * @example\n * Configuration with custom session and state handlers:\n * ```typescript\n * const lixa = new Lixa({\n * providers: {\n * google: {\n * provider: new GoogleProvider(),\n * clientId: process.env.GOOGLE_CLIENT_ID!,\n * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n * redirectUri: 'https://app.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * },\n * stateHandler: {\n * storage: {\n * saveState: async (state, data, ttl) => await redis.setex(state, ttl, JSON.stringify(data)),\n * getState: async (state) => JSON.parse(await redis.get(state) || 'null'),\n * deleteState: async (state) => await redis.del(state)\n * }\n * },\n * sessionHandler: {\n * GenerateSession: async (tokenData, providerMetadata) => {\n * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);\n * const user = await db.users.upsert({ email: userInfo.email });\n * return { token: tokenData.access_token, raw: { ...tokenData, userId: user.id } };\n * },\n * storage: {\n * saveSession: async (id, session, ttl) => await db.sessions.create({ id, session, ttl }),\n * getSession: async (id) => await db.sessions.findOne({ id }),\n * deleteSession: async (id) => await db.sessions.delete({ id })\n * }\n * },\n * debug: true\n * });\n * ```\n *\n/**\n * Strategy mode for multi-SSO identity account linking.\n * \n * @public\n */\nexport enum AccountLinkingStrategy {\n /** Automatically merge identities matching the same verified primary email address */\n AUTO_LINK_BY_VERIFIED_EMAIL = \"AUTO_LINK_BY_VERIFIED_EMAIL\",\n\n /** Keep identity profiles isolated per provider (no automatic account merging) */\n ISOLATED = \"ISOLATED\",\n}\n\n/**\n * Supported mode values for account linking configuration.\n * \n * @public\n */\nexport type AccountLinkingMode =\n | AccountLinkingStrategy\n | \"AUTO_LINK_BY_VERIFIED_EMAIL\"\n | \"ISOLATED\"\n | \"linkByEmail\"\n | \"separate\";\n\n/**\n * Account linking settings for Lixa.\n * \n * @public\n */\nexport interface AccountLinkingConfig {\n /**\n * Account linking mode strategy:\n * - AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL (\"AUTO_LINK_BY_VERIFIED_EMAIL\" / \"linkByEmail\"): Auto-link accounts sharing same verified email.\n * - AccountLinkingStrategy.ISOLATED (\"ISOLATED\" / \"separate\"): Keep provider accounts isolated (default).\n * \n * @default AccountLinkingStrategy.ISOLATED\n */\n mode?: AccountLinkingMode;\n\n /**\n * Whether to require that the email address is verified by the provider before linking.\n * \n * @default true\n */\n requireVerifiedEmail?: boolean;\n}\n\n/**\n * Main configuration object for Lixa.\n * Provides type-safe provider name inference.\n * \n * @public\n */\nexport interface LixaConfig<TProviders extends Record<string, ProviderConfig> = Record<string, ProviderConfig>> {\n /** \n * Map of provider names to their configurations.\n * Provider names will be available for autocomplete in getAuthUrl() and handleCallback().\n */\n providers?: TProviders;\n\n /**\n * Credentials (username and password) authentication configuration.\n */\n credentials?: CredentialsConfig;\n\n /**\n * Account linking configuration for multi-SSO user linking.\n */\n accountLinking?: AccountLinkingConfig;\n /** \n * Optional custom state handler.\n * Handles state generation and storage during OAuth authorization flow.\n * \n * - GenerateState: Customizes how state parameters and PKCE verifiers are generated\n * - storage: Provides persistent state storage (save/get/delete operations)\n * \n * Defaults to in-memory cache if not provided (not suitable for production).\n * \n * @see {@link StateHandler}\n */\n stateHandler?: StateHandler;\n \n /** \n * Optional custom session handler.\n * Handles session generation and storage after authentication.\n * \n * - GenerateSession: Customizes how OAuth tokens are converted into session data\n * - storage: Provides persistent session storage (save/get/delete operations)\n * \n * Defaults to in-memory cache if not provided (not suitable for production).\n * \n * @see {@link SessionHandler}\n */\n sessionHandler?: SessionHandler;\n\n /**\n * Optional custom resource handler.\n * Handles storage and management of long-lived third-party resource provider tokens (AuthZ)\n * bound directly to user accounts (User ID or Email), independent of transient session IDs.\n * \n * @see {@link ResourceHandler}\n */\n resourceHandler?: import(\"./dao/types\").ResourceHandler;\n \n /** \n * Enable debug logging.\n * When enabled, outputs structured logs for initialization, auth flow, and errors.\n * Format: [Lixa] [timestamp] [level] [context] message\n */\n debug?: boolean;\n\n /**\n * Optional custom structured logger implementation.\n * If provided, all Lixa logs will be routed through this logger.\n */\n logger?: LixaLogger;\n}\n\n/**\n * Log level for Lixa structured logging.\n * @public\n */\nexport type LogLevel = \"INFO\" | \"WARN\" | \"ERROR\" | \"DEBUG\";\n\n/**\n * Log context for Lixa structured logging.\n * @public\n */\nexport type LogContext = \"Init\" | \"Auth\" | \"Token\" | \"Session\" | \"State\" | \"AccountLinking\" | \"Resource\" | \"Credentials\";\n\n/**\n * Custom logger interface for Lixa.\n * @public\n */\nexport interface LixaLogger {\n log(level: LogLevel, context: LogContext, message: string, data?: Record<string, unknown>): void;\n}\n\n/**\n * Helper type to create a configuration with only registered providers.\n * Use this with Lixa.createConfig() for type safety.\n * \n * @deprecated This type is maintained for backward compatibility.\n * The new inline provider configuration pattern makes this unnecessary.\n *\n * @public\n */\nexport type SafeLixaConfig<TProviders extends Record<string, ProviderConfig>> = LixaConfig<TProviders> & {\n providers: TProviders;\n};\n\n","import NodeCache from 'node-cache';\nimport { randomBytes } from 'crypto';\nimport { StateHandler, StateStorage, StateData } from \"./types\";\n\nclass LocalStateHandler implements StateHandler {\n private cache: NodeCache;\n public stateStorage: StateStorage;\n\n constructor(defaultTtlSeconds: number = 600) {\n this.cache = new NodeCache({ stdTTL: defaultTtlSeconds });\n \n // Provide storage implementation\n this.stateStorage = {\n saveState: async (state: string, data: StateData, expiresInSeconds: number): Promise<void> => {\n this.cache.set(state, data, expiresInSeconds);\n },\n\n getState: async (state: string): Promise<StateData | null> => {\n return this.cache.get<StateData>(state) || null;\n },\n\n deleteState: async (state: string): Promise<void> => {\n this.cache.del(state);\n }\n };\n }\n\n // Default generateState implementation\n async generateState(provider: string): Promise<{ state: string; data: StateData }> {\n const state = randomBytes(16).toString(\"hex\"); // 32 characters\n const codeVerifier = randomBytes(32).toString(\"hex\"); // 64 characters\n \n return {\n state,\n data: {\n provider,\n codeVerifier,\n createdAt: Date.now()\n }\n };\n }\n\n // PascalCase alias for backward compatibility\n async GenerateState(provider: string): Promise<{ state: string; data: StateData }> {\n return this.generateState(provider);\n }\n}\n\nexport { LocalStateHandler };\n\n","import NodeCache from 'node-cache';\nimport { SessionHandler, SessionStorage } from \"./types\";\nimport type { Session, OAuthTokenResponse, ProviderMetadata } from \"../models/session\";\n\nclass LocalSessionHandler implements SessionHandler {\n private cache: NodeCache;\n private emailToSessionMap: Map<string, string> = new Map();\n public sessionStorage: SessionStorage;\n\n constructor(defaultTtlSeconds: number = 600) {\n this.cache = new NodeCache({ stdTTL: defaultTtlSeconds, checkperiod: 60 });\n \n // Automatically purge email map entries when cache keys expire or are deleted to prevent memory leaks\n this.cache.on(\"expired\", (_key: string, value: any) => {\n if (value && typeof value === \"object\" && value.email) {\n this.emailToSessionMap.delete(String(value.email).toLowerCase());\n }\n });\n\n this.cache.on(\"del\", (_key: string, value: any) => {\n if (value && typeof value === \"object\" && value.email) {\n this.emailToSessionMap.delete(String(value.email).toLowerCase());\n }\n });\n\n this.cache.on(\"flush\", () => {\n this.emailToSessionMap.clear();\n });\n\n // Provide storage implementation\n this.sessionStorage = {\n saveSession: async <T extends Session>(sessionId: string, session: T, expiresInSeconds: number): Promise<void> => {\n this.cache.set(sessionId, session, expiresInSeconds);\n if (session.email) {\n this.emailToSessionMap.set(session.email.toLowerCase(), sessionId);\n }\n },\n\n getSession: async <T extends Session>(sessionId: string): Promise<T | null> => {\n return this.cache.get<T>(sessionId) || null;\n },\n\n deleteSession: async (sessionId: string): Promise<void> => {\n const session = this.cache.get<Session>(sessionId);\n if (session?.email) {\n this.emailToSessionMap.delete(session.email.toLowerCase());\n }\n this.cache.del(sessionId);\n },\n\n getSessionByEmail: async <T extends Session>(email: string): Promise<{ sessionId: string; session: T } | null> => {\n const normalizedEmail = email.toLowerCase();\n const sessionId = this.emailToSessionMap.get(normalizedEmail);\n if (!sessionId) return null;\n const session = this.cache.get<T>(sessionId);\n if (!session) {\n this.emailToSessionMap.delete(normalizedEmail);\n return null;\n }\n return { sessionId, session };\n }\n };\n }\n\n // Default generateSession implementation\n async generateSession<T extends Session>(\n tokenData: OAuthTokenResponse,\n _providerMetadata?: ProviderMetadata\n ): Promise<T> {\n if (!tokenData.access_token || typeof tokenData.access_token !== 'string') {\n throw new Error('No valid access token found in OAuth response');\n }\n\n const session: Session = {\n token: tokenData.access_token,\n raw: tokenData,\n };\n\n return session as T;\n }\n\n // PascalCase alias for backward compatibility\n async GenerateSession<T extends Session>(\n tokenData: OAuthTokenResponse,\n providerMetadata?: ProviderMetadata\n ): Promise<T> {\n return this.generateSession(tokenData, providerMetadata);\n }\n}\n\nexport { LocalSessionHandler };\n\n","import type { OAuthTokenResponse, ProviderMetadata } from \"../models/session\";\n\n/**\n * User information extracted from OAuth provider\n * \n * @public\n */\nexport interface UserInfo {\n email: string;\n id?: string | undefined;\n sub?: string | undefined;\n given_name?: string | undefined;\n family_name?: string | undefined;\n name?: string | undefined;\n picture?: string | undefined;\n email_verified?: boolean | undefined;\n iss?: string | undefined;\n}\n\n/**\n * Decode JWT ID token to extract user information\n * \n * @public\n */\nexport function decodeIdToken(idToken: string): UserInfo {\n const parts = idToken.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid ID token format: expected 3 parts separated by dots');\n }\n \n const base64Payload = parts[1];\n if (!base64Payload) {\n throw new Error('Invalid ID token: missing payload section');\n }\n const payload = Buffer.from(base64Payload, 'base64').toString();\n \n try {\n return JSON.parse(payload);\n } catch (error) {\n throw new Error('Invalid ID token: failed to parse payload JSON');\n }\n}\n\n/**\n * Determine OAuth provider from ID token issuer\n * \n * @public\n */\nexport function determineProviderFromIssuer(userInfo: UserInfo): string | null {\n if (!userInfo.iss) {\n return null;\n }\n \n const issuer = userInfo.iss.toLowerCase();\n \n if (issuer.includes('accounts.google.com')) {\n return 'google';\n }\n \n if (issuer.includes('github')) {\n return 'github';\n }\n \n // Unknown issuer\n return null;\n}\n\n/**\n * Fetch user info from OAuth provider's userinfo endpoint\n * \n * @param accessToken - OAuth access token\n * @param userInfoEndpoint - The provider's userinfo endpoint URL\n * @param providerName - Provider name for error messages (optional)\n * @returns User information from the provider\n * \n * @throws Error if the request fails or response is invalid\n * \n * @public\n */\nexport async function fetchUserInfo(\n accessToken: string, \n userInfoEndpoint: string\n): Promise<UserInfo> {\n const response = await fetch(userInfoEndpoint, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch user info: ${response.status} ${response.statusText}`);\n }\n \n const data = await response.json();\n if (!data || typeof data !== 'object' || !('email' in data) || typeof data.email !== 'string') {\n throw new Error(`Invalid user info response: missing or invalid email`);\n }\n \n return {\n email: data.email,\n id: 'id' in data ? String(data.id) : undefined,\n sub: 'sub' in data ? String(data.sub) : undefined,\n given_name: 'given_name' in data ? String(data.given_name) : undefined,\n family_name: 'family_name' in data ? String(data.family_name) : undefined,\n name: 'name' in data ? String(data.name) : undefined,\n picture: 'picture' in data ? String(data.picture) : undefined,\n email_verified: 'email_verified' in data ? Boolean(data.email_verified) : undefined,\n iss: 'iss' in data ? String(data.iss) : undefined,\n };\n}\n\n/**\n * Extract user info from OAuth token data\n * \n * @param tokenData - OAuth token response from provider\n * @param providerMetadata - Provider metadata containing endpoints configuration\n * @returns User info extracted from token or fetched from provider\n * \n * @remarks\n * This function attempts to extract user information in the following order:\n * 1. Decode ID token if present (preferred method for OIDC providers)\n * 2. Fetch from userinfo endpoint using access token (uses providerMetadata.endpoints.userInfo)\n * \n * The function automatically determines the best method based on available token data.\n * For OIDC providers (like Google), it decodes the JWT ID token.\n * For OAuth-only providers (like GitHub), it fetches from the userinfo endpoint.\n * \n * @throws Error if no ID token or access token is available\n * @throws Error if userinfo endpoint is required but not provided in providerMetadata\n * \n * @example\n * With ID token (OIDC provider like Google):\n * ```typescript\n * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);\n * console.log(`User ${userInfo.email} authenticated`);\n * ```\n * \n * @example\n * Without ID token (OAuth provider like GitHub):\n * ```typescript\n * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);\n * // Automatically fetches from providerMetadata.endpoints.userInfo\n * console.log(`User ${userInfo.email} authenticated`);\n * ```\n * \n * @public\n */\nexport async function extractUserInfo(\n tokenData: OAuthTokenResponse,\n providerMetadata: ProviderMetadata\n): Promise<{ userInfo: UserInfo }> {\n let userInfo: UserInfo;\n const userInfoEndpoint = providerMetadata.endpoints.userInfo;\n \n if (tokenData.id_token) {\n // Decode ID token to get user info\n userInfo = decodeIdToken(tokenData.id_token);\n } else if (tokenData.access_token) {\n // Fetch user info from provider's API\n userInfo = await fetchUserInfo(tokenData.access_token, userInfoEndpoint);\n } else {\n throw new Error('No ID token or access token available to fetch user info');\n }\n \n return { userInfo };\n}\n","/**\n * Sensible default cookie configuration options.\n * Follows RFC 6265bis and OAuth 2.0 security best practices.\n * \n * @public\n */\nexport interface CookieOptions {\n /**\n * Cookie name.\n * @default 'lixa_session'\n */\n name?: string | undefined;\n\n /**\n * Cookie path.\n * @default '/'\n */\n path?: string | undefined;\n\n /**\n * Maximum age of the cookie in seconds.\n */\n maxAge?: number | undefined;\n\n /**\n * Prevents client-side scripts from accessing the cookie (XSS protection).\n * @default true\n */\n httpOnly?: boolean | undefined;\n\n /**\n * Ensures the cookie is only transmitted over secure HTTPS connections.\n * @default false in development, true in production\n */\n secure?: boolean | undefined;\n\n /**\n * Controls whether the cookie is sent with cross-site requests (CSRF protection).\n * @default 'lax'\n */\n sameSite?: \"lax\" | \"strict\" | \"none\" | undefined;\n\n /**\n * Cookie domain.\n */\n domain?: string | undefined;\n}\n\n/**\n * Cookie payload containing name, value, options, and formatted header.\n * \n * @public\n */\nexport interface CookiePayload {\n name: string;\n value: string;\n options: CookieOptions;\n /**\n * Formatted `Set-Cookie` header value string.\n */\n header: string;\n}\n\n/**\n * Default session cookie name.\n * @public\n */\nexport const DEFAULT_SESSION_COOKIE_NAME = \"lixa_session\";\n\n/**\n * Default OAuth state cookie name.\n * @public\n */\nexport const DEFAULT_STATE_COOKIE_NAME = \"lixa_oauth_state\";\n\n/**\n * Default session max age in seconds (24 hours).\n * @public\n */\nexport const DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60; // 24 hours\n\n/**\n * Default OAuth state max age in seconds (5 minutes / 300 seconds).\n * @public\n */\nexport const DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60; // 5 minutes (synchronized with state storage TTL)\n\n/**\n * Checks if the runtime environment is production.\n * @public\n */\nexport function isProductionEnvironment(): boolean {\n return typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n}\n\n/**\n * Serializes a cookie name, value, and options into a standard `Set-Cookie` header string.\n * \n * @param name - Cookie name\n * @param value - Cookie value\n * @param options - Cookie attributes\n * @returns Formatted `Set-Cookie` string\n * \n * @public\n */\nexport function serializeCookie(name: string, value: string, options?: CookieOptions): string {\n const isProd = isProductionEnvironment();\n const path = options?.path ?? \"/\";\n const httpOnly = options?.httpOnly ?? true;\n const secure = options?.secure ?? isProd;\n const sameSite = options?.sameSite ?? \"lax\";\n\n const parts: string[] = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];\n\n if (path) {\n parts.push(`Path=${path}`);\n }\n\n if (typeof options?.maxAge === \"number\") {\n parts.push(`Max-Age=${Math.floor(options.maxAge)}`);\n // Also include Expires for legacy browser compatibility\n const expires = new Date(Date.now() + options.maxAge * 1000).toUTCString();\n parts.push(`Expires=${expires}`);\n }\n\n if (options?.domain) {\n parts.push(`Domain=${options.domain}`);\n }\n\n if (httpOnly) {\n parts.push(\"HttpOnly\");\n }\n\n if (secure) {\n parts.push(\"Secure\");\n }\n\n if (sameSite) {\n const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();\n parts.push(`SameSite=${capitalized}`);\n }\n\n return parts.join(\"; \");\n}\n\n/**\n * Generates a session cookie payload with secure default options.\n * \n * @param sessionId - The session identifier string\n * @param options - Optional overrides for cookie attributes\n * \n * @example\n * ```typescript\n * const cookie = createSessionCookie(sessionId);\n * res.setHeader(\"Set-Cookie\", cookie.header);\n * // or with Express:\n * res.cookie(cookie.name, cookie.value, cookie.options);\n * ```\n * \n * @public\n */\nexport function createSessionCookie(sessionId: string, options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_SESSION_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, sessionId, resolvedOptions);\n\n return {\n name,\n value: sessionId,\n options: resolvedOptions,\n header,\n };\n}\n\n/**\n * Generates an expired session cookie payload to clear the session on logout.\n * \n * @param options - Optional overrides for cookie name or attributes\n * \n * @example\n * ```typescript\n * const cookie = clearSessionCookie();\n * res.setHeader(\"Set-Cookie\", cookie.header);\n * // or with Express:\n * res.clearCookie(cookie.name, cookie.options);\n * ```\n * \n * @public\n */\nexport function clearSessionCookie(options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_SESSION_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: 0,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, \"\", resolvedOptions);\n\n return {\n name,\n value: \"\",\n options: resolvedOptions,\n header,\n };\n}\n\n/**\n * Generates an OAuth CSRF state cookie payload for in-flight authorization flows.\n * \n * @param state - The random state string\n * @param options - Optional overrides for cookie attributes\n * \n * @public\n */\nexport function createStateCookie(state: string, options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_STATE_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, state, resolvedOptions);\n\n return {\n name,\n value: state,\n options: resolvedOptions,\n header,\n };\n}\n\n/**\n * Generates an expired OAuth state cookie payload to clean up the state cookie after callback.\n * \n * @param options - Optional overrides for cookie attributes\n * \n * @public\n */\nexport function clearStateCookie(options?: CookieOptions): CookiePayload {\n const isProd = isProductionEnvironment();\n const resolvedOptions: CookieOptions = {\n name: options?.name || DEFAULT_STATE_COOKIE_NAME,\n path: options?.path ?? \"/\",\n maxAge: 0,\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? isProd,\n sameSite: options?.sameSite ?? \"lax\",\n domain: options?.domain,\n };\n\n const name = resolvedOptions.name!;\n const header = serializeCookie(name, \"\", resolvedOptions);\n\n return {\n name,\n value: \"\",\n options: resolvedOptions,\n header,\n };\n}\n","export * from \"./types\";\nexport * from \"./hasher\";\nexport * from \"./policy\";\nexport * from \"./local-storage\";\nexport * from \"./credentials-manager\";\n"],"mappings":"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","names":["import_crypto","crypto","import_crypto","crypto","AccountLinkingStrategy","import_crypto","NodeCache","import_crypto","import_node_cache","NodeCache","crypto"]}
|