anthropics / anthropics/claude-code
macOS: token refresh rewrites Claude Code-credentials in place and drops its keychain ACL, restarting the password prompt loop
- Ngôn ngữ chính
- Python
- Star
- 145k
- Fork
- 23.1k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
## 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
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Đánh giá
Issue này chưa được đánh giá.