Skip to content

Fix Claude Auto fallback after OAuth rotation - #2981

Merged
steipete merged 5 commits into
mainfrom
fix/2516-claude-oauth-fallback
Aug 16, 2026
Merged

Fix Claude Auto fallback after OAuth rotation#2981
steipete merged 5 commits into
mainfrom
fix/2516-claude-oauth-fallback

Conversation

@steipete

Copy link
Copy Markdown
Owner

Summary

Fixes #2516 by making Claude Auto resilient when Claude Code rotates its Keychain credential and replaces CodexBar's ACL grant.

The source fallback loop was already capable of continuing from OAuth to CLI and Web. The failure happened in the CLI availability gate: a background Auto refresh treated an unreadable OAuth credential as neither usable nor conclusively absent, then skipped the CLI even when an account-scoped CLI result had succeeded moments earlier. With no usable Web session, the OAuth error became the visible result.

What changed

  • Allow background Auto to reuse a fresh, account-scoped CLI usage result without launching Claude or touching Keychain. OAuth failures can now fall through to that working fallback and are cleared by its success.
  • Distinguish a genuinely absent credential from a previously granted Claude Code Keychain item whose access was revoked during token rotation. The latter now tells the user to click Refresh to re-grant access or switch the usage source to CLI/Web.
  • Preserve an existing Claude snapshot after all consumer Auto sources fail. If no in-memory snapshot survives, reconstruct session, weekly, and Opus bars from the selected persisted history/claude.json bucket, retain the capture timestamp, and show the capture age as stale data.
  • Keep Admin API and configured token-account authority boundaries out of the history fallback.
  • Document the behavior, localize the new user-facing strings, and add the 0.50.1 changelog entry.

Deliberately not changed

This does not attempt to preserve or rewrite the ACL on Claude Code's Claude Code-credentials item. Claude Code owns that item and replaces its ACL during token rotation; CodexBar cannot fix that behavior. Manual Refresh remains the explicit re-grant path.

Proof

  • swift test --filter ClaudeOAuthRotationErrorTests
  • swift test --filter ClaudeCLIUsageSpawnThrottleTests
  • swift test --filter ClaudeResilienceTests
  • make check
  • make test
  • autoreview: clean, no accepted/actionable findings

@clawsweeper

clawsweeper Bot commented Aug 16, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Aug 16, 2026
@clawsweeper

clawsweeper Bot commented Aug 16, 2026

Copy link
Copy Markdown

Codex review: needs changes before merge. Reviewed August 16, 2026, 1:00 PM ET / 17:00 UTC.

ClawSweeper review

What this changes

This PR changes Claude Auto to reuse recent CLI usage after OAuth Keychain access is lost, show recovery guidance, and retain stale quota history when consumer sources fail.

Merge readiness

⚠️ Ready for maintainer review - 5 items remain

Keep open: the latest head still retains both prior P2 blockers around background Claude CLI safety and revoked-access classification.

Priority: P2
Reviewed head: 4fe1ceb3e3f96da4a9324ccc4407108e4442b203

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The patch has a focused, useful direction but remains blocked by two concrete P2 correctness and Keychain-safety defects.
Proof confidence 🌊 off-meta tidepool Not applicable: This owner-authored PR is exempt from the external-contributor real-behavior-proof gate; its reported focused tests remain supplemental validation.
Patch quality 🦐 gold shrimp (3/6) Security review found an item that needs attention.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: This owner-authored PR is exempt from the external-contributor real-behavior-proof gate; its reported focused tests remain supplemental validation.
Evidence reviewed 7 items Current head is not on main or in a release: The PR head is not an ancestor of current main, and no local tag contains it, so current main does not yet include this fix.
Owner-recovery cache bypass remains: The supplied current-head patch makes cached CLI usage immediately available during background Auto, while the existing fetch path deliberately excludes owner-only recovery from that cache and launches Claude instead.
Established no-UI boundary: Current documentation requires Auto availability checks to use non-interactive loads and respect the Keychain prompt cooldown; the proposed availability shortcut can admit an owner-recovery fetch that spawns the opaque Claude child.
Findings 2 actionable findings [P2] Keep owner-only recovery behind the availability gate
[P2] Require both rotation signals for revoked-access guidance
Security Needs attention Background recovery can launch an opaque Keychain child: The shortcut declares the owner-only recovery available from a cached result, but its fetch path does not use that cache and launches Claude during a scheduled refresh.

How this fits together

Claude Auto selects OAuth, CLI, or web usage sources and publishes the resulting quota snapshot to the menu bar. The patch changes its OAuth-to-CLI fallback and the stale-data behavior when no live consumer source succeeds.

flowchart LR
A[Claude Auto refresh] --> B[OAuth credential read]
B --> C{Credential usable?}
C -->|No| D[CLI or web fallback]
C -->|Revoked| E[Recovery guidance]
D --> F[Usage snapshot]
D -->|All fail| G[Stored quota history]
G --> F
F --> H[Menu bar display]
Loading

