anthropics / anthropics/claude-code
[BUG] Credential storage: refresh token written in plaintext, and Keychain items accumulate without bound
- Lenguaje dominante
- Python
- Estrellas
- 145k
- Forks
- 23.1k
- Métricas de merge de PR
- Métricas de PR pendientes
Descripción
## What's Wrong?
Two related credential-storage problems on macOS, plus a scope/auth-mode issue.
1. PLAINTEXT REFRESH TOKEN. `~/.claude/.credentials.json` (mode 0600) contains a live
access token and a refresh token valid for 27 days. A Keychain item requires
per-application ACL consent; a 0600 file requires only executing as the user. The
Keychain is in use simultaneously (see 2), so this is not a fallback for a missing
platform store.
2. UNBOUNDED KEYCHAIN ACCUMULATION. The login keychain holds 1,306 items with service
prefix `Claude Code-credentials` out of 1,440 total services — 90.6% of the keychain.
Suffixes are uniformly 8 lowercase hex chars with no other length present, indicating
one generating code path. Observed growing live during a single session:
1,295 -> 1,298 -> 1,300 -> 1,306. Not correlated with sessions
(68 session-env dirs, 2 sessions dirs).
3. SCOPE DOWNGRADE ON RE-AUTH. The authorize URL requests `scope=user%3Ainference`
(one scope) while the stored credential carries five, including
`user:sessions:claude_code`. Completing that flow yields fewer capabilities than the
credential it replaces, and drops the scope `claude --teleport` requires — so the
offered login cannot fix the failure that prompted it. No warning is shown.
4. AUTH MODE MISREPORTED. `claude --teleport ` returns "API key authentication is
not sufficient" while a valid Max OAuth credential with the required scope is present
and no API key is configured anywhere.
## What Should Happen?
1. Refresh tokens stored in the Keychain, not plaintext, when the Keychain is available.
2. Superseded credential items removed; item count bounded.
3. Re-auth requests at least the scopes the existing credential holds, or warns explicitly.
4. Auth mode reported accurately based on actual credential state.
## Error Messages / Logs
$ claude --teleport session_01EWeDKeB3S5mWCn92jWJar4
Claude Code web sessions require authentication with a Claude.ai account. API
key authentication is not sufficient. Please run /login to authenticate, or
check your authentication status with /status.
Machine state at that same moment:
accessToken valid (expires 2026-09-01T11:31:05Z)
refreshToken valid (expires 2026-09-28T20:09:46Z)
subscription max
scopes user:file_upload user:inference user:mcp_servers user:profile
user:sessions:claude_code
ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / CLAUDE_CODE_OAUTH_TOKEN : all unset
(verified: env -i HOME=$HOME /bin/zsh -lic, plus .zshrc .zshenv .zprofile
.profile .bashrc, plus settings.json and settings.local.json)
## Steps to Reproduce
1. Authenticate normally with a Claude.ai (Max) account on macOS.
2. Observe the plaintext store exists alongside the Keychain:
ls -l ~/.claude/.credentials.json
3. Count credential items:
security dump-keychain | grep -c '"svce"="Claude Code-credentials'
4. Use Claude Code normally for a period; re-run step 3. The count increases and
superseded items are never removed.
5. Run `claude --teleport `. Observe the API-key message despite a
valid OAuth credential.
6. Start `/login` and inspect the authorize URL: `scope=user%3Ainference` only.
## Additional Information
MEASURED, NEGATIVE — Keychain performance is NOT degraded at this item count, so this
is a hygiene and blast-radius issue, not a performance one. `SecItemCopyMatching`
(attributes only, 25 iterations): exact-service lookup median 0.258 ms against a
0.135 ms nonexistent-service control floor (control returns errSecItemNotFound/-25300,
confirming the query was valid and searching); full enumeration of 1,430 items 14.3 ms.
RELATED: #81281 describes a stale `~/.claude/.credentials.json` overriding a valid
`CLAUDE_CODE_OAUTH_TOKEN`. This report is distinct — no env var is set here (verified
above) and the error string differs — but both indicate the client misreading its own
auth state, and #81281 independently confirms the plaintext file is authoritative.
---
### Environment
| | |
|---|---|
| Claude Code | 2.1.212 (Claude Code) |
| Platform | Claude API (Max subscription) |
| OS | macOS 27.0 (build 26A5425a, developer beta) |
| Hardware | Apple M2 Pro, arm64 |
| Terminal / Shell | Ghostty 1.3.1 / zsh (also reproduced in Terminal.app) |
| Model | Opus |
| Regression | No — not tested against a prior version |
**Preflight:** searched existing issues; single bug report; on latest version.
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Línea de trabajo
Start by tracing the macOS credential handling used by `/login`, `--teleport`, and the `~/.claude/.credentials.json` store, then compare it with the Keychain items described in the report. Reproduce the item count, scope mismatch, and authentication message using the listed commands and environment checks. Done means refresh tokens are protected, obsolete items are bounded, re-auth preserves or warns about scopes, and `--teleport` reports the actual credential mode.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- macos, python
- Área
- authentication, authorization, cli, security
- Tipo de issue
- Error
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Estado de actividad
- Activo
- Claridad
- Bastante claro
- Aptitud para principiantes
- 32/100