[FEATURE] Reduce keychain prompt friction for third-party tools (usage cache or credential export)

Resolved 💬 2 comments Opened Jan 31, 2026 by NSEvent Closed Mar 1, 2026

Summary

Third-party tools that need to access Claude Code usage data (like menu bar apps) cause repeated macOS keychain prompts because the Keychain ACL is reset every time Claude CLI refreshes tokens. This creates a poor user experience with 5-10+ password prompts per day.

Problem Description

On macOS, Claude Code stores OAuth credentials in the system Keychain with service name Claude Code-credentials. When third-party apps access this item:

  1. First access works fine - User grants "Always Allow" in the keychain prompt
  2. Token refresh breaks ACL - When Claude CLI refreshes the token (every ~8 hours), it deletes and recreates the keychain item, resetting the ACL
  3. New prompt required - The third-party app no longer has permission and user sees another password prompt
  4. Cycle repeats - With typical usage patterns, this means 5-10+ prompts per day

This is related to #19456 (keychain deletion failures) and #19078 (OAuth token expiration), but affects a different scenario: legitimate third-party tools rather than Claude Code itself.

Current Behavior

Third-party app polls usage API every 5 minutes
→ Needs OAuth token from keychain
→ First call: user grants access (one prompt)
→ 8 hours later: Claude CLI refreshes token, recreates keychain item
→ ACL reset, third-party app loses access
→ Next poll: user prompted again
→ Repeat 2-3x per day minimum

Impact

  • CodexBar - macOS menu bar app showing Claude usage
  • Any future tools that want to display/monitor Claude Code usage
  • Users report this as one of the main pain points

Proposed Solutions (in order of preference)

Option A: Usage Data Cache File (Recommended)

As requested in #21943 - write usage data to ~/.claude/usage-cache.json. Third-party tools read the cache instead of needing credentials.

Benefits:

  • No credential sharing needed
  • No security concerns
  • Third-party tools get read-only access to usage data only
  • Cleanest separation of concerns
Option B: Token Export Command

Add a command to output the current OAuth token:

claude auth token          # Outputs access token
claude auth token --json   # Full credentials as JSON

Third-party tools can invoke this instead of reading keychain directly. Claude CLI handles keychain access (it has permission).

Benefits:

  • Works with existing infrastructure
  • Claude CLI already handles token refresh

Concerns:

  • Exposes raw tokens to any local process (but env vars already do this)
Option C: Credentials File on macOS

Write credentials to ~/.claude/.credentials.json on macOS (like Linux does). Third-party tools read the file instead of keychain.

Benefits:

  • Consistent behavior across platforms
  • Simple implementation (Linux code path already exists)

Concerns:

  • Credentials in plain file (but Linux already does this)

Workaround Attempts

  1. Environment variable (CODEXBAR_CLAUDE_OAUTH_TOKEN) - Requires manual token copy, expires in ~8 hours, not viable
  2. CodexBar's own keychain cache - Helps but still needs initial read from Claude's keychain, prompt happens on every refresh cycle
  3. File fallback - Doesn't exist on macOS (~/.claude/.credentials.json not written)

Environment

  • macOS 15.x (Sequoia)
  • Claude Code 2.1.x
  • Affects any third-party app accessing credentials

Related Issues

  • #21943 - Feature request for usage data cache (would solve this)
  • #19456 - Keychain deletion failures during token refresh
  • #19078 - OAuth token expiration issues

Summary

The core issue is that macOS Keychain ACLs don't survive token refresh cycles. Rather than fighting the Keychain system, providing an alternative access method (cache file, token command, or credentials file) would give third-party tools a reliable way to access usage data without causing repeated prompts.

I'd be happy to contribute a PR for any of these solutions if the team has a preferred approach.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