-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Describe the bug
When two requests for the same OAuth MCP server arrive in parallel, both calls independently detect a 401, call auth(), and race to refresh using the same refresh token. With OAuth servers that use rotating refresh tokens (e.g. Atlassian, Asana), this triggers RFC 6819 5.2.2.3 replay detection — the authorization server detects that a consumed refresh token was reused and revokes the entire token family, permanently breaking the connection until the user manually re-authorizes.
To Reproduce
- Set up an MCP server connection using an OAuth provider with rotating refresh tokens (e.g. Atlassian)
- Let the access token expire, or manually add a bad token
- Send two parallel requests to the MCP server before either completes the refresh, this will have to be quick so use a simple script to make 2 immediate calls
curl ... &
curl ... &
wait
- Both requests call auth() simultaneously, each POSTing to the token endpoint with the same refresh token
- The OAuth server receives two requests with the same refresh token — the second is a replay
Expected behavior
Only one token refresh should occur. The second concurrent request should wait for the in-flight refresh to complete and reuse the resulting tokens, not trigger its own parallel refresh.
Logs
InvalidGrantError: Invalid refresh token
at parseErrorResponse (client/auth.ts:292:16)
at refreshAuthorization (client/auth.ts:1022:15)
at authInternal (client/auth.ts:419:31)
at auth (client/auth.ts:325:20)
at StreamableHTTPClientTransport.send (client/streamableHttp.ts:442:36)
Additional context
Root cause is in auth() in client/auth.ts — there is no concurrency guard preventing two simultaneous refresh flows for the same provider.