@vunexa/lixa 0.0.1-alpha.8 → 0.1.0

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.
Files changed (52) hide show
  1. package/README.md +263 -143
  2. package/dist/dao/session-cache.d.ts +11 -0
  3. package/dist/dao/session-cache.d.ts.map +1 -0
  4. package/dist/dao/state-cache.d.ts +8 -6
  5. package/dist/dao/state-cache.d.ts.map +1 -1
  6. package/dist/dao/types.d.ts +376 -3
  7. package/dist/dao/types.d.ts.map +1 -1
  8. package/dist/export-types/index.d.ts +1397 -0
  9. package/dist/export-types/tsdoc-metadata.json +11 -0
  10. package/dist/index.cjs +1035 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +1361 -0
  13. package/dist/index.d.ts +11 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +992 -9
  16. package/dist/index.js.map +1 -1
  17. package/dist/lixa.d.ts +286 -19
  18. package/dist/lixa.d.ts.map +1 -1
  19. package/dist/models/session.d.ts +316 -0
  20. package/dist/models/session.d.ts.map +1 -0
  21. package/dist/providers/IProvider.d.ts +127 -4
  22. package/dist/providers/IProvider.d.ts.map +1 -1
  23. package/dist/providers/index.d.ts +0 -2
  24. package/dist/providers/index.d.ts.map +1 -1
  25. package/dist/types.d.ts +195 -24
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/utils/user-info.d.ts +82 -0
  28. package/dist/utils/user-info.d.ts.map +1 -0
  29. package/package.json +15 -10
  30. package/dist/dao/state-cache.js +0 -18
  31. package/dist/dao/state-cache.js.map +0 -1
  32. package/dist/dao/types.js +0 -2
  33. package/dist/dao/types.js.map +0 -1
  34. package/dist/lixa.js +0 -248
  35. package/dist/lixa.js.map +0 -1
  36. package/dist/providers/IProvider.js +0 -2
  37. package/dist/providers/IProvider.js.map +0 -1
  38. package/dist/providers/github.d.ts +0 -9
  39. package/dist/providers/github.d.ts.map +0 -1
  40. package/dist/providers/github.js +0 -8
  41. package/dist/providers/github.js.map +0 -1
  42. package/dist/providers/google.d.ts +0 -9
  43. package/dist/providers/google.d.ts.map +0 -1
  44. package/dist/providers/google.js +0 -8
  45. package/dist/providers/google.js.map +0 -1
  46. package/dist/providers/index.js +0 -3
  47. package/dist/providers/index.js.map +0 -1
  48. package/dist/types.js +0 -2
  49. package/dist/types.js.map +0 -1
  50. package/dist/utils/constants.js +0 -4
  51. package/dist/utils/constants.js.map +0 -1
  52. package/index.d.ts +0 -227
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC"}
1
+ {"version":3,"sources":["../src/lixa.ts","../src/types.ts","../src/dao/state-cache.ts","../src/dao/session-cache.ts","../src/utils/user-info.ts"],"sourcesContent":["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 } 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\";\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 private static LOCAL_STATE_HANDLER = new LocalStateHandler();\n private static LOCAL_SESSION_HANDLER = new LocalSessionHandler();\n private config: TConfig;\n private stateHandler: StateHandler;\n private sessionHandler: SessionHandler;\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.stateHandler = config.stateHandler || Lixa.LOCAL_STATE_HANDLER;\n this.sessionHandler = config.sessionHandler || Lixa.LOCAL_SESSION_HANDLER;\n this.debug = config.debug || false;\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 Error(\n `Provider '${providerName}' is not available. ` +\n `Either import it from '@vunexa/lixa-providers' and include it in the configuration, ` +\n `or provide a custom implementation using the 'provider' field: ` +\n `{ provider: new CustomProvider(), clientId: '...', ... }`\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 Error 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 Error(\n `Provider '${name}' configuration is missing required fields: ${missingFields.join(', ')}`\n );\n }\n \n // Validate scopes is an array\n if (!Array.isArray(config.scopes)) {\n throw new Error(\n `Provider '${name}' configuration error: 'scopes' must be an array of strings`\n );\n }\n \n if (config.scopes.length === 0) {\n throw new Error(\n `Provider '${name}' configuration error: 'scopes' array cannot be empty`\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 Error 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 Error(\n `Provider '${name}' implementation is missing required properties: ${missingProps.join(', ')}. ` +\n `All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`\n );\n }\n }\n\n /**\n * Structured debug logging with standardized format.\n * \n * @param level - Log level (INFO, WARN, ERROR)\n * @param context - Context of the log (Init, Auth, Token, Session, State)\n * @param message - Log message\n * @param data - Optional data to log\n * \n * @remarks\n * Format: [Lixa] [timestamp] [level] [context] message\n * Only logs when debug mode is enabled.\n */\n private log(level: 'INFO' | 'WARN' | 'ERROR', context: 'Init' | 'Auth' | 'Token' | 'Session' | 'State' | 'AccountLinking', message: string, data?: Record<string, string | number | boolean | string[]>): void {\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 Error(\n `Provider '${name}' not found. ` +\n `Ensure the provider is included in the configuration with a 'provider' field, ` +\n `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 Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);\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 if (this.stateHandler.generateState) {\n this.log('INFO', 'State', 'Calling custom GenerateState');\n const generated = await this.stateHandler.generateState(providerType);\n stateValue = state || generated.state;\n codeVerifier = generated.data.codeVerifier;\n \n // Save the generated state data\n const storage = this.stateHandler.stateStorage || Lixa.LOCAL_STATE_HANDLER.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 || Lixa.LOCAL_STATE_HANDLER.stateStorage!;\n await storage.saveState(\n stateValue,\n {\n createdAt: Date.now(),\n provider: providerType,\n codeVerifier,\n },\n 300 // 5 minutes in seconds\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(providerType, providerConfig.scopes, providerImpl);\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.\n */\n private resolveAuthNScopes(\n providerType: string,\n configuredScopes: string[],\n providerImpl: IProvider\n ): string[] {\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 )}]. 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 Error(\"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 Error(\"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 || Lixa.LOCAL_STATE_HANDLER.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 Error(\"Invalid or expired 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 Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);\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 = this.sessionHandler.generateSession || Lixa.LOCAL_SESSION_HANDLER.GenerateSession!.bind(Lixa.LOCAL_SESSION_HANDLER);\n \n if (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 || Lixa.LOCAL_SESSION_HANDLER.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 || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n const existingSession = await sessionStorage.getSession(sessionId);\n\n if (!existingSession) {\n throw new Error(\"Invalid session ID. User must be authenticated to link an account.\");\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new Error(`Provider '${String(provider)}' is not configured`);\n }\n const providerImpl = this.getProvider(providerType, providerConfig);\n\n // Get code verifier if state is provided\n let codeVerifier = randomBytes(32).toString(\"hex\");\n if (state) {\n const stateStorage = this.stateHandler.stateStorage || Lixa.LOCAL_STATE_HANDLER.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (cachedState) {\n codeVerifier = cachedState.codeVerifier;\n await stateStorage.deleteState(state);\n }\n }\n\n const tokens = await this.exchangeCodeForToken(providerType, 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 || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n const session = await sessionStorage.getSession(sessionId);\n\n if (!session || !session.accounts) {\n throw new Error(\"Session not found or has no linked accounts.\");\n }\n\n const linkedProviders = Object.keys(session.accounts);\n if (linkedProviders.length <= 1) {\n throw new Error(\"Cannot unlink the only authentication provider for this account.\");\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 }): Promise<string> {\n const { sessionId, provider, scopes, state } = params;\n const sessionStorage = this.sessionHandler.sessionStorage || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n\n if (!activeSession) {\n throw new Error(\"Authentication required. Active session must exist to connect resource providers.\");\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new Error(`Provider '${String(provider)}' is not configured`);\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 || Lixa.LOCAL_STATE_HANDLER.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 ...providerConfig.extraConfig,\n });\n\n return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;\n }\n\n /**\n * Handles the OAuth callback for a connected resource provider and stores resource tokens on the session.\n * \n * @param params - Object containing sessionId, provider, code, state, and requested scopes\n * @returns Updated Session containing stored resource tokens under session.resources[provider]\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 || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n\n if (!activeSession) {\n throw new Error(\"Authentication required. Active session not found for resource connection.\");\n }\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n if (!providerConfig) {\n throw new Error(`Provider '${String(provider)}' is not configured`);\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 || Lixa.LOCAL_STATE_HANDLER.stateStorage!;\n const cachedState = await stateStorage.getState(state);\n if (cachedState) {\n codeVerifier = cachedState.codeVerifier;\n await stateStorage.deleteState(state);\n }\n }\n\n const tokens = await this.exchangeCodeForToken(providerType, providerConfig, providerImpl, codeVerifier);\n\n activeSession.resources = activeSession.resources || {};\n activeSession.resources[providerType] = {\n provider: providerType,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n scopes: scopes || (tokens.scope ? tokens.scope.split(\" \") : []),\n raw: tokens,\n connectedAt: Date.now(),\n };\n\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n return activeSession;\n }\n\n /**\n * Retrieves a connected resource provider token for an active session.\n * \n * @param sessionId - Active session ID\n * @param provider - Provider identifier (e.g. 'github')\n */\n public async getConnectedResource(sessionId: string, provider: string): Promise<ConnectedResource | null> {\n const sessionStorage = this.sessionHandler.sessionStorage || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (!activeSession || !activeSession.resources) return null;\n return activeSession.resources[provider.toLowerCase()] || null;\n }\n\n /**\n * Disconnects a resource provider from an active session.\n * \n * @param sessionId - Active session ID\n * @param provider - Provider identifier to disconnect\n */\n public async disconnectResource(sessionId: string, provider: string): Promise<boolean> {\n const sessionStorage = this.sessionHandler.sessionStorage || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n const activeSession = await sessionStorage.getSession(sessionId);\n if (!activeSession || !activeSession.resources) return false;\n delete activeSession.resources[provider.toLowerCase()];\n await sessionStorage.saveSession(sessionId, activeSession, 86400);\n return true;\n }\n\n public async fetchSessionInfo(sessionId: string): Promise<Session | null> {\n const sessionStorage = this.sessionHandler.sessionStorage || Lixa.LOCAL_SESSION_HANDLER.sessionStorage!;\n return await sessionStorage.getSession<Session>(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 Error(\n `Token exchange failed: ${response.status} ${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, 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 { 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 /** 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 * 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/**\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","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\nexport { LocalStateHandler };\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 });\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 // Default implementation: just use access token as session token\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\nexport { LocalSessionHandler };\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"],"mappings":"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","names":["randomBytes","AccountLinkingStrategy","NodeCache","randomBytes"]}
package/dist/lixa.d.ts CHANGED
@@ -1,20 +1,27 @@
1
- import { type LixaConfig, type Session } from "./types";
1
+ import { type LixaConfig, type ProviderConfig } from "./types";
2
2
  import { IProvider } from "./providers";
