mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:30:42 +00:00
fix(openai): tighten codex device auth trust
This commit is contained in:
committed by
Val Alexander
parent
fd6e12f051
commit
cb353c0c5a
@@ -1,8 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
resolveCodexAuthIdentity,
|
||||
resolveCodexChatgptAccountId,
|
||||
} from "./openai-codex-auth-identity.js";
|
||||
import { resolveCodexAuthIdentity } from "./openai-codex-auth-identity.js";
|
||||
|
||||
function createJwt(payload: Record<string, unknown>): string {
|
||||
const header = Buffer.from(JSON.stringify({ alg: "none", typ: "JWT" })).toString("base64url");
|
||||
@@ -57,21 +54,3 @@ describe("resolveCodexAuthIdentity", () => {
|
||||
expect(resolveCodexAuthIdentity({ accessToken: "not-a-jwt-token" })).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveCodexChatgptAccountId", () => {
|
||||
it("extracts the ChatGPT account id from the auth claim", () => {
|
||||
expect(
|
||||
resolveCodexChatgptAccountId(
|
||||
createJwt({
|
||||
"https://api.openai.com/auth": {
|
||||
chatgpt_account_id: "acct_123",
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe("acct_123");
|
||||
});
|
||||
|
||||
it("returns undefined when the account id is missing", () => {
|
||||
expect(resolveCodexChatgptAccountId(createJwt({}))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,11 +60,6 @@ export function resolveCodexStableSubject(payload: CodexJwtPayload | null): stri
|
||||
return sub;
|
||||
}
|
||||
|
||||
export function resolveCodexChatgptAccountId(token: string): string | undefined {
|
||||
const auth = decodeCodexJwtPayload(token)?.["https://api.openai.com/auth"];
|
||||
return trimNonEmptyString(auth?.chatgpt_account_id);
|
||||
}
|
||||
|
||||
export function resolveCodexAccessTokenExpiry(accessToken: string): number | undefined {
|
||||
const payload = decodeCodexJwtPayload(accessToken);
|
||||
const exp = normalizeFutureEpochSeconds(payload?.exp);
|
||||
|
||||
@@ -91,8 +91,8 @@ describe("loginOpenAICodexDeviceCode", () => {
|
||||
expect(credentials).toMatchObject({
|
||||
access: expect.any(String),
|
||||
refresh: "refresh-token-123",
|
||||
accountId: "acct_123",
|
||||
});
|
||||
expect(credentials).not.toHaveProperty("accountId");
|
||||
expect(credentials.expires).toBeGreaterThan(Date.now());
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { ensureGlobalUndiciEnvProxyDispatcher } from "openclaw/plugin-sdk/runtime-env";
|
||||
import {
|
||||
resolveCodexAccessTokenExpiry,
|
||||
resolveCodexChatgptAccountId,
|
||||
} from "./openai-codex-auth-identity.js";
|
||||
import { resolveCodexAccessTokenExpiry } from "./openai-codex-auth-identity.js";
|
||||
import { trimNonEmptyString } from "./openai-codex-shared.js";
|
||||
|
||||
const OPENAI_AUTH_BASE_URL = "https://auth.openai.com";
|
||||
@@ -22,7 +18,6 @@ type OpenAICodexDeviceCodeCredentials = {
|
||||
access: string;
|
||||
refresh: string;
|
||||
expires: number;
|
||||
accountId?: string;
|
||||
};
|
||||
|
||||
type DeviceCodeUserCodePayload = {
|
||||
@@ -41,7 +36,6 @@ type DeviceCodeTokenPayload = {
|
||||
type OAuthTokenPayload = {
|
||||
access_token?: unknown;
|
||||
refresh_token?: unknown;
|
||||
id_token?: unknown;
|
||||
expires_in?: unknown;
|
||||
};
|
||||
|
||||
@@ -259,7 +253,6 @@ async function exchangeOpenAICodexDeviceCode(params: {
|
||||
const body = parseJsonObject(bodyText) as OAuthTokenPayload | null;
|
||||
const access = trimNonEmptyString(body?.access_token);
|
||||
const refresh = trimNonEmptyString(body?.refresh_token);
|
||||
const idToken = trimNonEmptyString(body?.id_token);
|
||||
if (!access || !refresh) {
|
||||
throw new Error("OpenAI token exchange succeeded but did not return OAuth tokens.");
|
||||
}
|
||||
@@ -269,14 +262,11 @@ async function exchangeOpenAICodexDeviceCode(params: {
|
||||
expiresInMs !== undefined
|
||||
? Date.now() + expiresInMs
|
||||
: (resolveCodexAccessTokenExpiry(access) ?? Date.now());
|
||||
const accountId =
|
||||
resolveCodexChatgptAccountId(access) ?? (idToken && resolveCodexChatgptAccountId(idToken));
|
||||
|
||||
return {
|
||||
access,
|
||||
refresh,
|
||||
expires,
|
||||
...(accountId ? { accountId } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -285,7 +275,6 @@ export async function loginOpenAICodexDeviceCode(params: {
|
||||
onVerification: (prompt: OpenAICodexDeviceCodePrompt) => Promise<void> | void;
|
||||
onProgress?: (message: string) => void;
|
||||
}): Promise<OpenAICodexDeviceCodeCredentials> {
|
||||
ensureGlobalUndiciEnvProxyDispatcher();
|
||||
const fetchFn = params.fetchFn ?? fetch;
|
||||
|
||||
params.onProgress?.("Requesting device code…");
|
||||
|
||||
@@ -252,7 +252,6 @@ describe("openai codex provider", () => {
|
||||
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL2F1dGgiOnsiY2hhdGdwdF9hY2NvdW50X2lkIjoiYWNjdC1kZXZpY2UtMTIzIn19.signature",
|
||||
refresh: "device-refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
accountId: "acct-device-123",
|
||||
});
|
||||
|
||||
const result = await deviceCodeMethod?.run({
|
||||
@@ -283,13 +282,13 @@ describe("openai codex provider", () => {
|
||||
access:
|
||||
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL2F1dGgiOnsiY2hhdGdwdF9hY2NvdW50X2lkIjoiYWNjdC1kZXZpY2UtMTIzIn19.signature",
|
||||
refresh: "device-refresh-token",
|
||||
accountId: "acct-device-123",
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultModel: "openai-codex/gpt-5.4",
|
||||
});
|
||||
expect(result?.profiles[0]?.credential).not.toHaveProperty("idToken");
|
||||
expect(result?.profiles[0]?.credential).not.toHaveProperty("accountId");
|
||||
});
|
||||
|
||||
it("does not log the device pairing code in remote mode", async () => {
|
||||
@@ -313,7 +312,6 @@ describe("openai codex provider", () => {
|
||||
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL2F1dGgiOnsiY2hhdGdwdF9hY2NvdW50X2lkIjoiYWNjdC1kZXZpY2UtMTIzIn19.signature",
|
||||
refresh: "device-refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
accountId: "acct-device-123",
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
readOpenAICodexCliOAuthProfile,
|
||||
} from "./openai-codex-cli-auth.js";
|
||||
import { loginOpenAICodexDeviceCode } from "./openai-codex-device-code.js";
|
||||
import { trimNonEmptyString } from "./openai-codex-shared.js";
|
||||
import {
|
||||
buildOpenAIResponsesProviderHooks,
|
||||
buildOpenAISyntheticCatalogEntry,
|
||||
@@ -357,7 +356,6 @@ async function runOpenAICodexDeviceCode(ctx: ProviderAuthContext) {
|
||||
expires: creds.expires,
|
||||
email: identity.email,
|
||||
profileName: identity.profileName,
|
||||
credentialExtra: trimNonEmptyString(creds.accountId) ? { accountId: creds.accountId } : {},
|
||||
});
|
||||
} catch (error) {
|
||||
spin.stop("OpenAI device code failed");
|
||||
|
||||
Reference in New Issue
Block a user