anthropics / anthropics/claude-code

Remote MCP OAuth: shared user-scope refresh token breaks against IdPs with single-use refresh-token rotation (ODC)

オープン
#91,641 コメント 2 件 リアクション 1 件 担当者 0 名 GitHub で見る
area:auth area:mcp bug has repro platform:macos
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

## 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.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

評価

領域
authentication
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
48/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。