From b31c001a2b51f3df7f081b05048e83f977918e18 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 4 May 2026 00:48:16 -0700 Subject: [PATCH] fix(googlechat): isolate auth transports --- CHANGELOG.md | 1 + .../src/google-auth.runtime.test.ts | 12 ++++++++++ .../googlechat/src/google-auth.runtime.ts | 24 +++++-------------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d080cc92692..08148e3394c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis. +- Google Chat: create an isolated Google auth transport per auth client, so google-auth-library interceptor mutations do not accumulate across webhook verification and access-token clients. Thanks @vincentkoc. - Control UI/performance: cap long-task and long-animation-frame diagnostics in the shared event log, so slow-render telemetry does not evict gateway/plugin events from the Debug and Overview views. Thanks @vincentkoc. - Web fetch: late-bind `web_fetch` config and provider fallback metadata from the active runtime snapshot, matching `web_search` so long-lived tools do not use stale fetch provider settings. Thanks @vincentkoc. - Discord: clear stale startup probe bot/application status when the async bot probe throws, not just when it returns a degraded probe result. Thanks @vincentkoc. diff --git a/extensions/googlechat/src/google-auth.runtime.test.ts b/extensions/googlechat/src/google-auth.runtime.test.ts index 1a583af9496..f7ef267292b 100644 --- a/extensions/googlechat/src/google-auth.runtime.test.ts +++ b/extensions/googlechat/src/google-auth.runtime.test.ts @@ -359,6 +359,18 @@ describe("googlechat google auth runtime", () => { } }); + it("keeps auth transports isolated from google-auth interceptor mutations", async () => { + const first = await getGoogleAuthTransport(); + const second = await getGoogleAuthTransport(); + + expect(first).not.toBe(second); + expect(mocks.gaxiosCtor).toHaveBeenCalledTimes(2); + expect(first.interceptors.request.add).toHaveBeenCalledOnce(); + expect(first.interceptors.response.add).toHaveBeenCalledOnce(); + expect(second.interceptors.request.add).toHaveBeenCalledOnce(); + expect(second.interceptors.response.add).toHaveBeenCalledOnce(); + }); + it("normalizes Google auth request headers before upstream interceptors run", async () => { const config = { headers: { "x-test": "1" }, diff --git a/extensions/googlechat/src/google-auth.runtime.ts b/extensions/googlechat/src/google-auth.runtime.ts index 171526884fb..ca05810f762 100644 --- a/extensions/googlechat/src/google-auth.runtime.ts +++ b/extensions/googlechat/src/google-auth.runtime.ts @@ -71,7 +71,6 @@ const MAX_GOOGLE_AUTH_RESPONSE_BYTES = 1024 * 1024; const MAX_GOOGLE_CHAT_SERVICE_ACCOUNT_FILE_BYTES = 64 * 1024; let googleAuthRuntimePromise: Promise | null = null; -let googleAuthTransportPromise: Promise | null = null; function normalizeGoogleAuthPreparedRequestHeaders( config: T, @@ -536,22 +535,12 @@ export async function loadGoogleAuthRuntime(): Promise { } export async function getGoogleAuthTransport(): Promise { - if (!googleAuthTransportPromise) { - googleAuthTransportPromise = (async () => { - try { - const { Gaxios } = await loadGoogleAuthRuntime(); - return installGoogleAuthHeaderCompatibilityInterceptor( - new Gaxios({ - fetchImplementation: createGoogleAuthFetch(), - }), - ); - } catch (error) { - googleAuthTransportPromise = null; - throw error; - } - })(); - } - return await googleAuthTransportPromise; + const { Gaxios } = await loadGoogleAuthRuntime(); + return installGoogleAuthHeaderCompatibilityInterceptor( + new Gaxios({ + fetchImplementation: createGoogleAuthFetch(), + }), + ); } export async function resolveValidatedGoogleChatCredentials( @@ -570,7 +559,6 @@ export async function resolveValidatedGoogleChatCredentials( export const __testing = { resetGoogleAuthRuntimeForTests(): void { googleAuthRuntimePromise = null; - googleAuthTransportPromise = null; }, normalizeGoogleAuthPreparedRequestHeaders, normalizeGoogleAuthResponseHeaders,