fix(claude-auth): refuse stale same-identity credential overwrites - #13568
fix(claude-auth): refuse stale same-identity credential overwrites#13568brennanb2025 wants to merge 1 commit into
Conversation
Shared Claude runtime stores (file + keychain) are multi-writer. Guard managed materialization with expiresAt monotonicity so an older managed snapshot cannot clobber a fresher login, and adopt the fresher copy back into managed storage. Force-write still applies after re-auth. Closes #13165
|
Warning Review limit reached
Next review available in: 13 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
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. Comment |
Summary
expiresAtmonotonic write guard for shared Claude runtime auth (file + active keychain).clearLastWrittenCredentialsJson), and keep managed/unmanaged + WSL isolation behavior unchanged.claude setup-tokenfor managed Claude accounts #12002, fix(claude-auth): recover stale managed accounts via cross-store token reconcile #6864.Screenshots
No visual change
Testing
pnpm lint(focused oxlint on changed files)pnpm typecheck(pnpm run typecheck:node)pnpm test(full suite not run)pnpm buildFocused validation:
pnpm exec vitest run --config config/vitest.config.ts src/main/claude-accounts/credential-freshness.test.ts src/main/claude-accounts/runtime-auth-service.test.ts— pass, 117 testspnpm run typecheck:node— passpnpm exec oxlinton touched files — passpnpm exec oxfmt --checkon touched files — passAI Review Report
Surface is main-process Claude managed-account runtime auth. Guard is structural (identity + expiresAt) with no token-family heuristics or secret logging. Extra cost is one best-effort read of file/keychain stores immediately before materialize and managed writes; no startup path changes beyond existing constructor sync. Electron UI/renderer untouched.
Security Audit
No new secret surfaces, IPC, or subprocesses. Tests use temp homes and fake keychain adapters only. No real
~/.claudeor Keychain access. Fail-open on unreadable runtime files so permission issues still allow atomic rewrite.Notes
Safe repro (synthetic only):
.credentials.json/ fake keychain holding same-email creds atexpiresAt=9000.expiresAt=1000.syncForCurrentSelection().Root cause: Unconditional materialization writers (
writeRuntimeCredentials,writeActiveClaudeKeychainCredentialsForRuntime, managed keychain/file) could land an older same-identity snapshot over a fresher multi-writer store.QA gaps: No live multi-process Claude CLI race on real Keychain; no Electron E2E of account switch UI; SSH host path not separately exercised (shares same service code). Cross-process TOCTOU with an external writer between read and write remains possible but window is minimized.
ELI5
Orca sometimes copied an old Claude login over a newer one and kicked you out. It now checks which copy expires later and keeps the fresher one when both belong to the same account.