UNPKG

@zestic/oauth-core

Version:

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

184 lines 6.47 kB
"use strict"; /** * Token exchange and management */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TokenManager = void 0; const OAuthTypes_1 = require("../types/OAuthTypes"); const ErrorHandler_1 = require("../utils/ErrorHandler"); class TokenManager { constructor(httpAdapter, storageAdapter) { this.httpAdapter = httpAdapter; this.storageAdapter = storageAdapter; } /** * Exchange authorization code for tokens */ async exchangeAuthorizationCode(code, codeVerifier, config) { const request = { grantType: 'authorization_code', code, redirectUri: config.redirectUri, codeVerifier, clientId: config.clientId, }; return this.performTokenExchange(request, config); } /** * Exchange magic link token for OAuth tokens */ async exchangeMagicLinkToken(token, config, additionalParams) { const request = { grantType: 'magic_link', token, clientId: config.clientId, ...additionalParams, }; return this.performTokenExchange(request, config); } /** * Refresh access token using refresh token */ async refreshToken(refreshToken, config) { const request = { grantType: 'refresh_token', token: refreshToken, clientId: config.clientId, }; return this.performTokenExchange(request, config); } /** * Perform the actual token exchange HTTP request */ async performTokenExchange(request, config) { try { const requestBody = this.buildTokenRequestBody(request); const response = await this.httpAdapter.post(config.endpoints.token, requestBody, { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', }); if (response.status >= 400) { throw ErrorHandler_1.ErrorHandler.handleTokenExchangeError(new Error(`HTTP ${response.status}`), response.data); } const tokenResponse = response.data; // Store tokens await this.storeTokens(tokenResponse); return { success: true, accessToken: tokenResponse.access_token, refreshToken: tokenResponse.refresh_token, expiresIn: tokenResponse.expires_in, }; } catch (error) { if (ErrorHandler_1.ErrorHandler.isOAuthError(error)) { throw error; } throw ErrorHandler_1.ErrorHandler.handleTokenExchangeError(error instanceof Error ? error : new Error(String(error))); } } /** * Build the request body for token exchange */ buildTokenRequestBody(request) { const body = { grant_type: request.grantType, client_id: request.clientId, }; if (request.code) { body.code = request.code; } if (request.redirectUri) { body.redirect_uri = request.redirectUri; } if (request.codeVerifier) { body.code_verifier = request.codeVerifier; } if (request.token) { if (request.grantType === 'refresh_token') { body.refresh_token = request.token; } else { body.token = request.token; } } if (request.state) { body.state = request.state; } return body; } /** * Store tokens in storage */ async storeTokens(tokenResponse) { try { await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.ACCESS_TOKEN, tokenResponse.access_token); if (tokenResponse.refresh_token) { await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.REFRESH_TOKEN, tokenResponse.refresh_token); } if (tokenResponse.expires_in) { const expiryTime = Date.now() + (tokenResponse.expires_in * 1000); await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.TOKEN_EXPIRY, expiryTime.toString()); } await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.TOKEN_TYPE, tokenResponse.token_type || 'Bearer'); } catch (error) { throw ErrorHandler_1.ErrorHandler.createError('Failed to store tokens', OAuthTypes_1.OAUTH_ERROR_CODES.NETWORK_ERROR, error instanceof Error ? error : new Error(String(error))); } } /** * Get stored access token */ async getAccessToken() { return this.storageAdapter.getItem(TokenManager.STORAGE_KEYS.ACCESS_TOKEN); } /** * Get stored refresh token */ async getRefreshToken() { return this.storageAdapter.getItem(TokenManager.STORAGE_KEYS.REFRESH_TOKEN); } /** * Check if access token is expired */ async isTokenExpired() { const expiryTimeStr = await this.storageAdapter.getItem(TokenManager.STORAGE_KEYS.TOKEN_EXPIRY); if (!expiryTimeStr) { return false; // No expiry time stored, assume not expired } const expiryTime = parseInt(expiryTimeStr, 10); return Date.now() >= expiryTime; } /** * Clear all stored tokens */ async clearTokens() { const keys = Object.values(TokenManager.STORAGE_KEYS); await this.storageAdapter.removeItems(keys); } /** * Revoke tokens */ async revokeTokens(config) { try { const accessToken = await this.getAccessToken(); if (accessToken) { await this.httpAdapter.post(config.endpoints.revocation, { token: accessToken, client_id: config.clientId }, { 'Content-Type': 'application/x-www-form-urlencoded' }); } await this.clearTokens(); } catch (error) { // Log error but don't throw - token revocation failure shouldn't prevent logout console.warn('Token revocation failed:', error); await this.clearTokens(); } } } exports.TokenManager = TokenManager; TokenManager.STORAGE_KEYS = { ACCESS_TOKEN: 'access_token', REFRESH_TOKEN: 'refresh_token', TOKEN_EXPIRY: 'token_expiry', TOKEN_TYPE: 'token_type', }; //# sourceMappingURL=TokenManager.js.map