fix: preserve OpenAI Codex OAuth transport (#75111)

Preserve the existing wrapped OpenAI Codex stream so PI OAuth bearer injection reaches ChatGPT/Codex Responses, and scope native Codex payload sanitization to the ChatGPT backend.\n\nThanks @keshavbotagent.
This commit is contained in:
keshavbotagent
2026-04-30 22:30:12 +05:30
committed by GitHub
parent adc20fed0d
commit 388019f5b6
10 changed files with 292 additions and 23 deletions

View File

@@ -24,6 +24,27 @@ describe("resolveCodexAuthIdentity", () => {
});
});
it("extracts account and plan metadata from the JWT auth claim", () => {
const identity = resolveCodexAuthIdentity({
accessToken: createJwt({
"https://api.openai.com/profile": {
email: "jwt-user@example.com",
},
"https://api.openai.com/auth": {
chatgpt_account_id: "acct-123",
chatgpt_plan_type: "prolite",
},
}),
});
expect(identity).toEqual({
accountId: "acct-123",
chatgptPlanType: "prolite",
email: "jwt-user@example.com",
profileName: "jwt-user@example.com",
});
});
it("falls back to credential email before synthetic ids", () => {
const identity = resolveCodexAuthIdentity({
accessToken: createJwt({}),

View File

@@ -10,6 +10,7 @@ type CodexJwtPayload = {
"https://api.openai.com/auth"?: {
chatgpt_account_id?: unknown;
chatgpt_account_user_id?: unknown;
chatgpt_plan_type?: unknown;
chatgpt_user_id?: unknown;
user_id?: unknown;
};
@@ -67,23 +68,33 @@ export function resolveCodexAccessTokenExpiry(accessToken: string): number | und
}
export function resolveCodexAuthIdentity(params: { accessToken: string; email?: string | null }): {
accountId?: string;
chatgptPlanType?: string;
email?: string;
profileName?: string;
} {
const payload = decodeCodexJwtPayload(params.accessToken);
const auth = payload?.["https://api.openai.com/auth"];
const accountId = trimNonEmptyString(auth?.chatgpt_account_id);
const chatgptPlanType = trimNonEmptyString(auth?.chatgpt_plan_type);
const email =
trimNonEmptyString(payload?.["https://api.openai.com/profile"]?.email) ??
trimNonEmptyString(params.email);
const metadata = {
...(accountId ? { accountId } : {}),
...(chatgptPlanType ? { chatgptPlanType } : {}),
};
if (email) {
return { email, profileName: email };
return { ...metadata, email, profileName: email };
}
const stableSubject = resolveCodexStableSubject(payload);
if (!stableSubject) {
return {};
return metadata;
}
return {
...metadata,
profileName: `id-${Buffer.from(stableSubject).toString("base64url")}`,
};
}

View File

@@ -225,13 +225,13 @@ describe("openai codex provider", () => {
access:
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL2F1dGgiOnsiY2hhdGdwdF9hY2NvdW50X2lkIjoiYWNjdC1kZXZpY2UtMTIzIn19.signature",
refresh: "device-refresh-token",
accountId: "acct-device-123",
},
},
],
defaultModel: "openai-codex/gpt-5.5",
});
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 () => {

View File

@@ -304,17 +304,33 @@ function withDefaultCodexContextMetadata(params: {
};
}
function buildCodexCredentialExtra(identity: {
accountId?: string;
chatgptPlanType?: string;
}): Record<string, unknown> | undefined {
const extra = {
...(identity.accountId ? { accountId: identity.accountId } : {}),
...(identity.chatgptPlanType ? { chatgptPlanType: identity.chatgptPlanType } : {}),
};
return Object.keys(extra).length > 0 ? extra : undefined;
}
async function refreshOpenAICodexOAuthCredential(cred: OAuthCredential) {
try {
const { refreshOpenAICodexToken } = await import("./openai-codex-provider.runtime.js");
const refreshed = await refreshOpenAICodexToken(cred.refresh);
const identity = resolveCodexAuthIdentity({
accessToken: refreshed.access,
email: cred.email,
});
return {
...cred,
...refreshed,
type: "oauth" as const,
provider: PROVIDER_ID,
email: cred.email,
email: identity.email ?? cred.email,
displayName: cred.displayName,
...buildCodexCredentialExtra(identity),
};
} catch (error) {
const message = formatErrorMessage(error);
@@ -359,6 +375,7 @@ async function runOpenAICodexOAuth(ctx: ProviderAuthContext) {
expires: creds.expires,
email: identity.email,
profileName: identity.profileName,
credentialExtra: buildCodexCredentialExtra(identity),
});
}
@@ -409,6 +426,7 @@ async function runOpenAICodexDeviceCode(ctx: ProviderAuthContext) {
expires: creds.expires,
email: identity.email,
profileName: identity.profileName,
credentialExtra: buildCodexCredentialExtra(identity),
});
} catch (error) {
spin.stop("OpenAI device code failed");