fix(auth): self-heal Codex OAuth token drift - #45573
Merged
Merged
Conversation
… ~/.codex Hermes keeps its own copy of the Codex OAuth token per profile and at the top level, separate from the Codex CLI's ~/.codex/auth.json. OAuth refresh_tokens are single-use, so when the Codex CLI (or another Hermes process) rotates the shared token, the frozen copy's refresh_token goes stale and refresh_codex_oauth_pure fails with a relogin-required error (invalid_grant / refresh_token_reused / 401). Today that surfaces as a hard 401 on the turn — idle profiles and desktop sessions 401 "token_expired" until a manual re-auth — even though ~/.codex/auth.json holds a fresh token. _refresh_codex_auth_tokens now falls back to _import_codex_cli_tokens() (the canonical Codex CLI store) when the stored refresh_token is rejected, adopts and persists the fresh token, and lets the in-flight retry succeed. This complements PR #6525 (force relogin on 401/403): we attempt automatic recovery before surfacing a relogin prompt. Transient failures (e.g. 429 quota, relogin_required=False) are never self-healed — the stored token is still valid there — so they re-raise unchanged, and the happy path is untouched. Adds tests/hermes_cli/test_auth_codex_self_heal.py covering: self-heal on invalid_grant, no self-heal on 429 quota, re-raise when ~/.codex is absent, and happy-path-unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r email
Addresses PR review feedback:
- Validate refresh_token (not only access_token) before persisting the
re-imported Codex token, so a half-token payload can't silently break the
next refresh cycle.
- Make the recovery log path-agnostic ("Codex CLI auth.json") since
_import_codex_cli_tokens can read $CODEX_HOME, not only ~/.codex.
- Add regression test: relogin-required + imported token missing refresh_token
-> re-raise and persist nothing.
- Map kenmege@yahoo.com -> Kenmege in scripts/release.py AUTHOR_MAP
(fixes the check-attribution job).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Recover Codex singleton auth entries that have a refresh token but no access token by adopting a valid Codex CLI token pair, matching the cron-time failure mode before falling back to the credential pool.
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-assignment |
1 |
no-matching-overload |
1 |
unresolved-import |
1 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`
hermes_cli/auth.py:3808: [no-matching-overload] no-matching-overload: No overload of `dict.__init__` matches arguments
tests/hermes_cli/test_auth_codex_self_heal.py:15: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
✅ Fixed issues (2):
| Rule | Count |
|---|---|
unresolved-attribute |
2 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
run_agent.py:2891: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`
Unchanged: 5709 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Codex OAuth recovery now handles both stale refresh_token rotation and the cron-time missing access_token state by adopting a valid Codex CLI token pair before asking the user to re-authenticate.
Changes
hermes_cli/auth.py: shared Codex CLI token adoption helper; uses it for rejected refresh tokens and malformed/missing singleton token state.tests/hermes_cli/test_auth_codex_self_heal.py: covers stale refresh tokens, malformed imports, and the exact missing-access-token cron failure path.tests/hermes_cli/test_auth_codex_provider.py: isolates the legacy missing-access-token error test from host Codex CLI credentials.scripts/release.py: maps @Kenmege's author email for attribution checks.Validation
python -m py_compile hermes_cli/auth.py tests/hermes_cli/test_auth_codex_self_heal.py tests/hermes_cli/test_auth_codex_provider.pyscripts/run_tests.sh tests/hermes_cli/test_auth_codex_self_heal.py tests/hermes_cli/test_auth_codex_provider.pyresolve_codex_runtime_credentials()with malformed Hermes auth + validCODEX_HOME/auth.jsonfresh-accessand persisted both tokensSalvages #45261 with @Kenmege's authorship preserved, then widens the fix to the missing
access_tokenpath from the reported cron failure.Infographic