fix(auth): harden Anthropic OAuth refresh and tokenless pool hydration - #58096
fix(auth): harden Anthropic OAuth refresh and tokenless pool hydration#58096mssteuer wants to merge 1 commit into
Conversation
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved (read-only token - formal approval deferred)
Hardens Anthropic OAuth refresh and tokenless pool hydration. Key changes: (1) OAuth token endpoint now uses a non-claude-code User-Agent to avoid HTTP 429 from Anthropic; (2) token endpoint URL migrated from console.anthropic.com to claude.com (Anthropic platform migration); (3) tokenless claude_code entries in credential pool are now hydrated from ~/.claude/.credentials.json instead of being tombstamped as exhausted. Includes new test. No concerns.
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved (LGTM)
Hardens Anthropic OAuth refresh and tokenless pool hydration. Security-relevant fix for auth flows.
What Looks Good
- Security-relevant fix
- No debug artifacts
Reviewed by Hermes Agent
|
Heads up @mssteuer — the UA half of this PR just landed on The tokenless Could you rebase this onto current Reference: UA fix merged in #58178 |
3846094 to
1df298b
Compare
|
@kshitijk4poor done — thanks for the clear read. I force-pushed this PR down to the single remaining concern you called out:
What the remaining patch does: when an Anthropic pool entry is a tokenless Verification I ran locally: GitHub CI is also green now, including required checks and Docker builds. |
|
Hi, I ran into a related issue while testing multiple Claude Code accounts with Hermes. Hermes currently reads only one This work is for research and interoperability testing only. It is not intended to bypass usage limits, billing, licensing requirements, or the terms of use of either Hermes or Claude Code. Patch and test notes: https://github.com/vcnngr/hermes-claude-config-dir-patch This may be useful alongside the tokenless pool hydration work in this PR. |
|
Thanks for the focused follow-up after the OAuth-UA portion was removed. This automated hermes-sweeper review found that current
The proposed regression test reaches the force-refresh branch only by using private |
…tombstoning Our anthropic credential-pool entry is a tokenless claude_code lazy pointer (source=claude_code, auth_type=oauth, refresh_token=""); it resolves the real token from ~/.claude/.credentials.json at request time. A forced refresh (try_refresh_current -> _refresh_entry(force=True)) hit the `not entry.refresh_token` bail and called _mark_exhausted(None), writing STATUS_EXHAUSTED (1h TTL) to the SHARED auth.json -- poisoning the anthropic provider for every gateway + cron until the TTL expired or a full restart. Fix: in the tokenless bail, for an anthropic claude_code entry, hydrate from the credentials file first via _sync_anthropic_entry_from_credentials_file and only fall through to exhaustion if that yields no usable refresh token. Adapted from upstream PR NousResearch#58096. Two regression tests added (hydrate-instead-of-exhaust, and still-exhaust-when-no-creds-file). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tombstoning Our anthropic credential-pool entry is a tokenless claude_code lazy pointer (source=claude_code, auth_type=oauth, refresh_token=""); it resolves the real token from ~/.claude/.credentials.json at request time. A forced refresh (try_refresh_current -> _refresh_entry(force=True)) hit the `not entry.refresh_token` bail and called _mark_exhausted(None), writing STATUS_EXHAUSTED (1h TTL) to the SHARED auth.json -- poisoning the anthropic provider for every gateway + cron until the TTL expired or a full restart. Fix: in the tokenless bail, for an anthropic claude_code entry, hydrate from the credentials file first via _sync_anthropic_entry_from_credentials_file and only fall through to exhaustion if that yields no usable refresh token. Adapted from upstream PR NousResearch#58096. Two regression tests added (hydrate-instead-of-exhaust, and still-exhaust-when-no-creds-file). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Fixes two Anthropic OAuth/auth-pool failure modes:
claude-code/user-agent prefixes, which can trigger token-endpoint 429sclaude_codepool entries from~/.claude/.credentials.jsonbefore force-refresh marks them exhaustedWhy
A
claude_codepool entry can be a lazy reference with no in-poolaccess_token/refresh_token. Underround_robin, if that entry is selected and_refresh_entry(force=True)sees no refresh token, it currently tombstones the entry immediately. Because pool state is persisted to sharedauth.json, that synthetic exhaustion can poison all gateway/cron processes.The new behavior attempts the existing Claude credentials-file sync first, then only marks the entry exhausted if no usable token material exists.
Test plan
python -m pytest tests/agent/test_credential_pool.py tests/agent/test_anthropic_adapter.py tests/agent/test_anthropic_oauth_ua_prefix.py -qpython -m py_compile agent/credential_pool.py agent/anthropic_adapter.py tests/agent/test_credential_pool.py tests/agent/test_anthropic_adapter.py tests/agent/test_anthropic_oauth_ua_prefix.pyResult from clean worktree:
264 passed in 9.53s.Notes
I searched for existing PRs around
tokenless claude_code round_robin auth.jsonand did not find a duplicate.