Before merge

  • Keep owner-only recovery behind the availability gate (P2) - The cached-result shortcut has no !context.claudeOwnerCLIRecoveryOnly guard. For an owner-recovery pass it returns available, but fetch deliberately bypasses that cache and launches Claude, allowing a scheduled refresh to evade the existing opaque-child/Keychain safety gate. Restrict this shortcut to non-recovery contexts and add the regression.
  • Require both rotation signals for revoked-access guidance (P2) - keychainAccessDenied alone selects keychainAccessRevoked, so a denial cooldown after logout or credential removal can show token-rotation recovery guidance without a stored grant or logged-in profile. Require the prior-grant and profile evidence together; otherwise preserve the missing-credentials result.
  • Resolve security concern: Background recovery can launch an opaque Keychain child - The shortcut declares the owner-only recovery available from a cached result, but its fetch path does not use that cache and launches Claude during a scheduled refresh.
  • Resolve merge risk (P1) - A scheduled account-recovery refresh can pass the new cached-availability shortcut, then bypass the cache in fetch and launch Claude across the background Keychain boundary.
  • Resolve merge risk (P1) - A Keychain denial cooldown after logout or credential removal can incorrectly tell an unauthenticated user that token rotation revoked access.

Findings

  • [P2] Keep owner-only recovery behind the availability gate — Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift:996-1004
  • [P2] Require both rotation signals for revoked-access guidance — Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:1980-1983
  • [medium] Background recovery can launch an opaque Keychain child — Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift:996
Agent review details

Security

Needs attention: The cached availability change can bypass the background Keychain no-UI safety boundary during owner-only recovery.

Review metrics

Metric Value Why it matters
Patch surface 40 files affected; production +320/-54, tests +245/-36 The change spans fallback policy, persistent history, UI strings, and regression coverage, so the background and account-boundary paths need complete review.

Merge-risk options

Maintainer options:

  1. Repair both fallback guards before merge (recommended)
    Limit the cached availability shortcut to non-recovery contexts and make revoked-access messaging require the recorded-grant and active-profile signals.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Restrict cached CLI availability to non-recovery Auto contexts, require both rotation-evidence signals for revoked-access messaging, and add focused regressions.

Technical review

Best possible solution:

Restrict cached CLI availability to non-recovery Auto contexts, require both prior-grant and active-profile evidence for revoked-access guidance, and cover both cases with focused regressions.

Do we have a high-confidence way to reproduce the issue?

Yes, from source: seed a cached CLI result, enter background Auto with owner-only recovery, and the availability shortcut admits a fetch that intentionally bypasses that cache; separately, set the denial cooldown without grant/profile evidence and observe revoked-access classification.

Is this the best way to solve the issue?

No: the central fallback approach is appropriate, but the two guards above must be tightened to preserve the existing no-UI and authentication-state contracts.

Full review comments:

  • [P2] Keep owner-only recovery behind the availability gate — Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift:996-1004
    The cached-result shortcut has no !context.claudeOwnerCLIRecoveryOnly guard. For an owner-recovery pass it returns available, but fetch deliberately bypasses that cache and launches Claude, allowing a scheduled refresh to evade the existing opaque-child/Keychain safety gate. Restrict this shortcut to non-recovery contexts and add the regression.
    Confidence: 0.98
  • [P2] Require both rotation signals for revoked-access guidance — Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:1980-1983
    keychainAccessDenied alone selects keychainAccessRevoked, so a denial cooldown after logout or credential removal can show token-rotation recovery guidance without a stored grant or logged-in profile. Require the prior-grant and profile evidence together; otherwise preserve the missing-credentials result.
    Confidence: 0.97

Overall correctness: patch is incorrect
Overall confidence: 0.97

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 355f8443a55c.

Labels

Label justifications:

  • P2: The two remaining defects affect a bounded Claude Auto recovery flow but can produce incorrect provider behavior for affected users.
  • merge-risk: 🚨 compatibility: The patch changes established Auto fallback and stale-usage presentation behavior.
  • merge-risk: 🚨 auth-provider: It changes routing among Claude OAuth, CLI, and web usage sources after credential failures.
  • merge-risk: 🚨 security-boundary: The new availability path can permit a background owner-recovery CLI launch across the documented Keychain no-UI boundary.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🌊 off-meta tidepool and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Not applicable: This owner-authored PR is exempt from the external-contributor real-behavior-proof gate; its reported focused tests remain supplemental validation.

Evidence

Security concerns:

  • [medium] Background recovery can launch an opaque Keychain child — Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift:996
    The shortcut declares the owner-only recovery available from a cached result, but its fetch path does not use that cache and launches Claude during a scheduled refresh.
    Confidence: 0.98

