diff --git a/extensions/codex/src/app-server/auth-bridge.test.ts b/extensions/codex/src/app-server/auth-bridge.test.ts index 8093dcfc05d8..160557c51e05 100644 --- a/extensions/codex/src/app-server/auth-bridge.test.ts +++ b/extensions/codex/src/app-server/auth-bridge.test.ts @@ -69,6 +69,9 @@ vi.mock("openclaw/plugin-sdk/agent-runtime", async (importOriginal) => { : ""); return apiKey ? { apiKey, provider: credential.provider, email: credential.email } : null; } + if (credential.type !== "oauth") { + return null; + } let oauthCredential = credential; if ((oauthCredential.expires ?? 0) <= Date.now()) { const refreshed = await providerRuntimeMocks.refreshProviderOAuthCredentialWithPlugin({ @@ -545,6 +548,35 @@ describe("bridgeCodexAppServerStartOptions", () => { } }); + it("rejects unsupported Codex auth profile credential types before OAuth refresh", async () => { + const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-")); + const request = vi.fn(async () => ({ type: "chatgptAuthTokens" })); + try { + upsertAuthProfile({ + agentDir, + profileId: "openai-codex:aws", + credential: { + type: "aws-sdk", + provider: "openai-codex", + }, + }); + + await expect( + applyCodexAppServerAuthProfile({ + client: { request } as never, + agentDir, + authProfileId: "openai-codex:aws", + }), + ).rejects.toThrow( + 'Codex app-server auth profile "openai-codex:aws" does not contain usable credentials.', + ); + expect(oauthMocks.refreshOpenAICodexToken).not.toHaveBeenCalled(); + expect(request).not.toHaveBeenCalled(); + } finally { + await fs.rm(agentDir, { recursive: true, force: true }); + } + }); + it("falls back to CODEX_API_KEY when no auth profile and no Codex account is available", async () => { const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-")); const request = vi.fn(async (method: string) => { diff --git a/extensions/codex/src/app-server/auth-bridge.ts b/extensions/codex/src/app-server/auth-bridge.ts index 641e7ff04e03..fba62d2d39c7 100644 --- a/extensions/codex/src/app-server/auth-bridge.ts +++ b/extensions/codex/src/app-server/auth-bridge.ts @@ -272,6 +272,9 @@ async function resolveLoginParamsForCredential( ? buildChatgptAuthTokensParams(profileId, credential, accessToken) : undefined; } + if (credential.type !== "oauth") { + return undefined; + } const resolvedCredential = await resolveOAuthCredentialForCodexAppServer(profileId, credential, { agentDir: params.agentDir, forceRefresh: params.forceOAuthRefresh, diff --git a/extensions/microsoft-foundry/index.test.ts b/extensions/microsoft-foundry/index.test.ts index fb96bfc8b022..86961f3108a2 100644 --- a/extensions/microsoft-foundry/index.test.ts +++ b/extensions/microsoft-foundry/index.test.ts @@ -698,6 +698,40 @@ describe("microsoft-foundry plugin", () => { ]); }); + it("keeps Foundry profile selection compatible with unrelated AWS SDK profile modes", async () => { + const provider = registerProvider(); + const config: OpenClawConfig = { + ...buildFoundryConfig({ + profileIds: ["microsoft-foundry:entra"], + orderedProfileIds: ["microsoft-foundry:entra"], + }), + auth: { + profiles: { + "amazon-bedrock:default": { + provider: "amazon-bedrock", + mode: "aws-sdk", + }, + "microsoft-foundry:entra": { + provider: "microsoft-foundry", + mode: "api_key", + }, + }, + order: { + "microsoft-foundry": ["microsoft-foundry:entra"], + }, + }, + }; + + await provider.onModelSelected?.({ + config, + model: "microsoft-foundry/gpt-5.4", + prompter: {} as never, + agentDir: defaultFoundryAgentDir, + }); + + expect(config.auth?.order?.["microsoft-foundry"]).toEqual(["microsoft-foundry:entra"]); + }); + it("persists discovered deployments alongside the selected default model", () => { const result = buildFoundryAuthResult({ profileId: "microsoft-foundry:entra", diff --git a/extensions/microsoft-foundry/shared.ts b/extensions/microsoft-foundry/shared.ts index 58868bd580f1..ff9c0d2f749e 100644 --- a/extensions/microsoft-foundry/shared.ts +++ b/extensions/microsoft-foundry/shared.ts @@ -100,7 +100,7 @@ type FoundryModelCompat = { type FoundryAuthProfileConfig = { provider: string; - mode: "api_key" | "oauth" | "token"; + mode: "api_key" | "aws-sdk" | "oauth" | "token"; email?: string; };