anthropics / anthropics/claude-code
Remote MCP OAuth: shared user-scope refresh token breaks against IdPs with single-use refresh-token rotation (ODC)
- Lingua principale
- Python
- Stelle
- 145k
- Fork
- 23.1k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
## Environment
- Claude Code 2.1.258, macOS (Darwin 25.6)
- Remote HTTP MCP server with OAuth via dynamic client registration (issuer `as-proxy`, scope `openid`), credential stored at user scope in the macOS keychain (`Claude Code-credentials` → `mcpOAuth.`)
- IdP behind the server: OutSystems Developer Cloud (ODC) Identity Service
## Summary
The IdP issues 5‑minute access tokens and **single-use rotating refresh tokens**; reusing a refresh token revokes the whole session. Claude Code stores **one refresh token per server at user scope**, and every concurrent session refreshes from it independently. With two or more Claude Code sessions open (CLI worktrees, Desktop, Cowork), the second refresh is treated as reuse, the IdP revokes the token family, and every session then reports:
```
MCP server "" requires re-authorization (token expired)
```
Observed ≥6 forced re-authorizations in one working day. Never observed with a single active session, and never while one session was continuously calling tools (the refresh itself works: keychain `expiresAt` advanced from 22:37:27Z to 22:38:39Z between two reads one minute apart). Each re-auth mints a new DCR client.
IdP behaviour is documented by the vendor:
- https://success.outsystems.com/documentation/outsystems_developer_cloud/managing_outsystems_platform_and_apps/cloud_native_architecture_of_outsystems_developer_cloud/architecture_of_authentication_and_authorization_mechanism/#token-lifecycle-and-logout — "access tokens are short-lived JWTs with a five-minute lifetime … Refresh tokens are single-use … If the same refresh token is used a second time, the entire session is invalidated"
- https://success.outsystems.com/documentation/outsystems_developer_cloud/user_management/configure_user_session/#refresh-token-rotation — "If a previously used token is reused, ODC revokes all related tokens and terminates the session"
The Claude Code MCP docs describe the "refresh token rejection" notice but say nothing about sharing one OAuth credential across concurrent sessions, so this looks like an unhandled interaction rather than intended behaviour. (v2.1.206's transient-refresh fix is not the cause; 2.1.258 is affected.)
Side effect: a server-side stateful operation bound to the token (an in-flight Mentor run) is lost when the family is revoked.
## Steps to reproduce
1. Add an OAuth remote MCP server whose IdP rotates refresh tokens with reuse detection (ODC; or Keycloak with "Revoke Refresh Token" enabled and max reuse 0). Sign in once.
2. Open two Claude Code sessions in the same user account.
3. In session A call any tool. Wait > 5 min (access token expiry). In session B call any tool (B refreshes from the shared token and rotates it). Wait > 5 min. In session A call a tool again.
4. Session A refreshes from the now-consumed token → IdP treats it as reuse → whole family revoked → both sessions show "requires re-authorization".
## Expected
Any of:
- (a) Refreshes serialised through one owner/lock, with the rotated refresh token written back before any other session reads it, and sessions re-reading the store before refreshing.
- (b) Credentials keyed per session/client instead of one user-scope entry.
- (c) At minimum, documented guidance for IdPs with single-use rotation (e.g. "use one session per server").
## Actual
Concurrent sessions silently invalidate each other's refresh token; the user must re-authenticate roughly every 30–90 minutes.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Direzione di ricerca
Start by tracing Claude Code's MCP OAuth credential storage and refresh handling, using the user-scope macOS keychain entry described in the report. Reproduce the two-session sequence with a single-use rotating refresh-token provider. Done means concurrent sessions no longer invalidate each other's credentials, or the supported limitation and one-session guidance are documented.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Ambito
- authentication
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Attiva
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 48/100