mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix: reuse provider auth hook lookup context
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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<string>;
|
||||
buildApiKey: (
|
||||
provider: string,
|
||||
credentials: OAuthCredential,
|
||||
context: { cfg?: OpenClawConfig; agentDir?: string },
|
||||
) => Promise<string>;
|
||||
refreshCredential: (credential: OAuthCredential) => Promise<OAuthCredentials | null>;
|
||||
readBootstrapCredential: (params: {
|
||||
profileId: string;
|
||||
@@ -318,6 +323,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) {
|
||||
profileId: string;
|
||||
provider: string;
|
||||
agentDir?: string;
|
||||
cfg?: OpenClawConfig;
|
||||
}): Promise<ResolvedOAuthAccess | null> {
|
||||
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<ResolvedOAuthAccess | null> {
|
||||
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<ResolvedOAuthAccess | null> {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,9 +93,14 @@ function isProfileConfigCompatible(params: {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function buildOAuthApiKey(provider: string, credentials: OAuthCredential): Promise<string> {
|
||||
async function buildOAuthApiKey(
|
||||
provider: string,
|
||||
credentials: OAuthCredential,
|
||||
context: { cfg?: OpenClawConfig },
|
||||
): Promise<string> {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user