Skip to content

fix(claude): Read the session from the macOS login keychain - #39

Merged
euxaristia merged 1 commit into
mainfrom
fix/claude-macos-keychain
Aug 25, 2026
Merged

fix(claude): Read the session from the macOS login keychain#39
euxaristia merged 1 commit into
mainfrom
fix/claude-macos-keychain

Conversation

@euxaristia

Copy link
Copy Markdown
Owner

Summary

macOS Claude Code writes no ~/.claude/.credentials.json. It keeps the OAuth
session in the login keychain under the service Claude Code-credentials, keyed
by the OS username rather than a fixed account name. load_claude() probed only
that file and three fixed service/account pairs, so every macOS install reported
Claude as UNCONFIGURED while Linux and Windows, which do get the file, worked.

Closes #38

Changes

  • keyring::read() takes an optional account. None matches on the service
    alone: no -a on macOS, no username on Linux, and the bare service as the
    target name on Windows. Guessing the username was the alternative, and this
    crate has no business doing that.
  • load_claude() reads a new CLAUDE_KEYRING_ENTRIES list led by
    Claude Code-credentials, and takes the live candidate via pick_live()
    rather than the first one found -- the same shape load_grok() settled on in
    Grok reports HTTP 401 when a stale keyring entry shadows the CLI credential #36. A leftover credentials file from an older install would otherwise shadow
    the keychain entry Claude Code actually refreshes, and the refresh probe could
    not recover: it would refresh a store the loader had already decided to ignore.
  • The unconfigured hint no longer points only at the file path.

Test plan

  • reads_the_claude_code_keyring_entry_keyed_by_os_username injects a fake
    reader that answers only for the account-less macOS service. It fails on main
    and passes here.
  • cargo test: 161 passed. cargo clippy --all-targets: clean.
  • limits status -p claude on macOS now reports Session 22.0% (4h 8m) and
    Weekly 3.0% (6d 11h) against a live Pro account, in place of UNCONFIGURED.

load_claude() looked for ~/.claude/.credentials.json and, failing that, three
hard-coded keyring pairs: cairn-code:oauth:claude, cairn-code:claude, and
claude:credentials. macOS Claude Code writes none of them. It writes no
credentials file at all, and keeps the OAuth session in the login keychain under
the service "Claude Code-credentials", keyed by the OS username rather than a
fixed account name. So every macOS install reported Claude as UNCONFIGURED while
Linux and Windows, which do get the file, worked.

Look the entry up by service alone. keyring::read() now takes an optional
account: None drops -a on macOS and username on Linux, and falls back to the
bare service as the target name on Windows. Guessing the username would be the
alternative, and this crate has no business doing that.

Take the live credential rather than the first one found, as load_grok() does
for the same reason. Only Claude Code rewrites either copy, so a leftover
credentials file from an older install would otherwise shadow the keychain entry
it actually refreshes, and the refresh probe could never recover: it would
refresh a store the loader had already decided to ignore.

Refs #38
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d9d390d3-3725-4aaa-b8c6-9a0cc558b027

📥 Commits

Reviewing files that changed from the base of the PR and between ceaed03 and d51712d.

📒 Files selected for processing (3)
  • src/credentials/keyring.rs
  • src/credentials/mod.rs
  • src/model.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The keyring API now supports service-only searches. Claude loading reads macOS keychain credentials, evaluates all available candidates, and selects a live token. Provider guidance and coverage were updated.

Changes

Claude keyring support

Layer / File(s) Summary
Optional-account keyring lookup
src/credentials/keyring.rs
keyring::read accepts Option<&str>. Windows, macOS, and Linux omit account matching when the account is None.
Claude candidate selection
src/credentials/mod.rs, src/model.rs
Claude loading checks the username-keyed Claude Code-credentials service, parses all valid candidates, and selects a live credential. Grok and Antigravity retain account-specific lookups. Documentation and tests cover the macOS keyring path.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: 🔵 Low · up to d5171

Claude now reads macOS login-keychain credentials, but service-only matching could select the wrong Claude identity when multiple matching entries exist. The change is otherwise mergeable with explicit owner awareness or follow-up to define deterministic handling for duplicate entries.

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeLoader
  participant CredentialsFile
  participant Keyring
  participant ClaudeParser
  ClaudeLoader->>CredentialsFile: Read credentials file
  ClaudeLoader->>Keyring: Read Claude Code-credentials by service
  Keyring-->>ClaudeLoader: Return keyring credential
  ClaudeLoader->>ClaudeParser: Parse all candidates
  ClaudeParser-->>ClaudeLoader: Select live credential
Loading

Poem

A rabbit checks the keyring bright

Finds Claude’s token tucked from sight
The stale file waits, but does not win
Fresh credentials now get in
Service names guide the way
Hoppy tests confirm today

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: reading the Claude session from the macOS login keychain.
Description check ✅ Passed The description explains the macOS keychain issue, the implementation changes, and the validation results. It is directly related to the changeset.
Linked Issues check ✅ Passed The changes address issue #38 by adding account-less keychain lookup, reading the Claude Code-credentials service, selecting a live credential over a stale file, updating the configuration hint, and a…
Out of Scope Changes check ✅ Passed The changes are within scope. Updates to the shared keyring API and optional account handling support the required macOS lookup while preserving related platform credential behaviour.
Docstring Coverage ✅ Passed Docstring coverage is 82.35% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 3 files.
Full details: Linked Issues check

Explanation

The changes address issue #38 by adding account-less keychain lookup, reading the Claude Code-credentials service, selecting a live credential over a stale file, updating the configuration hint, and adding test coverage.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@euxaristia
euxaristia merged commit 25511b1 into main Aug 25, 2026
4 checks passed
@euxaristia
euxaristia deleted the fix/claude-macos-keychain branch August 25, 2026 23:52
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.

Claude reports UNCONFIGURED on macOS, where the session lives in the login keychain

1 participant