Skip to content

fix: accept RT-exchanged tokens missing chatgpt_account_id claim#1

Closed
xiaoliu10 wants to merge 1 commit into
devfrom
fix/rt-import-missing-account-id
Closed

fix: accept RT-exchanged tokens missing chatgpt_account_id claim#1
xiaoliu10 wants to merge 1 commit into
devfrom
fix/rt-import-missing-account-id

Conversation

@xiaoliu10

Copy link
Copy Markdown
Owner

Summary

修复 Refresh Token 换取的 access token 缺少 chatgpt_account_id claim 时导入失败的问题。OpenAI RT exchange 返回的 access token 有时不携带 chatgpt_account_id,导致 validateManualToken 拒绝该 token,RT-only 导入无法完成。

Changes

  • src/auth/jwt-utils.ts:新增 extractCodexTokenMetadata() 从 access_token + id_token 中提取 accountId / userId / email / planType,兼容 claim 在任一 token 中的情况
  • src/services/account-import.ts:RT exchange 后用 extractCodexTokenMetadata 提取元数据传给 pool;新增 canAcceptRtExchangeToken() 允许缺少 chatgpt_account_id 但未过期的 RT 换取 token 通过验证;ImportDeps.refreshToken 返回类型增加 id_token
  • src/auth/account-registry.tsaddAccount() 新增可选 metadata 参数,当 extractChatGptAccountId 返回 null 时回退到 metadata.accountId,email/planType 同理
  • src/auth/account-pool.ts:透传 metadata 参数到 registry
  • src/auth/oauth-pkce.tsrefreshAccessToken 返回类型增加 id_token 字段
  • CHANGELOG.md[Unreleased] → Fixed 补充条目
  • 测试jwt-utils.test.ts 补充 extractCodexTokenMetadata 用例;account-import.test.ts 补充 RT exchange 缺 claim 场景;accounts-import-export.test.ts / rt-reuse-race.test.ts 补齐 extractCodexTokenMetadata mock

Test Plan

  • npm test — 251 files, 2452 passed, 1 skipped
  • npx tsc --noEmit — zero errors

Copilot AI review requested due to automatic review settings June 10, 2026 07:55
@xiaoliu10

Copy link
Copy Markdown
Owner Author

Closed because this PR was accidentally opened against the fork. Reopening against the upstream repository.

@xiaoliu10 xiaoliu10 closed this Jun 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves RT-only account import robustness by tolerating RT-exchanged access tokens that sometimes omit the chatgpt_account_id claim, extracting needed metadata from either access_token or an accompanying id_token, and plumbing that metadata through the account creation flow.

Changes:

  • Add extractCodexTokenMetadata() to derive accountId/userId/email/planType from access_token + optional id_token.
  • Update RT-only import to use extracted metadata and to allow “missing chatgpt_account_id” validation failures when the exchanged token is still fresh.
  • Extend account pool/registry APIs to accept optional metadata and add/adjust unit tests + changelog entry.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/services/account-import.test.ts Adds RT-exchange scenarios covering missing chatgpt_account_id in access_token, including fallback to id_token.
tests/unit/routes/accounts-import-export.test.ts Updates jwt-utils mocks to include extractCodexTokenMetadata.
tests/unit/auth/rt-reuse-race.test.ts Updates jwt-utils mocks to include extractCodexTokenMetadata.
tests/unit/auth/jwt-utils.test.ts Adds unit test for extractCodexTokenMetadata fallback behavior.
src/services/account-import.ts Extracts metadata from RT exchange results, passes it through to the pool, and relaxes validation for fresh RT-exchanged tokens missing chatgpt_account_id.
src/auth/oauth-pkce.ts Adds explicit scope on refresh to encourage id_token return (openid).
src/auth/jwt-utils.ts Introduces CodexTokenMetadata and extractCodexTokenMetadata() helper.
src/auth/account-registry.ts Accepts optional metadata in addAccount() and uses it as fallback for account/user/email/plan fields.
src/auth/account-pool.ts Plumbs optional metadata through to the registry.
CHANGELOG.md Adds an “Unreleased → Fixed” entry for the RT-only import compatibility fix.
Comments suppressed due to low confidence (1)

src/services/account-import.ts:83

  • The warmup hook still derives accountId only from extractChatGptAccountId(resolved.token). For RT-exchanged tokens missing chatgpt_account_id, you now have resolved.metadata?.accountId (possibly from id_token), but it won’t be used, so warmup (if enabled) will be called with a null accountId even when it’s known.
      const entryId = this.pool.addAccount(resolved.token, resolved.rt, resolved.metadata);
      this.scheduler.scheduleOne(entryId, resolved.token);

      if (entry.label) {
        this.pool.setLabel(entryId, entry.label);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -101,7 +106,7 @@ export class AccountRegistry {
existing.refreshToken = refreshToken;
}
existing.email = profile?.email ?? existing.email;
Comment thread CHANGELOG.md
- 隐式续链反向校验缺失导致客户端持续看到上游 `invalid_request_error: No tool output found for function call call_X`:`evaluateImplicitResume` 此前只做 forward 检查(新输入里的 `function_call_output.call_id` 必须命中上一轮 stored function_call),漏了反向(上一轮 stored function_call 必须在新输入里有 output)。当上一轮模型并发吐 N 个 tool_use、客户端只回 N-1 个 tool_result 时,proxy 仍然 resume + `previous_response_id` 发出去,上游存的 context 里那个未回复的 function_call 触发 400。新增反向检查 → 走完整重放(`reason: "unanswered_tool_calls"`),同时 `error-classification.ts` 加 `isUnansweredFunctionCallError`,proxy-handler catch 块兜底:strip `previous_response_id` + 完整历史重放 + 同账号重试一次(与 `previous_response_not_found` 同款),避免 ws/sse 路径上的 400 静默吞掉变成 502
- `codex-to-anthropic.ts` / `codex-to-openai.ts` / `codex-to-gemini.ts` 非流式 collect 路径里把上游错误事件抛成 `new Error(...)`,丢失 status 信息,handleNonStreaming 的 collectErr 再通过正则匹配 `HTTP/X.X NNN` 状态码必然失败 → 一律 502 兜底,客户端拿到的是模糊的 502 而不是上游真实的 400/429。改为统一 `codexApiErrorFromEvent(evt.error)` 抛 `CodexApiError(status, body)`,按 error code 映射到 400/401/402/403/429(默认 502);handleNonStreaming 的 collectErr 也加一条 `instanceof CodexApiError` 分支直接透传 status,不再走正则降级
- `streamResponse` 流式路径里上游错误此前只往 SSE 写一条 `stream_error` 事件、零日志,客户端能看到错误但 proxy `dev-YYYY-MM-DD.log` 里完全没记录,排查时无证据链。catch 块加 `console.warn` 打 `status / msg / body`,留下 call_id 等关键现场
- 修复 RT-only 导入兼容性:Refresh Token 换取的 access token 缺少 `chatgpt_account_id` 时,自动从 JWT `sub` 或导入 ID 回填账号标识,避免导入失败(`src/auth/jwt-utils.ts`、`src/services/account-import.ts`、`src/auth/account-pool.ts`、`src/auth/account-registry.ts`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants