fix(codex): avoid bare-email OAuth dedup (#2477)

Only update an existing Codex OAuth row when both rows share the same
chatgptAccountId, so a second Codex login no longer overwrites the first
account's rotated token pair. Also fall back to
workspaceId || chatgptAccountId || accountId for the chatgpt-account-id header.
This commit is contained in:
Hermes Hunter
2026-07-10 11:41:43 +07:00
committed by decolua
parent a3b267a5cb
commit c73c419d09
2 changed files with 23 additions and 5 deletions
+11 -4
View File
@@ -199,10 +199,17 @@ export class CodexExecutor extends BaseExecutor {
headers["session_id"] = this._currentSessionId || credentials?.connectionId || "default"; headers["session_id"] = this._currentSessionId || credentials?.connectionId || "default";
// Identify client type to Codex backend (matches official codex CLI) // Identify client type to Codex backend (matches official codex CLI)
if (!headers["originator"]) headers["originator"] = "codex_cli_rs"; if (!headers["originator"]) headers["originator"] = "codex_cli_rs";
// Workspace binding header — improves account scope + cache affinity // Account/workspace binding header — required when multiple Codex accounts
const workspaceId = credentials?.providerSpecificData?.workspaceId || credentials?.providerSpecificData?.chatgptAccountId; // are configured. OAuth import stores ChatGPT account ID as chatgptAccountId;
if (typeof workspaceId === "string" && workspaceId && !headers["ChatGPT-Account-ID"]) { // older/custom rows may use workspaceId/accountId. Prefer explicit workspaceId
headers["ChatGPT-Account-ID"] = workspaceId; // but fall back to chatgptAccountId so requests don't cross-bind to the wrong
// OpenAI account and surface as token_invalid after adding another account.
const accountId =
credentials?.providerSpecificData?.workspaceId ||
credentials?.providerSpecificData?.chatgptAccountId ||
credentials?.providerSpecificData?.accountId;
if (typeof accountId === "string" && accountId && !headers["ChatGPT-Account-ID"]) {
headers["ChatGPT-Account-ID"] = accountId;
} }
return headers; return headers;
} }
+12 -1
View File
@@ -113,7 +113,18 @@ export async function createProviderConnection(data) {
const incomingWs = data.providerSpecificData?.chatgptAccountId; const incomingWs = data.providerSpecificData?.chatgptAccountId;
existing = all.find(c => { existing = all.find(c => {
if (c.authType !== "oauth" || c.email !== data.email) return false; if (c.authType !== "oauth" || c.email !== data.email) return false;
// Workspace providers (Codex) use workspace ID when both sides have it
// Codex/OpenAI can issue multiple OAuth grants for the same email.
// Refresh tokens are rotated single-use; collapsing a new login onto an
// existing bare-email row overwrites the first account's token pair and
// makes it look "invalid" after adding a second account. Only update an
// existing Codex row when both rows expose the same ChatGPT account ID.
if (data.provider === "codex") {
const existingWs = c.providerSpecificData?.chatgptAccountId;
return !!incomingWs && !!existingWs && incomingWs === existingWs;
}
// Workspace providers use workspace ID when both sides have it
const existingWs = c.providerSpecificData?.chatgptAccountId; const existingWs = c.providerSpecificData?.chatgptAccountId;
if (incomingWs && existingWs) return incomingWs === existingWs; if (incomingWs && existingWs) return incomingWs === existingWs;
if (incomingWs && !existingWs) return false; if (incomingWs && !existingWs) return false;