Skip to content

[Bug]: OAuth token responses with invalid expires_in produce NaN expiries that never refresh #1417

Description

@Bruce-Yii

Client or integration

Codex CLI / Direct HTTP/API client

Area

Authentication and account pool

Summary

OAuth token responses are parsed into credential expiries in four places without a Number.isFinite guard on expires_in. A malformed upstream response (missing, NaN, or string expires_in) yields a NaN expiry that flows into downstream time comparisons, where NaN <= x is always false — the credential is treated as never-expiring and never refreshed. This is the same data-quality class as the recently merged #1366/#1369 fix (Number.isFinite guards on local token expiry parsing) that remains unfixed at the token-response parse sites.

Affected sites:

  1. src/oauth/anthropic.ts:87Date.now() + data.expires_in * 1000 - 5 * 60 * 1000 — missing expires_inundefined * 1000 = NaN
  2. src/oauth/kimi.ts:163Date.now() + payload.expires_in * 1000 - OAUTH_EXPIRY_SKEW_MS — line 154 checks typeof !== "number", but NaN passes (typeof NaN === "number")
  3. src/oauth/chatgpt.ts:55Date.now() + ((data.expires_in as number) ?? 3600) * 1000?? only guards null/undefined; NaN or a numeric string passes through
  4. src/codex/account-store.ts:498Date.now() + data.expires_in * 1000 — no validation at all

Reference implementation with the correct guard already exists at src/oauth/xai.ts:128-129:

const expiresIn = typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in) ? payload.expires_in : 3600;

Reproduction

Mock an upstream OAuth token response with expires_in missing (or null/NaN):

{ "access_token": "...", "refresh_token": "...", "expires_in": null }

Observe the stored credential carries expires: NaN, so expires <= now is always false — the credential never refreshes and ocx status reports it as logged in indefinitely (its health/refresh machinery cannot detect expiry).

Expected: a non-finite expires_in falls back to the same default used for a missing field (e.g. 3600s) so the credential stays refreshable.

Version

dev 0de4fd2d

Operating system

Cross-platform

Provider and model

anthropic / kimi / chatgpt / codex (account-store) — OAuth token response parsing

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions