From 7ec572ca47f7fab6b27ea39696bf22d4854f776a Mon Sep 17 00:00:00 2001 From: Antigravity Assistant Date: Fri, 1 May 2026 17:59:20 -0300 Subject: [PATCH] fix(auth): prevent token refresh race conditions causing refresh_token_reused errors --- open-sse/services/tokenRefresh.ts | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/open-sse/services/tokenRefresh.ts b/open-sse/services/tokenRefresh.ts index e1324acced..0b9edc7bfc 100755 --- a/open-sse/services/tokenRefresh.ts +++ b/open-sse/services/tokenRefresh.ts @@ -841,6 +841,51 @@ export async function getAccessToken(provider, credentials, log, proxyConfig: un return null; } + // RACE CONDITION PREVENTION: + // If the credentials object in memory is stale (e.g. it waited in a semaphore while another + // request refreshed the token), using its OLD refreshToken will cause the provider (e.g. OpenAI) + // to reject it with 'refresh_token_reused' and revoke the new token family. + // We MUST check if the DB has a newer token before proceeding with a network refresh. + if (credentials.connectionId) { + try { + const { getProviderConnectionById } = await import("../../src/lib/db/providers"); + const dbConnection = await getProviderConnectionById(credentials.connectionId); + if ( + dbConnection && + dbConnection.refreshToken && + dbConnection.refreshToken !== credentials.refreshToken + ) { + log?.info?.( + "TOKEN_REFRESH", + `Stale token detected in memory for ${provider}. Using refreshed token from DB.` + ); + + // If the DB token is not expired, we can just return it! + const now = Date.now(); + const dbExpiresAt = dbConnection.expiresAt ? new Date(dbConnection.expiresAt).getTime() : 0; + + if (dbExpiresAt > now + 60000) { + // 60 seconds buffer + log?.info?.("TOKEN_REFRESH", `DB token is still valid. Skipping OAuth refresh.`); + return { + accessToken: dbConnection.accessToken, + refreshToken: dbConnection.refreshToken, + expiresIn: dbConnection.expiresIn, + }; + } else { + // DB token is also expired, but it's the NEWEST one. We must use it to refresh. + credentials.refreshToken = dbConnection.refreshToken; + credentials.accessToken = dbConnection.accessToken; + } + } + } catch (e) { + log?.warn?.( + "TOKEN_REFRESH", + `Failed to check DB for stale token: ${e instanceof Error ? e.message : String(e)}` + ); + } + } + const cacheKey = getRefreshCacheKey(provider, credentials.refreshToken); // If a refresh is already in-flight, reuse it