UNPKG

oauth-connector

Version:

OAuth Connector SDK for automatic token management and refresh with multiple persistence strategies

1,173 lines (1,160 loc) 36.7 kB
'use strict'; var crypto = require('crypto'); var util = require('util'); var fs = require('fs'); var path = require('path'); var https = require('https'); var http = require('http'); var url = require('url'); // src/utils/logger.ts var LogLevel = /* @__PURE__ */ ((LogLevel2) => { LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG"; LogLevel2[LogLevel2["INFO"] = 1] = "INFO"; LogLevel2[LogLevel2["WARN"] = 2] = "WARN"; LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR"; return LogLevel2; })(LogLevel || {}); var Logger = class { constructor(level = 1 /* INFO */, prefix = "") { this.level = level; this.prefix = prefix; } /** * Set log level */ setLevel(level) { this.level = level; } /** * Set prefix for log messages */ setPrefix(prefix) { this.prefix = prefix; } /** * Get formatted timestamp */ getTimestamp() { return (/* @__PURE__ */ new Date()).toISOString(); } /** * Format log message with timestamp */ formatMessage(level, message) { const timestamp = this.getTimestamp(); const prefixPart = this.prefix ? ` [${this.prefix}]` : ""; return `[${timestamp}] [${level}]${prefixPart} ${message}`; } /** * Debug log */ debug(message, ...args) { if (this.level <= 0 /* DEBUG */) { console.debug(this.formatMessage("DEBUG", message), ...args); } } /** * Info log */ info(message, ...args) { if (this.level <= 1 /* INFO */) { console.info(this.formatMessage("INFO", message), ...args); } } /** * Warn log */ warn(message, ...args) { if (this.level <= 2 /* WARN */) { console.warn(this.formatMessage("WARN", message), ...args); } } /** * Error log */ error(message, ...args) { if (this.level <= 3 /* ERROR */) { console.error(this.formatMessage("ERROR", message), ...args); } } }; var defaultLogger = new Logger(); // src/token-manager.ts var TokenManager = class { // In-memory cache constructor(oauthService, storageStrategy, backgroundSyncIntervalInSecs, graceExpiryTimeInSecs, logger) { // seconds before expiry to refresh this.cachedTokenData = null; this.oauthService = oauthService; this.storageStrategy = storageStrategy; this.backgroundSyncIntervalSecs = backgroundSyncIntervalInSecs; this.graceExpiryTimeSecs = graceExpiryTimeInSecs || 0; this.logger = logger || new Logger(1 /* INFO */); } /** * Check if token is expired or within grace period */ isTokenExpired(tokenData) { if (!tokenData.expiresAt) { return true; } const now = Date.now(); const gracePeriodMs = this.graceExpiryTimeSecs * 1e3; const effectiveExpiryTime = tokenData.expiresAt - gracePeriodMs; return now >= effectiveExpiryTime; } /** * Get valid access token, refreshing if necessary * Checks in-memory cache first for performance */ async getAccessToken() { try { if (this.cachedTokenData && !this.isTokenExpired(this.cachedTokenData)) { this.logger.debug("Returning token from cache"); return this.cachedTokenData.accessToken; } let tokenData = null; if (this.storageStrategy) { tokenData = await this.storageStrategy.loadToken(); } else { tokenData = this.cachedTokenData; } if (!tokenData || this.isTokenExpired(tokenData)) { this.logger.debug("Token expired or missing, refreshing..."); const refreshToken = tokenData?.refreshToken || this.oauthService.getRefreshToken(); if (!refreshToken) { throw new Error("No refresh token available. Please re-authenticate."); } tokenData = await this.oauthService.refreshAccessToken(refreshToken); if (this.storageStrategy) { await this.storageStrategy.saveToken(tokenData); } this.logger.debug("Token refreshed and saved"); } this.cachedTokenData = tokenData; this.logger.debug("Token cached in memory"); return tokenData.accessToken; } catch (error) { this.cachedTokenData = null; this.logger.error( `Failed to get access token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Manually refresh token */ async refreshToken() { try { let tokenData = null; if (this.storageStrategy) { tokenData = await this.storageStrategy.loadToken(); } else { tokenData = this.cachedTokenData; } const refreshToken = tokenData?.refreshToken || this.oauthService.getRefreshToken(); if (!refreshToken) { throw new Error("No refresh token available. Please re-authenticate."); } this.logger.debug("Manually refreshing token..."); const newTokenData = await this.oauthService.refreshAccessToken(refreshToken); if (this.storageStrategy) { await this.storageStrategy.saveToken(newTokenData); } this.cachedTokenData = newTokenData; this.logger.debug("Token manually refreshed and saved"); return newTokenData; } catch (error) { this.cachedTokenData = null; this.logger.error( `Failed to refresh token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Start background token sync */ startBackgroundSync() { if (this.backgroundSyncInterval) { this.logger.debug("Background sync already running"); return; } if (!this.backgroundSyncIntervalSecs) { this.logger.debug("Background sync is not configured"); return; } this.logger.debug( `Starting background sync (interval: ${this.backgroundSyncIntervalSecs} seconds)` ); const intervalMs = this.backgroundSyncIntervalSecs * 1e3; this.backgroundSyncInterval = setInterval(async () => { try { this.logger.debug("Background sync: checking token..."); let tokenData = this.cachedTokenData; if (!tokenData && this.storageStrategy) { tokenData = await this.storageStrategy.loadToken(); } if (tokenData && this.isTokenExpired(tokenData)) { this.logger.debug("Background sync: token expired, refreshing..."); const refreshToken = tokenData.refreshToken || this.oauthService.getRefreshToken(); if (refreshToken) { const newTokenData = await this.oauthService.refreshAccessToken(refreshToken); if (this.storageStrategy) { await this.storageStrategy.saveToken(newTokenData); } this.cachedTokenData = newTokenData; this.logger.debug("Background sync: token refreshed successfully"); } else { this.logger.warn("Background sync: no refresh token available"); } } else { this.logger.debug("Background sync: token still valid"); } } catch (error) { this.cachedTokenData = null; this.logger.error( `Background sync error: ${error instanceof Error ? error.message : "Unknown error"}` ); } }, intervalMs); } /** * Stop background token sync */ stopBackgroundSync() { if (this.backgroundSyncInterval) { clearInterval(this.backgroundSyncInterval); this.backgroundSyncInterval = void 0; this.logger.debug("Background sync stopped"); } } /** * Get current token data (uses cache) */ async getTokenData() { if (this.cachedTokenData) { return this.cachedTokenData; } if (this.storageStrategy) { const tokenData = await this.storageStrategy.loadToken(); if (tokenData) { this.cachedTokenData = tokenData; } return tokenData; } return null; } /** * Set cached token (used after exchangeCodeForTokens) */ setCachedToken(tokenData) { this.cachedTokenData = tokenData; this.logger.debug("Token cached in memory"); } /** * Clear cached token */ clearCache() { this.cachedTokenData = null; this.logger.debug("Token cache cleared"); } /** * Cleanup resources */ destroy() { this.stopBackgroundSync(); this.clearCache(); } }; // src/connector.ts var Connector = class { constructor(oauthService, storageStrategy, options = {}) { this.oauthService = oauthService; this.storageStrategy = storageStrategy; const logLevel = options.debug ? 0 /* DEBUG */ : 1 /* INFO */; this.logger = new Logger(logLevel, options.instanceId); if (options.instanceId && storageStrategy && "setInstanceId" in storageStrategy) { storageStrategy.setInstanceId(options.instanceId); } const backgroundSyncIntervalInSecs = options.backgroundSyncIntervalInSecs; const graceExpiryTimeInSecs = options.graceExpiryTimeInSecs; this.tokenManager = new TokenManager( this.oauthService, this.storageStrategy, backgroundSyncIntervalInSecs, graceExpiryTimeInSecs, this.logger ); if (backgroundSyncIntervalInSecs) { this.tokenManager.startBackgroundSync(); } this.logger.debug(`Connector initialized (instance: ${options.instanceId || "default"})`); } /** * Get access token (auto-refreshes if expired) */ async getAccessToken() { try { if (this.onFetchingAccessToken) { await this.onFetchingAccessToken(); } const token = await this.tokenManager.getAccessToken(); const tokenData = await this.tokenManager.getTokenData(); if (tokenData) { const timeUntilExpiry = tokenData.expiresAt - Date.now(); if (timeUntilExpiry > 25 * 60 * 1e3) { if (this.onTokenRefreshed) { await this.onTokenRefreshed(tokenData); } } } return token; } catch (error) { const err = error instanceof Error ? error : new Error("Unknown error"); if (this.onTokenError) { await this.onTokenError(err); } this.logger.error(`Failed to get access token: ${err.message}`); throw err; } } /** * Manually refresh token */ async refreshToken() { try { if (this.onFetchingAccessToken) { await this.onFetchingAccessToken(); } const tokenData = await this.tokenManager.refreshToken(); if (this.onTokenRefreshed) { await this.onTokenRefreshed(tokenData); } return tokenData; } catch (error) { const err = error instanceof Error ? error : new Error("Unknown error"); if (this.onTokenError) { await this.onTokenError(err); } this.logger.error(`Failed to refresh token: ${err.message}`); throw err; } } /** * Get current token data */ async getTokenData() { return await this.tokenManager.getTokenData(); } /** * Get authorization URL for initial OAuth flow */ getAuthorizationUrl(redirectUri, scopes) { return this.oauthService.getAuthorizationUrl(redirectUri, scopes); } /** * Exchange authorization code for tokens */ async exchangeCodeForTokens(code, redirectUri) { try { this.logger.debug("Exchanging authorization code for tokens"); const tokenData = await this.oauthService.exchangeCodeForTokens(code, redirectUri); if (this.storageStrategy) { await this.storageStrategy.saveToken(tokenData); } this.tokenManager.setCachedToken(tokenData); this.logger.debug("Tokens obtained and saved"); return tokenData; } catch (error) { this.logger.error( `Failed to exchange code for tokens: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Delete stored token */ async deleteToken() { try { if (this.storageStrategy) { await this.storageStrategy.deleteToken(); } this.tokenManager.clearCache(); this.logger.debug("Token deleted"); } catch (error) { this.logger.error( `Failed to delete token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Start background sync */ startBackgroundSync() { this.tokenManager.startBackgroundSync(); } /** * Stop background sync */ stopBackgroundSync() { this.tokenManager.stopBackgroundSync(); } /** * Cleanup resources */ destroy() { this.tokenManager.destroy(); this.logger.debug("Connector destroyed"); } }; var scryptAsync = util.promisify(crypto.scrypt); var EncryptionService = class { constructor() { this.algorithm = "aes-256-gcm"; this.keyLength = 32; // 256 bits this.ivLength = 16; // 128 bits this.saltLength = 16; this.tagLength = 16; } /** * Derive key from password using scrypt */ async deriveKey(password, salt) { return await scryptAsync(password, salt, this.keyLength); } /** * Encrypt data */ async encrypt(data, password) { try { const salt = crypto.randomBytes(this.saltLength); const iv = crypto.randomBytes(this.ivLength); const key = await this.deriveKey(password, salt); const cipher = crypto.createCipheriv(this.algorithm, key, iv); let encrypted = cipher.update(data, "utf8", "hex"); encrypted += cipher.final("hex"); const tag = cipher.getAuthTag(); const result = Buffer.concat([salt, iv, tag, Buffer.from(encrypted, "hex")]).toString( "base64" ); return result; } catch (error) { throw new Error( `Encryption failed: ${error instanceof Error ? error.message : "Unknown error"}` ); } } /** * Decrypt data */ async decrypt(encryptedData, password) { try { const data = Buffer.from(encryptedData, "base64"); const salt = data.subarray(0, this.saltLength); const iv = data.subarray(this.saltLength, this.saltLength + this.ivLength); const tag = data.subarray( this.saltLength + this.ivLength, this.saltLength + this.ivLength + this.tagLength ); const encrypted = data.subarray(this.saltLength + this.ivLength + this.tagLength); const key = await this.deriveKey(password, salt); const decipher = crypto.createDecipheriv(this.algorithm, key, iv); decipher.setAuthTag(tag); let decrypted = decipher.update(encrypted, void 0, "utf8"); decrypted += decipher.final("utf8"); return decrypted; } catch (error) { throw new Error( `Decryption failed: ${error instanceof Error ? error.message : "Unknown error"}` ); } } }; var defaultEncryptionService = new EncryptionService(); // src/strategies/storage-strategy.ts var StorageStrategy = class { constructor(encryptionKey, logger) { this.encryptionKey = encryptionKey; this.encryptionService = defaultEncryptionService; this.logger = logger || defaultLogger; } /** * Encrypt token data if encryption key is provided */ async encryptIfNeeded(data) { if (this.encryptionKey) { return await this.encryptionService.encrypt(data, this.encryptionKey); } return data; } /** * Decrypt token data if encryption key is provided */ async decryptIfNeeded(data) { if (this.encryptionKey) { return await this.encryptionService.decrypt(data, this.encryptionKey); } return data; } }; var LocalStorageStrategy = class extends StorageStrategy { constructor(config, logger) { super(config.encryptionKey, logger); this.filePath = config.filePath; } /** * Save token to local file */ async saveToken(tokenData) { try { this.logger.debug(`Saving token to local file: ${this.filePath}`); const data = JSON.stringify(tokenData); const encrypted = await this.encryptIfNeeded(data); const dir = path.dirname(this.filePath); await fs.promises.mkdir(dir, { recursive: true }); await fs.promises.writeFile(this.filePath, encrypted, "utf8"); this.logger.debug("Token saved successfully"); } catch (error) { this.logger.error( `Failed to save token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Load token from local file */ async loadToken() { try { this.logger.debug(`Loading token from local file: ${this.filePath}`); const data = await fs.promises.readFile(this.filePath, "utf8"); const decrypted = await this.decryptIfNeeded(data); const tokenData = JSON.parse(decrypted); this.logger.debug("Token loaded successfully"); return tokenData; } catch (error) { if (error.code === "ENOENT") { this.logger.debug("Token file does not exist"); return null; } this.logger.error( `Failed to load token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Delete token file */ async deleteToken() { try { this.logger.debug(`Deleting token file: ${this.filePath}`); await fs.promises.unlink(this.filePath); this.logger.debug("Token file deleted successfully"); } catch (error) { if (error.code === "ENOENT") { this.logger.debug("Token file does not exist, nothing to delete"); return; } this.logger.error( `Failed to delete token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } }; // src/strategies/remote-storage-strategy.ts var RemoteStorageStrategy = class extends StorageStrategy { constructor(config, logger) { super(config.encryptionKey, logger); this.onUpload = config.onUpload; this.onDownload = config.onDownload; } /** * Save token using remote upload callback */ async saveToken(tokenData) { try { this.logger.debug("Saving token to remote storage"); let dataToUpload = tokenData; if (this.encryptionKey) { const dataString = JSON.stringify(tokenData); const encrypted = await this.encryptIfNeeded(dataString); dataToUpload = { _encrypted: true, _data: encrypted }; } await this.onUpload(dataToUpload); this.logger.debug("Token saved to remote storage successfully"); } catch (error) { this.logger.error( `Failed to save token to remote storage: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Load token using remote download callback */ async loadToken() { try { this.logger.debug("Loading token from remote storage"); const tokenData = await this.onDownload(); if (!tokenData) { this.logger.debug("No token found in remote storage"); return null; } const encryptedData = tokenData; if (this.encryptionKey && encryptedData._encrypted && encryptedData._data) { try { const decrypted = await this.decryptIfNeeded(encryptedData._data); const parsed = JSON.parse(decrypted); this.logger.debug("Token decrypted and loaded from remote storage successfully"); return parsed; } catch (error) { this.logger.error( `Failed to decrypt token: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } this.logger.debug("Token loaded from remote storage successfully"); return tokenData; } catch (error) { this.logger.error( `Failed to load token from remote storage: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Delete token from remote storage * Note: This requires onUpload to handle deletion (upload null/empty) */ async deleteToken() { try { this.logger.debug("Deleting token from remote storage"); await this.onUpload(null); this.logger.debug("Token deleted from remote storage successfully"); } catch (error) { this.logger.error( `Failed to delete token from remote storage: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } }; var HttpClient = class { constructor(logger) { this.logger = logger || defaultLogger; } /** * Make HTTP/HTTPS request */ async request(url$1, options = {}) { return new Promise((resolve, reject) => { try { const urlObj = new url.URL(url$1); const isHttps = urlObj.protocol === "https:"; const requestModule = isHttps ? https.request : http.request; const requestOptions = { hostname: urlObj.hostname, port: urlObj.port || (isHttps ? 443 : 80), path: urlObj.pathname + urlObj.search, method: options.method || "GET", headers: { ...options.headers } }; if (options.body) { requestOptions.headers = { ...requestOptions.headers, "Content-Length": Buffer.byteLength(options.body) }; } if (options.timeout) { requestOptions.timeout = options.timeout; } this.logger.debug(`Making ${requestOptions.method} request to: ${url$1}`); const req = requestModule(requestOptions, (res) => { let data = ""; res.on("data", (chunk) => { data += chunk; }); res.on("end", () => { try { const response = { statusCode: res.statusCode || 500, statusMessage: res.statusMessage || "Unknown", headers: res.headers, data: this.parseResponse(data, res.headers["content-type"]) }; if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { this.logger.debug(`Request successful: ${res.statusCode}`); resolve(response); } else { const error = new Error( `HTTP ${res.statusCode}: ${res.statusMessage || "Unknown error"} - ${data}` ); this.logger.error(`Request failed: ${error.message}`); reject(error); } } catch (parseError) { const error = new Error( `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}` ); this.logger.error(`Parse error: ${error.message}`); reject(error); } }); }); req.on("error", (error) => { this.logger.error(`Request error: ${error.message}`); reject(error); }); req.on("timeout", () => { req.destroy(); const error = new Error(`Request timeout after ${options.timeout}ms`); this.logger.error(`Request timeout: ${error.message}`); reject(error); }); if (options.body) { req.write(options.body); } req.end(); } catch (error) { const err = error instanceof Error ? error : new Error("Unknown error"); this.logger.error(`Request setup error: ${err.message}`); reject(err); } }); } /** * Make POST request */ async post(url, body, headers) { return this.request(url, { method: "POST", headers, body }); } /** * Make GET request */ async get(url, headers) { return this.request(url, { method: "GET", headers }); } /** * Parse response data based on content type */ parseResponse(data, contentType) { if (!data) { return {}; } const contentTypeStr = Array.isArray(contentType) ? contentType[0] : contentType; if (contentTypeStr && contentTypeStr.includes("application/json")) { try { return JSON.parse(data); } catch (error) { this.logger.warn("Failed to parse JSON response, returning raw string"); return data; } } if (data.trim().startsWith("{") || data.trim().startsWith("[")) { try { return JSON.parse(data); } catch { return data; } } return data; } }; // src/strategies/catalyst-cache-storage-strategy.ts var CatalystCacheStorageStrategy = class extends StorageStrategy { constructor(config, logger) { super(config?.encryptionKey, logger); this.key = "token"; this.httpReq = config?.httpReq; this.key = config?.key ?? "token"; this.httpClient = new HttpClient(this.logger); } getApiUrl() { const apiDomain = process.env.X_ZOHO_CATALYST_CONSOLE_URL; const projectId = process.env.CATALYST_PROJECT_ID; const apiPath = `/baas/v1/project/${projectId}/segment/Default/cache`; return apiDomain + apiPath; } getReqHeaders() { const oauthToken = this.httpReq?.headers["x-zc-user-cred-token"] || this.httpReq?.catalystHeaders?.["x-zc-user-cred-token"]; const projectKey = this.httpReq?.headers["x-zc-project-key"] || this.httpReq?.catalystHeaders?.["x-zc-project-key"]; return { Authorization: `Bearer ${oauthToken}`, PROJECT_ID: projectKey, "Content-Type": "application/json" }; } async getCache(key) { try { const apiUrl = this.getApiUrl() + `?cacheKey=${key}`; const reqHeaders = this.getReqHeaders(); const response = await this.httpClient.get(apiUrl, reqHeaders); if (response.data && response.data.status === "success" && response.data.data) { return response.data.data.cache_value; } return null; } catch (error) { this.logger.error( `Failed to get cache: ${error instanceof Error ? error.message : "Unknown error"}` ); return null; } } async setCache(key, value) { try { const apiUrl = this.getApiUrl(); const reqHeaders = this.getReqHeaders(); const reqBody = { cache_name: key, cache_value: value, expiry_in_hours: "1" }; await this.httpClient.post( apiUrl, JSON.stringify(reqBody), reqHeaders ); } catch (error) { this.logger.error( `Failed to set cache: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Save token to Catalyst cache */ async saveToken(tokenData) { if (!this.httpReq) { this.logger.warn("HTTP request object is null and strategy is not supported"); return; } try { this.logger.debug(`Saving token to Catalyst cache with key: ${this.key}`); const dataString = JSON.stringify(tokenData); const dataToStore = await this.encryptIfNeeded(dataString); await this.setCache(this.key, dataToStore); this.logger.debug("Token saved to Catalyst cache successfully"); } catch (error) { this.logger.error( `Failed to save token to Catalyst cache: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Load token from Catalyst cache */ async loadToken() { if (!this.httpReq) { this.logger.warn("HTTP request object is null and strategy is not supported"); return null; } try { this.logger.debug(`Loading token from Catalyst cache with key: ${this.key}`); const cachedValue = await this.getCache(this.key); if (!cachedValue) { this.logger.debug("No token found in Catalyst cache"); return null; } const decrypted = await this.decryptIfNeeded(cachedValue); const tokenData = JSON.parse(decrypted); this.logger.debug("Token loaded from Catalyst cache successfully"); return tokenData; } catch (error) { if (error instanceof SyntaxError) { this.logger.error("Failed to parse token data from Catalyst cache"); return null; } this.logger.error( `Failed to load token from Catalyst cache: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Delete token from Catalyst cache */ async deleteToken() { if (!this.httpReq) { this.logger.warn("HTTP request object is null and strategy is not supported"); return; } try { this.logger.debug(`Deleting token from Catalyst cache with key: ${this.key}`); await this.setCache(this.key, ""); this.logger.debug("Token deleted from Catalyst cache successfully"); } catch (error) { this.logger.error( `Failed to delete token from Catalyst cache: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } }; // src/services/oauth-service.ts var OAuthService = class { constructor(config, logger) { this.config = config; this.logger = logger || defaultLogger; this.httpClient = new HttpClient(this.logger); } /** * Get refresh token from config (if available) */ getRefreshToken() { return this.config.refreshToken; } /** * Make token refresh request */ async makeTokenRequest(url, params) { try { this.logger.debug(`Making token request to: ${url}`); const formData = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { formData.append(key, value); }); const response = await this.httpClient.post(url, formData.toString(), { "Content-Type": "application/x-www-form-urlencoded" }); this.logger.debug("Token request successful"); return response.data; } catch (error) { this.logger.error( `Token request error: ${error instanceof Error ? error.message : "Unknown error"}` ); throw error; } } /** * Convert OAuth response to TokenData */ convertToTokenData(response) { const expiresIn = response.expires_in || 3600; const expiresAt = Date.now() + expiresIn * 1e3; return { accessToken: response.access_token, refreshToken: response.refresh_token, expiresAt, tokenType: response.token_type || "Bearer", scope: response.scope }; } }; // src/services/zoho-oauth.ts var ZohoOAuth = class extends OAuthService { constructor(config, logger) { const baseUrl = `https://${config.accountsDomain}`; const connectorConfig = { clientId: config.clientId, clientSecret: config.clientSecret, authUrl: `${baseUrl}/oauth/v2/auth`, refreshUrl: `${baseUrl}/oauth/v2/token`, refreshToken: config.refreshToken }; super(connectorConfig, logger); this.zohoConfig = config; this.authUrl = connectorConfig.authUrl; this.refreshUrl = connectorConfig.refreshUrl; } /** * Refresh access token using Zoho's refresh token endpoint */ async refreshAccessToken(refreshToken) { this.logger.debug("Refreshing Zoho access token"); const params = { refresh_token: refreshToken, client_id: this.zohoConfig.clientId, client_secret: this.zohoConfig.clientSecret, grant_type: "refresh_token" }; const response = await this.makeTokenRequest(this.refreshUrl, params); const tokenData = this.convertToTokenData(response); if (!tokenData.refreshToken) { tokenData.refreshToken = refreshToken; } this.logger.debug("Zoho access token refreshed successfully"); return tokenData; } /** * Get Zoho authorization URL */ getAuthorizationUrl(redirectUri, scopes = ["ZohoCRM.modules.ALL"]) { const params = new URLSearchParams({ response_type: "code", client_id: this.zohoConfig.clientId, scope: scopes.join(","), redirect_uri: redirectUri, access_type: "offline", prompt: "consent" }); return `${this.authUrl}?${params.toString()}`; } /** * Exchange authorization code for tokens */ async exchangeCodeForTokens(code, redirectUri) { this.logger.debug("Exchanging Zoho authorization code for tokens"); const params = { grant_type: "authorization_code", client_id: this.zohoConfig.clientId, client_secret: this.zohoConfig.clientSecret, redirect_uri: redirectUri, code }; const response = await this.makeTokenRequest(this.refreshUrl, params); const tokenData = this.convertToTokenData(response); this.logger.debug("Zoho tokens obtained successfully"); return tokenData; } }; // src/services/google-oauth.ts var GoogleOAuth = class extends OAuthService { constructor(config, logger) { super(config, logger); } /** * Refresh access token using Google's refresh token endpoint */ async refreshAccessToken(refreshToken) { this.logger.debug("Refreshing Google access token"); const params = { refresh_token: refreshToken, client_id: this.config.clientId, client_secret: this.config.clientSecret, grant_type: "refresh_token" }; const response = await this.makeTokenRequest(this.config.refreshUrl, params); const tokenData = this.convertToTokenData(response); if (!tokenData.refreshToken) { tokenData.refreshToken = refreshToken; } this.logger.debug("Google access token refreshed successfully"); return tokenData; } /** * Get Google authorization URL */ getAuthorizationUrl(redirectUri, scopes = ["openid", "email", "profile"]) { const params = new URLSearchParams({ response_type: "code", client_id: this.config.clientId, scope: scopes.join(" "), redirect_uri: redirectUri, access_type: "offline", prompt: "consent" }); return `${this.config.authUrl}?${params.toString()}`; } /** * Exchange authorization code for tokens */ async exchangeCodeForTokens(code, redirectUri) { this.logger.debug("Exchanging Google authorization code for tokens"); const params = { grant_type: "authorization_code", client_id: this.config.clientId, client_secret: this.config.clientSecret, redirect_uri: redirectUri, code }; const response = await this.makeTokenRequest(this.config.refreshUrl, params); const tokenData = this.convertToTokenData(response); this.logger.debug("Google tokens obtained successfully"); return tokenData; } }; // src/services/oauth.ts var OAuth = class extends OAuthService { constructor(config, logger) { super(config, logger); } /** * Refresh access token using generic OAuth refresh flow */ async refreshAccessToken(refreshToken) { this.logger.debug("Refreshing access token (generic OAuth)"); const params = { refresh_token: refreshToken, client_id: this.config.clientId, client_secret: this.config.clientSecret, grant_type: "refresh_token" }; const response = await this.makeTokenRequest(this.config.refreshUrl, params); const tokenData = this.convertToTokenData(response); if (!tokenData.refreshToken) { tokenData.refreshToken = refreshToken; } this.logger.debug("Access token refreshed successfully (generic OAuth)"); return tokenData; } /** * Get authorization URL (generic implementation) */ getAuthorizationUrl(redirectUri, scopes = []) { const params = new URLSearchParams({ response_type: "code", client_id: this.config.clientId, redirect_uri: redirectUri, access_type: "offline", prompt: "consent" }); if (scopes.length > 0) { params.append("scope", scopes.join(" ")); } return `${this.config.authUrl}?${params.toString()}`; } /** * Exchange authorization code for tokens */ async exchangeCodeForTokens(code, redirectUri) { this.logger.debug("Exchanging authorization code for tokens (generic OAuth)"); const params = { grant_type: "authorization_code", client_id: this.config.clientId, client_secret: this.config.clientSecret, redirect_uri: redirectUri, code }; const response = await this.makeTokenRequest(this.config.refreshUrl, params); const tokenData = this.convertToTokenData(response); this.logger.debug("Tokens obtained successfully (generic OAuth)"); return tokenData; } }; exports.CatalystCacheStorageStrategy = CatalystCacheStorageStrategy; exports.Connector = Connector; exports.EncryptionService = EncryptionService; exports.GoogleOAuth = GoogleOAuth; exports.HttpClient = HttpClient; exports.LocalStorageStrategy = LocalStorageStrategy; exports.LogLevel = LogLevel; exports.Logger = Logger; exports.OAuth = OAuth; exports.OAuthService = OAuthService; exports.RemoteStorageStrategy = RemoteStorageStrategy; exports.StorageStrategy = StorageStrategy; exports.TokenManager = TokenManager; exports.ZohoOAuth = ZohoOAuth;