UNPKG

@zestic/oauth-core

Version:

Framework-agnostic OAuth authentication library with support for multiple OAuth flows

209 lines 8.79 kB
"use strict"; /** * Main OAuth orchestrator */ Object.defineProperty(exports, "__esModule", { value: true }); exports.OAuthCore = void 0; const CallbackFlowRegistry_1 = require("./CallbackFlowRegistry"); const PKCEManager_1 = require("./PKCEManager"); const TokenManager_1 = require("./TokenManager"); const StateValidator_1 = require("./StateValidator"); const OAuthTypes_1 = require("../types/OAuthTypes"); const ErrorHandler_1 = require("../utils/ErrorHandler"); const UrlParser_1 = require("../utils/UrlParser"); // Note: Flow handlers are now registered manually by the user class OAuthCore { constructor(config, adapters, flowConfig) { this.config = config; this.adapters = adapters; this.flowRegistry = new CallbackFlowRegistry_1.CallbackFlowRegistry(); this.pkceManager = new PKCEManager_1.PKCEManager(adapters.pkce, adapters.storage); this.tokenManager = new TokenManager_1.TokenManager(adapters.http, adapters.storage); this.stateValidator = new StateValidator_1.StateValidator(adapters.storage); this.initializeFlows(flowConfig); } /** * Handle OAuth callback with automatic flow detection */ async handleCallback(params, explicitFlow) { try { // Parse parameters if string provided const urlParams = typeof params === 'string' ? UrlParser_1.UrlParser.parseParams(params) : params; // Log callback attempt (with sanitized parameters) console.log('[OAuthCore] Handling callback:', UrlParser_1.UrlParser.sanitizeForLogging(urlParams)); // Validate that we have at least one flow handler registered if (this.flowRegistry.getHandlerCount() === 0) { throw ErrorHandler_1.ErrorHandler.handleInvalidConfiguration('No flow handlers registered. Use registerFlow() to register at least one flow handler.'); } let handler; // Try explicit flow first if specified if (explicitFlow) { handler = this.flowRegistry.getHandler(explicitFlow); if (!handler) { throw ErrorHandler_1.ErrorHandler.handleUnknownFlow(explicitFlow); } } else { // Auto-detect flow using registered handlers handler = this.flowRegistry.detectFlow(urlParams, this.config); } if (!handler) { throw ErrorHandler_1.ErrorHandler.handleNoFlowHandler(); } console.log(`[OAuthCore] Using flow handler: ${handler.name}`); // Validate parameters before handling if (!(await handler.validate(urlParams, this.config))) { throw ErrorHandler_1.ErrorHandler.handleFlowValidationFailed(handler.name); } // Handle the flow const result = await handler.handle(urlParams, this.adapters, this.config); console.log(`[OAuthCore] Flow ${handler.name} completed:`, { success: result.success, hasAccessToken: !!result.accessToken, hasRefreshToken: !!result.refreshToken, }); return result; } catch (error) { console.error('[OAuthCore] Callback handling failed:', ErrorHandler_1.ErrorHandler.formatError(error)); if (ErrorHandler_1.ErrorHandler.isOAuthError(error)) { throw error; } throw ErrorHandler_1.ErrorHandler.createError(`OAuth callback handling failed: ${error instanceof Error ? error.message : String(error)}`, OAuthTypes_1.OAUTH_ERROR_CODES.TOKEN_EXCHANGE_FAILED, error instanceof Error ? error : undefined); } } /** * Generate PKCE challenge for authorization request */ async generatePKCEChallenge() { return this.pkceManager.generateChallenge(); } /** * Generate OAuth state parameter */ async generateState() { const state = await this.pkceManager.generateState(); await this.stateValidator.storeState(state); return state; } /** * Generate complete authorization URL with PKCE parameters * This method handles all OAuth logic including PKCE generation and state management */ async generateAuthorizationUrl(additionalParams) { try { // Generate and store PKCE challenge const pkceChallenge = await this.generatePKCEChallenge(); // Generate and store state const state = await this.generateState(); // Build authorization URL parameters const params = new URLSearchParams({ response_type: 'code', client_id: this.config.clientId, redirect_uri: this.config.redirectUri, scope: this.config.scopes.join(' '), state, code_challenge: pkceChallenge.codeChallenge, code_challenge_method: pkceChallenge.codeChallengeMethod, ...additionalParams, }); const url = `${this.config.endpoints.authorization}?${params.toString()}`; console.log('[OAuthCore] Generated authorization URL with PKCE parameters'); return { url, state }; } catch (error) { console.error('[OAuthCore] Failed to generate authorization URL:', ErrorHandler_1.ErrorHandler.formatError(error)); throw ErrorHandler_1.ErrorHandler.createError(`Failed to generate authorization URL: ${error instanceof Error ? error.message : String(error)}`, OAuthTypes_1.OAUTH_ERROR_CODES.MISSING_PKCE, error instanceof Error ? error : undefined); } } /** * Get current access token */ async getAccessToken() { return this.tokenManager.getAccessToken(); } /** * Get current refresh token */ async getRefreshToken() { return this.tokenManager.getRefreshToken(); } /** * Check if current token is expired */ async isTokenExpired() { return this.tokenManager.isTokenExpired(); } /** * Refresh access token using refresh token */ async refreshAccessToken() { const refreshToken = await this.tokenManager.getRefreshToken(); if (!refreshToken) { throw ErrorHandler_1.ErrorHandler.createError('No refresh token available', OAuthTypes_1.OAUTH_ERROR_CODES.MISSING_REQUIRED_PARAMETER); } return this.tokenManager.refreshToken(refreshToken, this.config); } /** * Revoke tokens and clear storage */ async logout() { await this.tokenManager.revokeTokens(this.config); await this.pkceManager.clearPKCEData(); await this.stateValidator.clearState(); } /** * Register a custom flow handler */ registerFlow(handler) { this.flowRegistry.register(handler); } /** * Unregister a flow handler */ unregisterFlow(name) { this.flowRegistry.unregister(name); } /** * Get all registered flow handlers */ getRegisteredFlows() { return this.flowRegistry.getAllHandlers(); } /** * Get compatible handlers for given parameters */ getCompatibleHandlers(params) { const urlParams = typeof params === 'string' ? UrlParser_1.UrlParser.parseParams(params) : params; return this.flowRegistry.getCompatibleHandlers(urlParams, this.config); } /** * Initialize flow handlers based on configuration */ initializeFlows(flowConfig) { // No built-in flows are automatically registered // Users must manually register the specific flow handlers they need: // - MagicLinkLoginFlowHandler for login flows // - MagicLinkVerifyFlowHandler for verification flows // Register custom flows if provided if (flowConfig?.customFlows) { for (const handler of flowConfig.customFlows) { this.flowRegistry.register(handler); } } // If enabledFlows is specified, remove all others if (flowConfig?.enabledFlows) { const allHandlers = this.flowRegistry.getAllHandlers(); for (const handler of allHandlers) { if (!flowConfig.enabledFlows.includes(handler.name)) { this.flowRegistry.unregister(handler.name); } } } // Note: Flow handlers must be manually registered after initialization // The validation will happen when handleCallback is called console.log('[OAuthCore] Initialized. Flow handlers must be registered manually.'); } } exports.OAuthCore = OAuthCore; //# sourceMappingURL=OAuthCore.js.map