Acceptance criteria:

  • [P1] swift test --filter ClaudeCLIUsageSpawnThrottleTests.
  • [P1] swift test --filter ClaudeOAuthRotationErrorTests.
  • [P1] swift test --filter ClaudeResilienceTests.
  • [P1] make check.
  • [P1] make test.

What I checked:

  • Current head is not on main or in a release: The PR head is not an ancestor of current main, and no local tag contains it, so current main does not yet include this fix. (4fe1ceb3e3f9)
  • Owner-recovery cache bypass remains: The supplied current-head patch makes cached CLI usage immediately available during background Auto, while the existing fetch path deliberately excludes owner-only recovery from that cache and launches Claude instead. (Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift:996, 4fe1ceb3e3f9)
  • Established no-UI boundary: Current documentation requires Auto availability checks to use non-interactive loads and respect the Keychain prompt cooldown; the proposed availability shortcut can admit an owner-recovery fetch that spawns the opaque Claude child. (docs/KEYCHAIN_FIX.md:51, 355f8443a55c)
  • Revoked-access classifier remains overbroad: The supplied current-head classifier treats a denied Keychain cooldown as sufficient for revoked-access guidance, even without both a recorded grant and a logged-in profile. (Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:1981, 4fe1ceb3e3f9)
  • Prior blockers were not changed at the latest head: The diff from the previous reviewed SHA to the current head changes only history/account-removal files and tests; it does not touch either Claude CLI availability or OAuth credential-classification file. (4fe1ceb3e3f9)
  • Background cache contract provenance: The current cache implementation was introduced with an explicit contract that owner-CLI recovery always spawns, which is the behavior the new availability shortcut must preserve. (Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift:1021, 9aff91a40954)

Likely related people:

  • steipete: Commit 9aff91a documents the cache semantics, including that owner-CLI recovery always spawns; the current PR also builds on that Claude fallback path. (role: introduced the background CLI cache contract and recent Claude-area contributor; confidence: high; commits: 9aff91a40954, 4a83b87aff22; files: Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift, Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift)
  • pickaxe: Recent main history attributes the OAuth-account retry scoping work to this contributor, adjacent to the owner-only recovery path affected here. (role: account-retry boundary contributor; confidence: medium; commits: bf8b897a43cd; files: Sources/CodexBar/UsageStore+Refresh.swift)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Add the recovery-context guard and a regression showing scheduled recovery cannot launch Claude through the cache shortcut.
  • Add a denial-cooldown regression with no prior grant and no active profile, asserting missing credentials rather than revoked access.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (4 earlier review cycles)
  • reviewed 2026-08-16T14:48:45.764Z sha 2b80797 :: needs maintainer review before merge. :: none
  • reviewed 2026-08-16T15:19:24.238Z sha c3a7333 :: needs changes before merge. :: [P2] Keep owner-CLI recovery behind the background availability gate | [P2] Require rotation evidence before overriding missing credentials
  • reviewed 2026-08-16T15:36:59.119Z sha a57e09f :: needs changes before merge. :: [P2] Keep owner-CLI recovery behind the availability gate | [P2] Require profile evidence for revoked-access guidance
  • reviewed 2026-08-16T16:12:09.841Z sha 46cce58 :: found issues before merge. :: [P2] Keep owner-only recovery behind the availability gate | [P2] Require both signals before reporting revoked Keychain access

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

Copy link
Copy Markdown

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: 2b807974d3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +999 to +1002
if let throttleKey = self.throttleKey(binary: binary, context: context),
ClaudeCLIUsageSpawnThrottle.cachedResult(for: throttleKey) != nil
{
return true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude owner-CLI recovery from cached availability

When a background Auto refresh enters claudeOwnerCLIRecoveryOnly while a recent cached CLI result exists, this returns true without consulting ClaudeCLIBackgroundAvailability, but fetch deliberately refuses that cached result for recovery passes and launches Claude instead. Thus an account-mismatch recovery can bypass the background opaque-child/Keychain-prompt gate and spawn the owner CLI during a scheduled refresh; only use this shortcut when !context.claudeOwnerCLIRecoveryOnly, otherwise retain the existing availability check.

Useful? React with 👍 / 👎.

Comment on lines +1980 to +1982
guard directReadConsentGranted,
!keychainAccessDisabled,
keychainAccessDenied || (previousKeychainGrantRecorded && loggedInProfilePresent)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require profile evidence before reporting token rotation

When the Keychain denial cooldown is active, this condition classifies missing credentials as keychainAccessRevoked solely because keychainAccessDenied is true, even if there is no recorded prior grant and identifiedSessionScope confirms no logged-in profile. This occurs after logout or credential removal during the six-hour cooldown and incorrectly tells the user that token rotation revoked access and that Refresh can re-grant it, rather than reporting that authentication is absent; the denial branch should not bypass the prior-grant/profile evidence.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Aug 16, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Aug 16, 2026
@steipete
steipete merged commit 68ed345 into main Aug 16, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude OAuth credentials not found. Run claude to authenticate.

1 participant