Skip to content

Conversation

@manikv12
Copy link

@manikv12 manikv12 commented Jan 25, 2026

Fixes #243

Summary

  • Fixes repeated keychain password prompts even after user grants "Always Allow" access to Claude Code credentials
  • Adds silent keychain access to avoid unexpected prompts during background operations
  • Caches credentials in CodexBar's own keychain to minimize access to Claude Code's keychain

Changes

  • Add allowKeychainPrompt parameter to ClaudeOAuthCredentialsStore.load() to control prompt behavior
  • Add loadFromClaudeKeychainWithoutPrompt() using LAContext.interactionNotAllowed to silently fail
  • Add invalidateCacheIfClaudeKeychainChanged() to detect credential changes without prompting
  • Skip keychain prompts during background refreshes and availability checks
  • Cache credentials in CodexBar's own keychain after first successful access
  • Re-migrate Claude credentials when they change to maintain proper accessibility settings

Test plan

  • All 548 tests pass (pre-existing BrowserDetection test failures unrelated to this change)
  • Verify keychain prompts are reduced when using Claude provider
  • Verify credentials are properly cached after first access
  • Verify account switches are detected and cache is invalidated

CodexBar was prompting for keychain password repeatedly even after
the user granted "Always Allow" access to Claude Code credentials.

Changes:
- Add `allowKeychainPrompt` parameter to `ClaudeOAuthCredentialsStore.load()`
- Add `loadFromClaudeKeychainWithoutPrompt()` using LAContext to silently fail
- Add `invalidateCacheIfClaudeKeychainChanged()` to detect credential changes
- Skip keychain prompts during background refreshes and availability checks
- Cache credentials in CodexBar's own keychain after first successful access
- Re-migrate Claude credentials when they change to update accessibility
Copilot AI review requested due to automatic review settings January 25, 2026 07:48
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2fa96efbe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #243 where CodexBar repeatedly prompts for keychain password despite "Always Allow" being selected. The fix implements a multi-layered caching strategy and silent keychain access to minimize user prompts.

Changes:

  • Added allowKeychainPrompt parameter to control when keychain prompts are shown, preventing unexpected prompts during background operations
  • Implemented silent keychain access using LAContext.interactionNotAllowed to read credentials without triggering prompts
  • Added credential caching in CodexBar's own keychain to reduce access frequency to Claude Code's keychain
  • Implemented change detection for both file-based and keychain-based credentials to maintain cache validity
  • Enhanced migration logic to re-migrate Claude credentials when they change

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift Core implementation: added allowKeychainPrompt parameter, silent keychain access methods, cache invalidation logic, and file fingerprinting to detect credential changes
Sources/CodexBar/KeychainMigration.swift Added logic to re-migrate Claude credentials when they change, tracking credentials via fingerprints to maintain proper accessibility settings
Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift Updated to use allowKeychainPrompt: false during background refreshes to prevent prompts
Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift Updated availability check to use allowKeychainPrompt: false to prevent prompts during background checks
Sources/CodexBar/UsageStore.swift Updated debug dump to use allowKeychainPrompt: false to prevent prompts during diagnostic operations
Sources/CodexBar/UsageStore+Refresh.swift Added cache invalidation check after fetching to detect credential changes and trigger re-fetch if needed
Tests/CodexBarTests/ClaudeOAuthCredentialsStoreTests.swift Added tests for cache invalidation when credentials file changes, with some test setup improvements
Tests/CodexBarTests/KeychainMigrationTests.swift Added test entries for new keychain items and migration tracking reset functionality
CHANGELOG.md Documented the bug fix with details of the implementation approach

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Remove redundant fingerprint check in cached credentials (already validated by invalidateCacheIfCredentialsFileChanged)
- Remove redundant KeychainAccessGate assignment in test
- Add comment explaining cache re-store in test
@elpinguinofrio
Copy link

ser, pls approve, it's annoying, compute is idling while waiting user to "always allow" every time
Screenshot 2026-01-25 at 9 35 39 AM