3
+ import { type Session, type ConnectedResource } from "./models/session";
3
4
  /**
4
5
  * Type representing the keys of configured providers
5
6
  */
6
- type ConfiguredProviderKey<T extends LixaConfig> = keyof T['providers'];
7
+ type ConfiguredProviderKey<T extends LixaConfig<Record<string, ProviderConfig>>> = keyof T['providers'];
7
8
  /**
8
9
  * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library.
9
10
  *
10
11
  * @remarks
11
12
  * Lixa simplifies multi-provider authentication flows and supports extensible session management.
13
+ * Providers can be passed inline in the configuration, eliminating the need for pre-registration.
12
14
  *
13
15
  * @example
16
+ * Using built-in providers from \@vunexa/lixa-providers:
14
17
  * ```typescript
18
+ * import { Lixa } from '@vunexa/lixa';
19
+ * import { GoogleProvider } from '@vunexa/lixa-providers';
20
+ *
15
21
  * const lixa = new Lixa({
16
22
  * providers: {
17
23
  * google: {
24
+ * provider: new GoogleProvider(),
18
25
  * clientId: 'your-client-id',
19
26
  * clientSecret: 'your-client-secret',
20
27
  * redirectUri: 'https://yourapp.com/auth/google/callback',
@@ -24,39 +31,131 @@ type ConfiguredProviderKey<T extends LixaConfig> = keyof T['providers'];
24
31
  * });
25
32
  * ```
26
33
  *
34
+ * @example
35
+ * Using custom inline providers:
36
+ * ```typescript
37
+ * import { Lixa, IProvider } from '@vunexa/lixa';
38
+ *
39
+ * const customProvider: IProvider = {
40
+ * authorizationEndpoint: 'https://custom.com/oauth/authorize',
41
+ * tokenEndpoint: 'https://custom.com/oauth/token',
42
+ * userInfoEndpoint: 'https://custom.com/api/user'
43
+ * };
44
+ *
45
+ * const lixa = new Lixa({
46
+ * providers: {
47
+ * custom: {
48
+ * provider: customProvider,
49
+ * clientId: 'your-client-id',
50
+ * clientSecret: 'your-client-secret',
51
+ * redirectUri: 'https://yourapp.com/auth/custom/callback',
52
+ * scopes: ['read:user']
53
+ * }
54
+ * }
55
+ * });
56
+ * ```
57
+ *
27
58
  * @public
28
59
  */
