feat(claude): read OAuth usage per CLAUDE_CONFIG_DIR account#1829
feat(claude): read OAuth usage per CLAUDE_CONFIG_DIR account#1829LawyZheng wants to merge 1 commit into
Conversation
Claude Code isolates each `CLAUDE_CONFIG_DIR` into its own macOS Keychain entry: `Claude Code-credentials` for the default `~/.claude`, and `Claude Code-credentials-<hash>` for a custom dir, where `<hash>` is the first 8 hex chars of the config directory path's SHA-256. Credentials also live at `$CLAUDE_CONFIG_DIR/.credentials.json`. Previously the service name and file path were hardcoded to the default account, so CodexBar could only ever read one Claude subscription. This makes credential resolution config-dir aware so a single CodexBar process can read usage for multiple Claude accounts by supplying a per-account `CLAUDE_CONFIG_DIR` in the fetch environment (the environment dictionary already flows through the whole read path). Changes: - Derive the Keychain service name from the active `CLAUDE_CONFIG_DIR` (verified against a real entry: sha256(dir)[0..<8]). Turning the single `claudeKeychainService` constant into a computed property makes all 14 existing references (queries, preflight, security-CLI reader, logs) resolve to the correct account with no other call-site changes. - Resolve `$CLAUDE_CONFIG_DIR/.credentials.json` for the file fallback. - Isolate the in-memory and Keychain credential caches per account so two accounts never cross-read; the default account's cache key is unchanged (backward compatible). - Inject the config dir as a TaskLocal at the `loadRecord` entry point so the whole synchronous read path sees it. Add ClaudeOAuthConfigDirAccountTests covering service-name derivation (incl. default = suffix-less, trailing-slash normalization), reading a credentials file from a custom config dir, and the memory cache not leaking tokens between accounts. This is infrastructure only: no UI, account enumeration, or credential storage is added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex review: needs real behavior proof before merge. Reviewed July 1, 2026, 3:30 PM ET / 19:30 UTC. Summary Reproducibility: yes. for the review finding: source inspection of the PR head shows Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Either approve a native Do we have a high-confidence way to reproduce the issue? Yes for the review finding: source inspection of the PR head shows Is this the best way to solve the issue? No; the implementation should make account scope an explicit credential-context concern across all credential probes and refresh paths, not a TaskLocal wrapper around only the main load path. Maintainers also need to choose whether native Keychain/config-dir reads fit the approved multi-account direction. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against c3d33308ac06. Label changesLabel changes:
Label justifications:
Evidence reviewedSecurity concerns:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Thanks @LawyZheng for investigating this and for adding focused tests. I am closing this implementation while keeping #1756 open for the product decision. This is an auth/storage feature, so VISION requires owner sign-off. The current decision record in #1812 explicitly holds native credential reads and recommends Phase 1 as an opt-in, read-only The audited head
There is no independently useful narrow seam to retain here: the cache-key overload has no approved account consumer, and the remaining changes encode the held native-Keychain design. If native credential discovery is approved later, it should start from an explicit account context threaded through every credential/cache/probe/refresh path and an account-aware usage/UI model, rather than this TaskLocal patch. Canonical path forward: #1812 and #1756. Thanks again for contributing the investigation. |
Summary
Makes Claude OAuth credential resolution
CLAUDE_CONFIG_DIR-aware so a single CodexBar process can read usage for multiple Claude subscription accounts — without an external dependency or manual token pasting.Background
Claude Code isolates each
CLAUDE_CONFIG_DIRinto its own macOS Keychain entry and credentials file:~/.claude(default)Claude Code-credentials~/.claude/.credentials.jsonCLAUDE_CONFIG_DIRClaude Code-credentials-<hash>$CLAUDE_CONFIG_DIR/.credentials.json<hash>is the first 8 hex chars of the config directory path's SHA-256. Verified against a real machine:sha256("/Users/…/.claude-work")[0..<8]→da6d47a8→ Keychain entryClaude Code-credentials-da6d47a8.Previously
claudeKeychainServiceand the credentials file path were hardcoded to the default account, so CodexBar could only ever read one Claude subscription (#81, #1756). Because Claude Code creates a distinct Keychain entry per config dir, deriving the service name from the activeCLAUDE_CONFIG_DIRis enough to read any account's usage.What changed
claudeKeychainServicebecomes a computed property derived from the activeCLAUDE_CONFIG_DIR. Making the single constant computed means all 14 existing references (candidate probes, preflight, thesecurity find-generic-password -sreader, and log labels) resolve to the correct account with no other call-site changes.defaultCredentialsURL()resolves$CLAUDE_CONFIG_DIR/.credentials.jsonfor the file fallback.KeychainCacheStore.Key.oauthgains an optionalaccountScopefollowing the existingcookie(…scopeIdentifier:)pattern.TaskLocalat theloadRecordentry point (theenvironmentdict already flows through the whole read path), so the entire synchronous read sees it.How it enables multi-account
A caller reads a specific account's usage by supplying its
CLAUDE_CONFIG_DIRin the fetch environment, e.g.["CLAUDE_CONFIG_DIR": "~/.claude-work"]. The default (empty /~/.claude) path is completely unchanged.This PR is infrastructure only — it does not add UI, account enumeration, or credential storage. It's the credential-layer primitive any multi-account approach (menu items,
claude-swap, etc.) needs.Tests
ClaudeOAuthConfigDirAccountTests:-<hash>; trailing-slash normalization; hash == sha256 first 8 hex)The hash/normalization logic was also cross-checked independently against the real Keychain entry on macOS.
Related
🤖 Generated with Claude Code