fix(auth): keep fresher Codex re-auth state#53611
Conversation
Greptile SummaryThis PR fixes two related auth-state bugs in the Codex CLI credential pipeline: a stale in-memory cache surviving a
Confidence Score: 5/5
Reviews (1): Last reviewed commit: "fix(auth): protect fresher codex reauth ..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2075d9de2e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (options.log !== false) { | ||
| log.debug(`kept newer stored ${provider} credentials over external cli sync`, { | ||
| profileId, | ||
| storedExpires: new Date(existingOAuth!.expires).toISOString(), |
There was a problem hiding this comment.
Avoid throwing when logging newer stored expiry
If auth-profiles.json contains an out-of-range numeric expires value (which is currently accepted during store coercion), this debug branch can throw RangeError: Invalid time value at runtime because new Date(existingOAuth!.expires).toISOString() is not guarded. That exception aborts external CLI sync (and therefore auth-store load paths that call it) instead of safely keeping or replacing credentials. Please guard storedExpires the same way as externalExpires (or log the raw number) before calling toISOString().
Useful? React with 👍 / 👎.
- invalidate cached Codex CLI credentials when auth.json changes within the TTL window - skip external CLI sync when the stored Codex OAuth credential is newer - cover both behaviors with focused regression tests Refs #53466 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2075d9d to
7689d1d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7689d1dfa0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (shallowEqualOAuthCredentials(existingOAuth, creds)) { | ||
| return false; | ||
| } | ||
| if (hasNewerStoredOAuthCredential(existingOAuth, creds)) { |
There was a problem hiding this comment.
Scope newer-token skip logic to Codex only
The new hasNewerStoredOAuthCredential short-circuit runs in the shared syncExternalCliCredentialsForProvider path, so it now changes behavior for Qwen and MiniMax as well as Codex. That means external sync can silently stop updating those profiles whenever the stored expires is higher than the incoming one, even if access/refresh tokens differ (for example after a CLI re-auth/account switch with a shorter lifetime), leaving stale credentials in auth-profiles.json instead of mirroring the external CLI state.
Useful? React with 👍 / 👎.
|
Landed via temp rebase onto main.
Thanks @giulio-leone! |
Summary
~/.codex/auth.jsonchanges, even inside the normal TTL windowopenai-codex:defaultOAuth credential is already newer than the imported oneFixes #53466.
Root cause
readCodexCliCredentialsCached()only honored a time-based TTL, so a stale external Codex credential could survive in memory afterauth.jsonchanged.Later,
syncExternalCliCredentials()would blindly replace the storedopenai-codex:defaultcredential whenever the imported OAuth payload differed, even if the stored credential had a later expiry from a freshopenclaw models auth loginre-auth flow.What changed
src/agents/cli-credentials.tsauth.jsonmtime alongside the cache entrysrc/agents/auth-profiles/external-cli-sync.tsValidation
Focused suite, run twice consecutively on the final code state:
pnpm exec vitest --run \ src/agents/cli-credentials.test.ts \ src/agents/auth-profiles.external-cli-sync.test.ts \ src/agents/auth-profiles.store-cache.test.ts \ src/agents/auth-profiles.markauthprofilefailure.test.ts \ src/agents/auth-profiles/oauth.openai-codex-refresh-fallback.test.tsBoth consecutive runs passed: 27/27 tests each time.
Live proof 1 (changed
auth.jsoninvalidates the stale cached Codex credential inside the TTL window):{ "cacheProof": true, "firstRefresh": "stale-external-refresh", "secondRefresh": "fresh-external-refresh" }Live proof 2 (a warmed stale external Codex credential no longer overwrites a fresher stored
openai-codex:defaultcredential on the real auth-store load path):{ "overwriteProof": true, "finalRefresh": "fresh-store-refresh" }The repo commit hook also completed successfully and committed the change after running the project
pnpm checkstack.