From 854323a12481fe506e36bcb9682ca5b37cc4c032 Mon Sep 17 00:00:00 2001 From: Shakker Date: Sat, 2 May 2026 04:41:51 +0100 Subject: [PATCH] fix: reuse provider auth hook lookup context --- .../auth-profiles/oauth-manager.test.ts | 39 ++++++++++++++ src/agents/auth-profiles/oauth-manager.ts | 52 +++++++++++++++---- src/agents/auth-profiles/oauth.ts | 9 +++- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/agents/auth-profiles/oauth-manager.test.ts b/src/agents/auth-profiles/oauth-manager.test.ts index c906849ddb8..dac75d09a3e 100644 --- a/src/agents/auth-profiles/oauth-manager.test.ts +++ b/src/agents/auth-profiles/oauth-manager.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { captureEnv } from "../../test-utils/env.js"; import { __testing as externalAuthTesting } from "./external-auth.js"; import { @@ -146,6 +147,44 @@ describe("OAuthManagerRefreshError", () => { }); describe("createOAuthManager", () => { + it("passes active config to OAuth API-key formatting", async () => { + const profileId = "openai-codex:default"; + const credential = createCredential({ expires: Date.now() + 10 * 60_000 }); + const cfg = { + models: { + providers: { + "openai-codex": { auth: "oauth", models: [] }, + }, + }, + } satisfies OpenClawConfig; + const buildApiKey = vi.fn(async (_provider, value: OAuthCredential) => value.access); + const manager = createOAuthManager({ + buildApiKey, + refreshCredential: vi.fn(async () => null), + readBootstrapCredential: () => null, + isRefreshTokenReusedError: () => false, + }); + + await expect( + manager.resolveOAuthAccess({ + store: { + version: 1, + profiles: { + [profileId]: credential, + }, + }, + profileId, + credential, + cfg, + }), + ).resolves.toMatchObject({ apiKey: "access-token" }); + + expect(buildApiKey).toHaveBeenCalledWith("openai-codex", credential, { + cfg, + agentDir: undefined, + }); + }); + it("does not overlay external auth while checking main-store adoption", async () => { const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "oauth-manager-main-adopt-")); tempDirs.push(tempRoot); diff --git a/src/agents/auth-profiles/oauth-manager.ts b/src/agents/auth-profiles/oauth-manager.ts index d426579ecb6..5333553b007 100644 --- a/src/agents/auth-profiles/oauth-manager.ts +++ b/src/agents/auth-profiles/oauth-manager.ts @@ -1,3 +1,4 @@ +import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { withFileLock } from "../../infra/file-lock.js"; import { @@ -34,7 +35,11 @@ import { import type { AuthProfileStore, OAuthCredential, OAuthCredentials } from "./types.js"; export type OAuthManagerAdapter = { - buildApiKey: (provider: string, credentials: OAuthCredential) => Promise; + buildApiKey: ( + provider: string, + credentials: OAuthCredential, + context: { cfg?: OpenClawConfig; agentDir?: string }, + ) => Promise; refreshCredential: (credential: OAuthCredential) => Promise; readBootstrapCredential: (params: { profileId: string; @@ -318,6 +323,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { profileId: string; provider: string; agentDir?: string; + cfg?: OpenClawConfig; }): Promise { const ownerAgentDir = resolvePersistedAuthProfileOwnerAgentDir(params); const authPath = resolveAuthStorePath(ownerAgentDir); @@ -336,7 +342,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { if (hasUsableOAuthCredential(cred)) { return { - apiKey: await adapter.buildApiKey(cred.provider, cred), + apiKey: await adapter.buildApiKey(cred.provider, cred, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: cred, }; } @@ -358,7 +367,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { expires: new Date(mainCred.expires).toISOString(), }); return { - apiKey: await adapter.buildApiKey(mainCred.provider, mainCred), + apiKey: await adapter.buildApiKey(mainCred.provider, mainCred, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: mainCred, }; } else if ( @@ -409,7 +421,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { credentialToRefresh = externallyManaged; if (hasUsableOAuthCredential(externallyManaged)) { return { - apiKey: await adapter.buildApiKey(externallyManaged.provider, externallyManaged), + apiKey: await adapter.buildApiKey(externallyManaged.provider, externallyManaged, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: externallyManaged, }; } @@ -445,7 +460,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { } } return { - apiKey: await adapter.buildApiKey(cred.provider, refreshedCredentials), + apiKey: await adapter.buildApiKey(cred.provider, refreshedCredentials, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: refreshedCredentials, }; }), @@ -466,6 +484,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { profileId: string; provider: string; agentDir?: string; + cfg?: OpenClawConfig; }): Promise { const key = refreshQueueKey(params.provider, params.profileId); const prev = refreshQueues.get(key) ?? Promise.resolve(); @@ -490,6 +509,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { profileId: string; credential: OAuthCredential; agentDir?: string; + cfg?: OpenClawConfig; }): Promise { const adoptedCredential = adoptNewerMainOAuthCredential({ @@ -506,7 +526,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { if (hasUsableOAuthCredential(effectiveCredential)) { return { - apiKey: await adapter.buildApiKey(effectiveCredential.provider, effectiveCredential), + apiKey: await adapter.buildApiKey(effectiveCredential.provider, effectiveCredential, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: effectiveCredential, }; } @@ -516,6 +539,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { profileId: params.profileId, provider: params.credential.provider, agentDir: params.agentDir, + cfg: params.cfg, }); return refreshed; } catch (error) { @@ -523,7 +547,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { const refreshed = refreshedStore.profiles[params.profileId]; if (refreshed?.type === "oauth" && hasUsableOAuthCredential(refreshed)) { return { - apiKey: await adapter.buildApiKey(refreshed.provider, refreshed), + apiKey: await adapter.buildApiKey(refreshed.provider, refreshed, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: refreshed, }; } @@ -542,7 +569,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { }); if (recovered) { return { - apiKey: await adapter.buildApiKey(recovered.provider, recovered), + apiKey: await adapter.buildApiKey(recovered.provider, recovered, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: recovered, }; } @@ -551,6 +581,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { profileId: params.profileId, provider: params.credential.provider, agentDir: params.agentDir, + cfg: params.cfg, }); if (retried) { return retried; @@ -579,7 +610,10 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { expires: new Date(mainCred.expires).toISOString(), }); return { - apiKey: await adapter.buildApiKey(mainCred.provider, mainCred), + apiKey: await adapter.buildApiKey(mainCred.provider, mainCred, { + cfg: params.cfg, + agentDir: params.agentDir, + }), credential: mainCred, }; } diff --git a/src/agents/auth-profiles/oauth.ts b/src/agents/auth-profiles/oauth.ts index 038b5988629..9ce4157542d 100644 --- a/src/agents/auth-profiles/oauth.ts +++ b/src/agents/auth-profiles/oauth.ts @@ -93,9 +93,14 @@ function isProfileConfigCompatible(params: { return true; } -async function buildOAuthApiKey(provider: string, credentials: OAuthCredential): Promise { +async function buildOAuthApiKey( + provider: string, + credentials: OAuthCredential, + context: { cfg?: OpenClawConfig }, +): Promise { const formatted = await formatProviderAuthProfileApiKeyWithPlugin({ provider, + config: context.cfg, context: credentials, }); return typeof formatted === "string" && formatted.length > 0 ? formatted : credentials.access; @@ -195,6 +200,7 @@ async function tryResolveOAuthProfile( profileId, credential: cred, agentDir: params.agentDir, + cfg, }); if (!resolved) { return null; @@ -333,6 +339,7 @@ export async function resolveApiKeyForProfile( agentDir: params.agentDir, profileId, credential: cred, + cfg, }); if (!resolved) { return null;