| |
| import * as log from "../utils/logger.js"; |
| import { updateProviderConnection } from "../../lib/localDb.js"; |
| import { |
| getProjectIdForConnection, |
| invalidateProjectId, |
| removeConnection, |
| } from "open-sse/services/projectId.js"; |
| import { |
| TOKEN_EXPIRY_BUFFER_MS as BUFFER_MS, |
| refreshAccessToken as _refreshAccessToken, |
| refreshClaudeOAuthToken as _refreshClaudeOAuthToken, |
| refreshGoogleToken as _refreshGoogleToken, |
| refreshQwenToken as _refreshQwenToken, |
| refreshCodexToken as _refreshCodexToken, |
| refreshIflowToken as _refreshIflowToken, |
| refreshGitHubToken as _refreshGitHubToken, |
| refreshCopilotToken as _refreshCopilotToken, |
| getAccessToken as _getAccessToken, |
| refreshTokenByProvider as _refreshTokenByProvider, |
| formatProviderCredentials as _formatProviderCredentials, |
| getAllAccessTokens as _getAllAccessTokens, |
| refreshKiroToken as _refreshKiroToken, |
| getRefreshLeadMs as _getRefreshLeadMs |
| } from "open-sse/services/tokenRefresh.js"; |
| import { |
| refreshProviderCredentials as _refreshProviderCredentials, |
| shouldRefreshCredentials as _shouldRefreshCredentials, |
| } from "open-sse/services/oauthCredentialManager.js"; |
|
|
| export const TOKEN_EXPIRY_BUFFER_MS = BUFFER_MS; |
|
|
| |
|
|
| export const refreshAccessToken = (provider, refreshToken, credentials) => |
| _refreshAccessToken(provider, refreshToken, credentials, log); |
|
|
| export const refreshClaudeOAuthToken = (refreshToken) => |
| _refreshClaudeOAuthToken(refreshToken, log); |
|
|
| export const refreshGoogleToken = (refreshToken, clientId, clientSecret) => |
| _refreshGoogleToken(refreshToken, clientId, clientSecret, log); |
|
|
| export const refreshQwenToken = (refreshToken) => |
| _refreshQwenToken(refreshToken, log); |
|
|
| export const refreshCodexToken = (refreshToken) => |
| _refreshCodexToken(refreshToken, log); |
|
|
| export const refreshIflowToken = (refreshToken) => |
| _refreshIflowToken(refreshToken, log); |
|
|
| export const refreshGitHubToken = (refreshToken) => |
| _refreshGitHubToken(refreshToken, log); |
|
|
| export const refreshCopilotToken = (githubAccessToken) => |
| _refreshCopilotToken(githubAccessToken, log); |
|
|
| export const refreshKiroToken = (refreshToken, providerSpecificData) => |
| _refreshKiroToken(refreshToken, providerSpecificData, log); |
|
|
| export const getAccessToken = (provider, credentials) => |
| _getAccessToken(provider, credentials, log); |
|
|
| export const refreshTokenByProvider = (provider, credentials) => |
| _refreshTokenByProvider(provider, credentials, log); |
|
|
| export const formatProviderCredentials = (provider, credentials) => |
| _formatProviderCredentials(provider, credentials, log); |
|
|
| export const getAllAccessTokens = (userInfo) => |
| _getAllAccessTokens(userInfo, log); |
|
|
| export const shouldRefreshCredentials = (provider, credentials) => |
| _shouldRefreshCredentials(provider, credentials); |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function releaseConnection(connectionId) { |
| if (!connectionId) return; |
| removeConnection(connectionId); |
| log.debug("TOKEN_REFRESH", "Released connection resources", { connectionId }); |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| function toExpiresAt(expiresIn) { |
| return new Date(Date.now() + expiresIn * 1000).toISOString(); |
| } |
|
|
| function normalizeExpiresAt(expiresAt) { |
| if (!expiresAt) return null; |
| const date = new Date(expiresAt); |
| if (!Number.isFinite(date.getTime())) return null; |
| return date.toISOString(); |
| } |
|
|
| |
| |
| |
| |
| |
| function needsProjectId(provider) { |
| return provider === "antigravity" || provider === "gemini-cli"; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function _refreshProjectId(provider, connectionId, accessToken) { |
| if (!needsProjectId(provider) || !connectionId || !accessToken) return; |
|
|
| |
| invalidateProjectId(connectionId); |
|
|
| getProjectIdForConnection(connectionId, accessToken) |
| .then((projectId) => { |
| if (!projectId) return; |
| updateProviderCredentials(connectionId, { projectId }).catch((err) => { |
| log.debug("TOKEN_REFRESH", "Failed to persist refreshed projectId", { |
| connectionId, |
| error: err?.message ?? err, |
| }); |
| }); |
| }) |
| .catch((err) => { |
| log.debug("TOKEN_REFRESH", "Failed to fetch projectId after token refresh", { |
| connectionId, |
| error: err?.message ?? err, |
| }); |
| }); |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export async function updateProviderCredentials(connectionId, newCredentials) { |
| try { |
| const updates = {}; |
|
|
| if (newCredentials.accessToken) updates.accessToken = newCredentials.accessToken; |
| if (newCredentials.refreshToken) updates.refreshToken = newCredentials.refreshToken; |
| if (newCredentials.idToken) updates.idToken = newCredentials.idToken; |
| if (newCredentials.lastRefreshAt) updates.lastRefreshAt = newCredentials.lastRefreshAt; |
| if (newCredentials.expiresAt) updates.expiresAt = newCredentials.expiresAt; |
| if (newCredentials.expiresIn) { |
| updates.expiresAt = toExpiresAt(newCredentials.expiresIn); |
| updates.expiresIn = newCredentials.expiresIn; |
| } else if (newCredentials.expiresAt) { |
| const expiresAt = normalizeExpiresAt(newCredentials.expiresAt); |
| if (expiresAt) { |
| updates.expiresAt = expiresAt; |
| updates.expiresIn = Math.max(1, Math.floor((new Date(expiresAt).getTime() - Date.now()) / 1000)); |
| } |
| } |
| if (newCredentials.providerSpecificData) { |
| updates.providerSpecificData = { |
| ...(newCredentials.existingProviderSpecificData || {}), |
| ...newCredentials.providerSpecificData, |
| }; |
| } |
| if (newCredentials.copilotToken || newCredentials.copilotTokenExpiresAt) { |
| updates.providerSpecificData = { |
| ...(updates.providerSpecificData || newCredentials.existingProviderSpecificData || {}), |
| ...(newCredentials.copilotToken ? { copilotToken: newCredentials.copilotToken } : {}), |
| ...(newCredentials.copilotTokenExpiresAt ? { copilotTokenExpiresAt: newCredentials.copilotTokenExpiresAt } : {}), |
| }; |
| } |
| if (newCredentials.projectId) updates.projectId = newCredentials.projectId; |
|
|
| const result = await updateProviderConnection(connectionId, updates); |
| log.info("TOKEN_REFRESH", "Credentials updated in localDb", { |
| connectionId, |
| success: !!result |
| }); |
| return !!result; |
| } catch (error) { |
| log.error("TOKEN_REFRESH", "Error updating credentials in localDb", { |
| connectionId, |
| error: error.message, |
| }); |
| return false; |
| } |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export async function checkAndRefreshToken(provider, credentials) { |
| let creds = { ...credentials }; |
| if (!creds.connectionId && creds.id) { |
| creds.connectionId = creds.id; |
| } |
|
|
| |
| if (_shouldRefreshCredentials(provider, creds)) { |
| const expiresAt = creds.expiresAt ? new Date(creds.expiresAt).getTime() : null; |
| const remaining = expiresAt ? expiresAt - Date.now() : null; |
| const refreshLead = _getRefreshLeadMs(provider); |
|
|
| log.info("TOKEN_REFRESH", "Refreshing provider credentials proactively", { |
| provider, |
| expiresIn: remaining === null ? null : Math.round(remaining / 1000), |
| refreshLeadMs: refreshLead, |
| lastRefreshAt: creds.lastRefreshAt || null, |
| }); |
|
|
| const newCreds = await _refreshProviderCredentials(provider, creds, log); |
| if (newCreds?.accessToken || newCreds?.apiKey || newCreds?.copilotToken) { |
| const mergedCreds = { |
| ...newCreds, |
| existingProviderSpecificData: creds.providerSpecificData, |
| }; |
|
|
| |
| await updateProviderCredentials(creds.connectionId, mergedCreds); |
|
|
| creds = { |
| ...creds, |
| ...newCreds, |
| expiresAt: newCreds.expiresIn |
| ? toExpiresAt(newCreds.expiresIn) |
| : normalizeExpiresAt(newCreds.expiresAt) || newCreds.expiresAt || creds.expiresAt, |
| providerSpecificData: newCreds.providerSpecificData |
| ? { ...creds.providerSpecificData, ...newCreds.providerSpecificData } |
| : creds.providerSpecificData, |
| }; |
|
|
| |
| _refreshProjectId(provider, creds.connectionId, creds.accessToken); |
| } |
| } |
|
|
| |
| if (provider === "github") { |
| const copilotToken = creds.providerSpecificData?.copilotToken; |
| const copilotExpiresAt = creds.providerSpecificData?.copilotTokenExpiresAt |
| ? creds.providerSpecificData.copilotTokenExpiresAt * 1000 |
| : 0; |
| const now = Date.now(); |
| const remaining = copilotExpiresAt - now; |
|
|
| if (!copilotToken || remaining < TOKEN_EXPIRY_BUFFER_MS) { |
| log.info("TOKEN_REFRESH", "Copilot token expiring soon or missing, refreshing proactively", { |
| provider, |
| expiresIn: copilotToken ? Math.round(remaining / 1000) : "missing", |
| }); |
|
|
| const copilotTokenResult = await refreshCopilotToken(creds.accessToken); |
| if (copilotTokenResult) { |
| const updatedSpecific = { |
| ...creds.providerSpecificData, |
| copilotToken: copilotTokenResult.token, |
| copilotTokenExpiresAt: copilotTokenResult.expiresAt, |
| }; |
|
|
| await updateProviderCredentials(creds.connectionId, { |
| providerSpecificData: updatedSpecific, |
| }); |
|
|
| creds.providerSpecificData = updatedSpecific; |
| creds.copilotToken = copilotTokenResult.token; |
| } |
| } |
| } |
|
|
| return creds; |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| export async function refreshGitHubAndCopilotTokens(credentials) { |
| const newGitHubCreds = await refreshGitHubToken(credentials.refreshToken); |
| if (!newGitHubCreds?.accessToken) return newGitHubCreds; |
|
|
| const copilotToken = await refreshCopilotToken(newGitHubCreds.accessToken); |
| if (!copilotToken) return newGitHubCreds; |
|
|
| return { |
| ...newGitHubCreds, |
| providerSpecificData: { |
| copilotToken: copilotToken.token, |
| copilotTokenExpiresAt: copilotToken.expiresAt, |
| }, |
| }; |
| } |
|
|