@manikv12
Copy link
Author

manikv12 commented Jan 25, 2026

@steipete

⚠️ Known Limitation: Token Refresh May Require Re-authorization

While this PR significantly reduces keychain prompts, there's one scenario where prompts may still occur:

When Claude Code refreshes expired OAuth tokens (typically every few hours/days), it updates the keychain item which can reset the Access Control List (ACL). This means:

  1. CodexBar's "Always Allow" permission may be cleared
  2. The next time CodexBar tries to access credentials, it may prompt again
  3. After re-granting "Always Allow", the credentials are cached in CodexBar's own keychain

Why this happens:

  • Claude Code owns the Claude Code-credentials keychain item
  • When updating expired tokens, macOS may reset the ACL to default (owner-only)
  • CodexBar cannot programmatically modify another app's keychain ACL

Mitigation in this PR:

  • CodexBar caches credentials in its own keychain after first successful access
  • Silent detection of credential changes (invalidateCacheIfClaudeKeychainChanged())
  • Background refreshes use allowKeychainPrompt: false to avoid unexpected prompts
  • Re-migration attempts to restore proper accessibility when credentials change

Result: Prompts are reduced from "every access" to "occasionally after token refresh" - a significant improvement but not completely eliminated.

Complete fix would require: Claude Code to preserve third-party app ACL entries when updating credentials (feature request to Anthropic).

@tylerseymour
Copy link

Does this address the scenario where the user does not wish to grant keychain access at all? I'm not sure why this is a mandatory requirement when just processing .codex/ folder data...

@manikv12
Copy link
Author

@tylerseymour It wont address that you need permission to token to be able to get your usage from anthropic API what other way you will get your usage ?

@elpinguinofrio
Copy link

can we please merge this at least partial solution instead of waiting for perfect solution and address future issues in the future?

@manikv12
Copy link
Author

Its not on my hand. Only project owner can merge it.

@steipete
Copy link
Owner

I'm sorry. The internet is exploding rn.

@manikv12
Copy link
Author

@elpinguinofrio
Have you pulled down the branch and tested this branch on your local if it fixes your issue?

./Scripts/compile_and_run.sh

@elpinguinofrio
Copy link

@manikv12 Hi! I'd like to help test this PR but can't build from source - the KeyboardShortcuts dependency fails with #Preview macro errors (I have Command Line
Tools but not full Xcode).

Tried:

  • swift test
  • ./Scripts/compile_and_run.sh

Both fail with:
external macro implementation type 'PreviewsMacros.SwiftUIView' could not be found for macro 'Preview(_:body:)'

Could you please provide a pre-built binary/DMG so I can test the keychain prompt behavior?

@manikv12
Copy link
Author

CodexBar-0.18.0-beta.2.zip
Try this.

@manuel-soria
Copy link

manuel-soria commented Jan 30, 2026

dude built clawdbot and probably forgot about codex bar so this VERY MUCH NEEDED pr will never get merged

I just don't understand how it's not terribly annoying for him as well

love codexbar btw, but PLEASE APPROVE THIS PR @steipete

@manikv12
Copy link
Author

@manuel-soria Dont worry he is getting back at it. He just have too many things to worry about because of OpenClaw .

@gingerbeardman
Copy link

I just don't understand how it's not terribly annoying for him as well

oh it is @manuel-soria it popped up in the middle of one of his recent presentations in London

@odysseus0
Copy link

Tested on macOS 26.2 Tahoe, Apple M4 Max.

Build & tests:swift build succeeds, 548 tests pass

Manual testing:

  • First launch: prompted for keychain access (expected)
  • Clicked "Always Allow"
  • Relaunched 3x — no further prompts ✅

(Note: Debug builds have inconsistent code signatures, so keychain ACL behavior differs from signed releases. Tested the logic path as best as possible in dev environment.)

Code review notes:

Found two edge cases worth flagging:

  1. OAuth-only users with keychain-only credentials can't bootstraploadViaOAuth() hardcodes allowKeychainPrompt: false, so if a user has no ~/.claude/.credentials.json file and no CodexBar cache yet, the initial prompt needed to populate the cache is blocked. Suggest allowing prompt when no cached credentials exist:
// ClaudeUsageFetcher.swift:266-268
let hasCache = ClaudeOAuthCredentialsStore.hasCachedCredentials()
let creds = try ClaudeOAuthCredentialsStore.load(
    environment: self.environment,
    allowKeychainPrompt: !hasCache)
  1. Token refresh timestamp not cleared on cache invalidationUsageStore+Refresh.swift:46-55 clears tokenSnapshots but not lastTokenFetchAt, so token usage stays empty for up to 1 hour after credential file change. Suggest also clearing lastTokenFetchAt[.claude].

Neither blocks the main fix. Happy to open a follow-up PR for these if helpful.

LGTM 👍

@manikv12
Copy link
Author

@odysseus0 Good edge cases you can push changes to this Branch if you would like to i have no problem with that.

Add automatic token refresh using Anthropic's OAuth token endpoint.
When the access token expires, CodexBar now refreshes it using the
refresh token and saves the new credentials to its own keychain cache.

This eliminates the need for users to run `claude` CLI to refresh
tokens. Users will only be prompted for keychain access on:
- First time setup (initial read from Claude's keychain)
- Account switches

Changes:
- Add loadWithAutoRefresh() async method that handles token refresh
- Add refreshAccessToken() to call Anthropic's /v1/oauth/token endpoint
- Add new error cases: refreshFailed, noRefreshToken
- Make OAuth client ID configurable via CODEXBAR_CLAUDE_OAUTH_CLIENT_ID
- Update ClaudeUsageFetcher to use auto-refresh method
@manikv12
Copy link
Author

✅ Token Refresh Limitation Now Resolved

The previous "Known Limitation" about token refresh prompts has been addressed in commit 968e52e.

What changed:

CodexBar now refreshes OAuth tokens automatically using Anthropic's /v1/oauth/token endpoint, rather than waiting for Claude CLI to do it. The refreshed tokens are saved to CodexBar's own keychain cache (com.steipete.codexbar.cache), not Claude's keychain.

Flow:

Token expired?
    ↓
CodexBar calls Anthropic's token endpoint directly
    ↓
Gets new access_token + refresh_token
    ↓
Saves to CodexBar's keychain (no ACL issues)
    ↓
Done - no prompts needed!

When users will still be prompted:

  • First time setup - Initial read from Claude's keychain to get the refresh token
  • Account switch - When user logs into a different Claude account

When users will NOT be prompted:

  • Token expiration (handled by auto-refresh)
  • App restarts (reads from CodexBar's cache)
  • Claude CLI updating its own keychain (we don't read from it after bootstrap)

This implementation is inspired by the opencode-anthropic-auth plugin approach.

Copy link

@rjm11 rjm11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally (build passes ✅). Code review looks solid:

What this fixes:

  • Adds allowKeychainPrompt parameter to prevent unexpected prompts during background refreshes
  • Uses LAContext.interactionNotAllowed for silent keychain reads
  • Caches credentials in CodexBar's own keychain after first successful access
  • Detects credential file/keychain changes via fingerprinting to invalidate stale cache

Why it works:
The root cause is that accessing Claude's keychain item ("Claude Code-credentials") triggers macOS prompts even after "Always Allow" because the ACL changes between app versions or code signatures. By caching in CodexBar's own keychain and only falling back to Claude's keychain when necessary (and only when prompts are allowed), this significantly reduces prompt frequency.

Note: Tests hang in dev builds without proper code signing (expected - they're waiting for keychain prompts). The production build should work fine since it's properly signed.

Ready to merge. 🦞

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.

"Always Allow" keychain prompt does not persist – repeatedly asks for login password on macOS

9 participants