anthropics / anthropics/claude-code

macOS: token refresh rewrites Claude Code-credentials in place and drops its keychain ACL, restarting the password prompt loop

Open
#94,491 1 comment 0 reactions 0 assignees View on GitHub
area:auth bug has repro platform:macos
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

## Summary

On macOS, every OAuth token refresh rewrites the `Claude Code-credentials` keychain item **in place** and drops the item's ACL in the process. Any grant the user has given — via "Always Allow", or via a manual `security set-generic-password-partition-list` repair — is wiped, and the "security wants to access key 'Claude Code-credentials'" password prompt loop starts over roughly every few hours.

This is the same user-visible symptom as #89985, which covers why the prompt appears at all (credentials are read by forking `/usr/bin/security`, so `securityd` evaluates the ACL against a generic Apple tool). This issue is narrower: it isolates the **write** path as an independently fixable cause, with evidence that the item is updated rather than replaced.

## Environment

- macOS 26.6.2 (25G83), Apple Silicon
- Claude Code 2.1.270 (bundled in Claude.app) and 2.1.271 (standalone CLI)
- Keychain item: service `Claude Code-credentials`, login keychain (`no-timeout`, unlocked)

## Evidence: the item is modified in place, not recreated

Across a token refresh, `cdat` is unchanged while `mdat` moves:

```
cdat = 20260908181554Z (unchanged across the refresh)
mdat = 20260915031337Z -> 20260915110920Z
```

A delete + re-add would produce a new `cdat`. So the write path reaches the existing item and drops its ACL along the way, rather than replacing the item wholesale. That suggests the fix may be small: preserve the existing `SecAccess` on update instead of writing a default one.

## Reproduction

1. Repair the grant on the item:

```
security set-generic-password-partition-list -S 'apple:,apple-tool:,teamid:XDX9UJP7V4' \
-s "Claude Code-credentials" -a ~/Library/Keychains/login.keychain-db
```

2. Confirm it worked — a scripted read returns the secret with no dialog:

```
security find-generic-password -s "Claude Code-credentials" -a -w
```

3. Wait for a token refresh (`mdat` on the item advances).
4. Run the same read again. It now blocks on a password dialog.

In step 4 the test process died by `SIGALRM` (exit 142) with nothing on stderr — the dialog was open and waiting, not an error return.

## Control

This account had a second credential item from another install (`Claude Code-credentials-`). Both items were repaired at the same moment. The second one has not been written since, and still reads silently; only the item that got rewritten regressed.

Same machine, same repair, same keychain state — the only difference is that one was rewritten. That isolates the regression to the write path.

## Contributing factor: multiple credential items

Installs using different config dirs create separate `Claude Code-credentials-` items, each with its own ACL, so a grant given for one does nothing for the others. This account had three, one of them stale from an install that no longer existed. Combined with concurrent Claude Code processes each forking their own `security` (see #89985), a single expired grant produces ~10 simultaneous dialogs.

## Expected

A token refresh should preserve the item's existing ACL, so that a grant the user has given survives until they revoke it.

## Notes

There appears to be no supported way to opt out of the keychain on macOS — the binary only exposes `--use-mock-keychain` (test) and an internal `allowUnrestrictedAccess` for CI, neither configurable by users. Happy to capture `securityd` logs around a refresh if useful.

— samirhvbr

Contributor guide

No contributing guide indexed for this repository

Research direction

The issue names no source files or tests; start by locating the macOS keychain credential write or token-refresh entry point and inspect how an existing item is updated. Reproduce the refresh with an existing ACL, then verify that the ACL remains intact and the repaired keychain read does not prompt again.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos
Domain
authentication, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.