29
- declare class Lixa<TConfig extends LixaConfig = LixaConfig> {
60
+ declare class Lixa<TConfig extends LixaConfig<Record<string, ProviderConfig>> = LixaConfig> {
61
+ private static DEFAULT_PROVIDERS;
30
62
  private static CONFIGURED_PROVIDERS;
31
- private static LOCAL_STATE_CACHE;
63
+ private static LOCAL_STATE_HANDLER;
64
+ private static LOCAL_SESSION_HANDLER;
32
65
  private config;
33
- private stateDao;
66
+ private stateHandler;
67
+ private sessionHandler;
68
+ private debug;
34
69
  /**
35
70
  * Creates a new Lixa instance with the provided configuration.
36
71
  *
72
+ * @remarks
73
+ * Providers can be passed inline in the configuration using the `provider` field.
74
+ * Provider resolution priority: inline custom provider \> default providers \> legacy registry.
75
+ *
37
76
  * @param config - The configuration object containing provider settings and optional session strategy
77
+ *
78
+ * @throws Error when provider configuration is missing required fields
79
+ * @throws Error when provider implementation is missing required properties
80
+ * @throws Error when provider is not available and no inline implementation is provided
38
81
  */
39
82
  constructor(config: TConfig);
40
83
  /**
41
- * Checks if a provider is registered and available for use.
84
+ * Validates that a provider configuration has all required credentials.
85
+ *
86
+ * @param name - The provider name
87
+ * @param config - The provider configuration
88
+ * @throws Error when required fields are missing or invalid
89
+ */
90
+ private validateProviderConfig;
91
+ /**
92
+ * Validates that a provider implementation has all required properties.
93
+ *
94
+ * @param name - The provider name
95
+ * @param provider - The provider implementation
96
+ * @throws Error when required properties are missing
97
+ */
98
+ private validateProviderImplementation;
99
+ /**
100
+ * Structured debug logging with standardized format.
101
+ *
102
+ * @param level - Log level (INFO, WARN, ERROR)
103
+ * @param context - Context of the log (Init, Auth, Token, Session, State)
104
+ * @param message - Log message
105
+ * @param data - Optional data to log
106
+ *
107
+ * @remarks
108
+ * Format: [Lixa] [timestamp] [level] [context] message
109
+ * Only logs when debug mode is enabled.
110
+ */
111
+ private log;
112
+ /**
113
+ * Checks if a provider is configured for this instance.
114
+ * This is a type guard that narrows the provider type for use with getAuthUrl.
42
115
  *
43
116
  * @param provider - The provider name to check (case-insensitive)
44
- * @returns True if the provider is registered, false otherwise
117
+ * @returns True if the provider is configured, false otherwise
45
118
  *
46
119
  * @example
47
120
  * ```typescript
48
- * if (Lixa.isProviderRegistered('google')) {
49
- * console.log('Google provider is available');
121
+ * if (lixa.isProviderConfigured(provider)) {
122
+ * // TypeScript now knows provider is a valid ConfiguredProviderKey
123
+ * const authUrl = lixa.getAuthUrl(provider, state);
50
124
  * }
51
125
  * ```
52
126
  */
53
- static isProviderRegistered(provider: string): boolean;
127
+ isProviderConfigured<T extends string>(provider: T): provider is T & ConfiguredProviderKey<TConfig>;
128
+ /**
129
+ * Gets a provider implementation by name.
130
+ * Resolution priority: inline custom provider \> default providers \> legacy registry
131
+ *
132
+ * @param name - The provider name (case-insensitive)
133
+ * @param config - The provider configuration
134
+ * @returns The provider implementation
135
+ * @throws Error when provider is not found
136
+ */
137
+ private getProvider;
54
138
  /**
55
139
  * Registers custom OAuth providers for use with Lixa.
56
140
  *
141
+ * @deprecated This method is maintained for backward compatibility.
142
+ * The recommended approach is to pass providers inline in the configuration:
143
+ * ```typescript
144
+ * const lixa = new Lixa({
145
+ * providers: {
146
+ * custom: {
147
+ * provider: new CustomProvider(),
148
+ * clientId: '...',
149
+ * // ...
150
+ * }
151
+ * }
152
+ * });
153
+ * ```
154
+ *
57
155
  * @param providerMap - A map of provider names to IProvider implementations
58
156
  *
59
157
  * @example
158
+ * Legacy usage (still supported):
60
159
  * ```typescript
61
160
  * class CustomProvider implements IProvider {
62
161
  * authorizationEndpoint = 'https://custom.com/oauth/authorize';
@@ -67,9 +166,39 @@ declare class Lixa<TConfig extends LixaConfig = LixaConfig> {
67
166
  * Lixa.registerProvider({ custom: new CustomProvider() });
68
167
  * ```
69
168
  */
70
- static registerProvider(providerMap: {
71
- [key: string]: IProvider;
72
- }): void;
169
+ static registerProvider<T extends Record<string, IProvider>>(providerMap: T): void;
170
+ /**
171
+ * Gets the list of registered provider names.
172
+ *
173
+ * @returns Array of registered provider names
174
+ */
175
+ static getRegisteredProviders(): string[];
176
+ /**
177
+ * Creates a type-safe configuration.
178
+ *
179
+ * @deprecated This method is maintained for backward compatibility.
180
+ * You can now pass configuration directly to the Lixa constructor without this helper.
181
+ *
182
+ * @param config - Configuration object with provider settings
183
+ * @returns The same configuration object with type safety
184
+ *
185
+ * @example
186
+ * New approach (recommended):
187
+ * ```typescript
188
+ * const lixa = new Lixa({
189
+ * providers: {
190
+ * google: {
191
+ * provider: new GoogleProvider(),
192
+ * clientId: '...',
193
+ * // ...
194
+ * }
195
+ * }
196
+ * });
197
+ * ```
198
+ */
199
+ static createConfig<T extends Record<string, ProviderConfig>>(config: LixaConfig<T> & {
200
+ providers: T;
201
+ }): LixaConfig<T>;
73
202
  /**
74
203
  * Generates a cryptographically secure random state parameter for OAuth flows.
75
204
  *
@@ -82,12 +211,81 @@ declare class Lixa<TConfig extends LixaConfig = LixaConfig> {
82
211
  /**
83
212
  * Generates a cryptographically secure code verifier for PKCE flows.
84
213
  *
85
- * @returns A 64-character hexadecimal string
214
+ * @returns A 64-character hexadecimal string (32 random bytes encoded as hex)
86
215
  *
87
216
  * @remarks
88
- * The code verifier is used in PKCE (Proof Key for Code Exchange) to enhance security.
217
+ * This method implements the code verifier generation as specified in RFC 7636 (PKCE).
218
+ *
219
+ * **PKCE (Proof Key for Code Exchange)** is a security extension to OAuth 2.0 that
220
+ * prevents authorization code interception attacks. It's especially important for
221
+ * public clients (mobile apps, SPAs) but is recommended for all OAuth flows.
222
+ *
223
+ * **Generation methodology:**
224
+ * 1. Generate 32 cryptographically random bytes using Node.js crypto.randomBytes()
225
+ * 2. Encode the bytes as a hexadecimal string (64 characters)
226
+ * 3. The verifier is stored securely and used later in the token exchange
227
+ *
228
+ * **RFC 7636 Requirements:**
229
+ * - Minimum length: 43 characters
230
+ * - Maximum length: 128 characters
231
+ * - Character set: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
232
+ * - This implementation produces 64 hex characters, meeting the requirements
233
+ *
234
+ * The code verifier is:
235
+ * - Generated when creating the authorization URL
236
+ * - Stored in state cache with the state parameter
237
+ * - Retrieved during callback handling
238
+ * - Sent to the token endpoint to prove the client's identity
239
+ *
240
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}
241
+ * @see buildCodeChallenge for the corresponding challenge generation
242
+ *
243
+ * @internal
89
244
  */
90
245
  private static generateCodeVerifier;
246
+ /**
247
+ * Generates a code challenge from a code verifier for PKCE flows.
248
+ *
249
+ * @param codeVerifier - The code verifier string (64 hex characters)
250
+ * @returns A base64url-encoded SHA-256 hash of the code verifier
251
+ *
252
+ * @remarks
253
+ * This method implements the code challenge generation as specified in RFC 7636 (PKCE)
254
+ * using the S256 (SHA-256) transformation method.
255
+ *
256
+ * **Challenge generation methodology:**
257
+ * 1. Hash the code verifier using SHA-256
258
+ * 2. Encode the hash as base64
259
+ * 3. Convert to base64url format (RFC 4648):
260
+ * - Replace '+' with '-'
261
+ * - Replace '/' with '_'
262
+ * - Remove trailing '=' padding
263
+ *
264
+ * **PKCE Flow:**
265
+ * 1. Client generates code_verifier (random string)
266
+ * 2. Client creates code_challenge = BASE64URL(SHA256(code_verifier))
267
+ * 3. Client sends code_challenge to authorization endpoint
268
+ * 4. Authorization server stores the code_challenge
269
+ * 5. Client sends code_verifier to token endpoint
270
+ * 6. Authorization server verifies: SHA256(code_verifier) == code_challenge
271
+ *
272
+ * **Security Benefits:**
273
+ * - Prevents authorization code interception attacks
274
+ * - Even if an attacker intercepts the authorization code, they cannot
275
+ * exchange it for tokens without the original code_verifier
276
+ * - The challenge is sent in the authorization request (public)
277
+ * - The verifier is sent in the token request (should be kept secret)
278
+ *
279
+ * **RFC 7636 Transformation Methods:**
280
+ * - plain: code_challenge = code_verifier (not recommended)
281
+ * - S256: code_challenge = BASE64URL(SHA256(code_verifier)) (recommended, used here)
282
+ *
283
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}
284
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc4648#section-5 | RFC 4648 - Base64url Encoding}
285
+ * @see generateCodeVerifier for the verifier generation
286
+ *
287
+ * @internal
288
+ */
91
289
  private static buildCodeChallenge;
92
290
  /**
93
291
  * Generates the authorization URL for the specified provider.
@@ -105,20 +303,24 @@ declare class Lixa<TConfig extends LixaConfig = LixaConfig> {
105
303
  * res.redirect(authUrl);
106
304
  * ```
107
305
  */
108
- getAuthUrl(provider: ConfiguredProviderKey<TConfig>, state: string): string;
306
+ getAuthUrl(provider: ConfiguredProviderKey<TConfig> | string, state?: string): Promise<string>;
307
+ /**
308
+ * Restricts primary authentication scopes strictly to AuthN identity scopes.
309
+ */
310
+ private resolveAuthNScopes;
109
311
  /**
110
312
  * Handles the OAuth callback and creates a user session.
111
313
  *
112
314
  * @param provider - The provider name (must be a configured provider key)
113
315
  * @param code - The authorization code from the provider
114
316
  * @param state - The state parameter for validation
115
- * @returns A Promise that resolves to a Session object
317
+ * @returns A Promise that resolves to the session ID
116
318
  *
117
319
  * @throws Error when code or state is missing/invalid, or provider is not configured
118
320
  *
119
321
  * @example
120
322
  * ```typescript
121
- * const session = await lixa.handleCallback({
323
+ * const sessionId = await lixa.handleCallback({
122
324
  * provider: 'google',
123
325
  * code: req.query.code,
124
326
  * state: req.query.state
@@ -126,10 +328,75 @@ declare class Lixa<TConfig extends LixaConfig = LixaConfig> {
126
328
  * ```
127
329
  */
128
330
  handleCallback({ provider, code, state, }: {
129
- provider: ConfiguredProviderKey<TConfig>;
331
+ provider: ConfiguredProviderKey<TConfig> | string;
130
332
  code: string;
131
333
  state?: string;
334
+ }): Promise<string>;
335
+ /**
336
+ * Explicitly link a new OAuth provider account to an active session.
337
+ *
338
+ * @param params - Object containing sessionId, provider, code, and optional state
339
+ * @returns The active session ID with the newly linked provider
340
+ */
341
+ linkAccount(params: {
342
+ sessionId: string;
343
+ provider: ConfiguredProviderKey<TConfig> | string;
344
+ code: string;
345
+ state?: string;
346
+ }): Promise<string>;
347
+ /**
348
+ * Unlinks an OAuth provider account from an active session.
349
+ *
350
+ * @param sessionId - Active session ID
351
+ * @param providerToUnlink - Provider name to unlink (e.g. 'github')
352
+ * @returns Promise resolving to true on successful unlink
353
+ */
354
+ unlinkAccount(sessionId: string, providerToUnlink: string): Promise<boolean>;
355
+ /**
356
+ * Generates an authorization URL for connecting a resource provider (AuthZ) post-login.
357
+ *
358
+ * @remarks
359
+ * Resource authorization is kept strictly separate from primary authentication (AuthN).
360
+ * Call this method after a user is authenticated to request permissions for external API access
361
+ * (e.g. GitHub repositories, Google Drive, Slack, etc.).
362
+ *
363
+ * @param params - Object containing sessionId, provider, requested resource scopes, and optional state
364
+ * @returns The authorization URL for resource consent
365
+ */
366
+ getResourceAuthUrl(params: {
367
+ sessionId: string;
368
+ provider: ConfiguredProviderKey<TConfig> | string;
369
+ scopes: string[];
370
+ state?: string;
371
+ }): Promise<string>;
372
+ /**
373
+ * Handles the OAuth callback for a connected resource provider and stores resource tokens on the session.
374
+ *
375
+ * @param params - Object containing sessionId, provider, code, state, and requested scopes
376
+ * @returns Updated Session containing stored resource tokens under session.resources[provider]
377
+ */
378
+ handleResourceCallback(params: {
379
+ sessionId: string;
380
+ provider: ConfiguredProviderKey<TConfig> | string;
381
+ code: string;
382
+ state?: string;
383
+ scopes?: string[];
132
384
  }): Promise<Session>;
385
+ /**
386
+ * Retrieves a connected resource provider token for an active session.
387
+ *
388
+ * @param sessionId - Active session ID
389
+ * @param provider - Provider identifier (e.g. 'github')
390
+ */
391
+ getConnectedResource(sessionId: string, provider: string): Promise<ConnectedResource | null>;
392
+ /**
393
+ * Disconnects a resource provider from an active session.
394
+ *
395
+ * @param sessionId - Active session ID
396
+ * @param provider - Provider identifier to disconnect
397
+ */
398
+ disconnectResource(sessionId: string, provider: string): Promise<boolean>;
399
+ fetchSessionInfo(sessionId: string): Promise<Session | null>;
133
400
  private exchangeCodeForToken;
134
401
  private findProviderByType;
135
402
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lixa.d.ts","sourceRoot":"","sources":["../src/lixa.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAuB,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAkC,MAAM,aAAa,CAAC;AAMxE;;GAEG;AACH,KAAK,qBAAqB,CAAC,CAAC,SAAS,UAAU,IAAI,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,cAAM,IAAI,CAAC,OAAO,SAAS,UAAU,GAAG,UAAU;IAChD,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAqC;IACxE,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAyB;IACzD,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,QAAQ,CAAW;IAE3B;;;;OAIG;gBACS,MAAM,EAAE,OAAO;IAW3B;;;;;;;;;;;;OAYG;WACW,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI7D;;;;;;;;;;;;;;;OAeG;WACW,gBAAgB,CAAC,WAAW,EAAE;QAC1C,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;KAC1B,GAAG,IAAI;IAQR;;;;;;;OAOG;WACW,mBAAmB,IAAI,MAAM;IAI3C;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAInC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAUjC;;;;;;;;;;;;;;;OAeG;IACI,UAAU,CAAC,QAAQ,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAsClF;;;;;;;;;;;;;;;;;;OAkBG;IACU,cAAc,CAAC,EAC1B,QAAQ,EACR,IAAI,EACJ,KAAK,GACN,EAAE;QACD,QAAQ,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,OAAO,CAAC;YAmDN,oBAAoB;IAsClC,OAAO,CAAC,kBAAkB;CAG3B;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"lixa.d.ts","sourceRoot":"","sources":["../src/lixa.ts"],"names":[],"mappings":"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"}