Repository navigation
fix(mcp): handle refresh_token grant in the consent proxy /token endpoint - #2701
Merged
Merged
Conversation
…oint Live PROD failure (2026-10-08) after a SUCCESSFUL initial auth: on reconnect mcp-remote POSTs grant_type=refresh_token to our /token, but handleToken only accepted authorization_code → 400 "unsupported_grant_type" on every refresh. The discovery doc advertises refresh_token in grant_types_supported, and we pass IAS's refresh_token through to the client verbatim at the code exchange — so the client correctly tries to refresh against us (its advertised AS). We just never implemented the branch. Fix: add an idpRefresh() boundary (mirrors idpExchange) that relays the client's refresh_token to IAS /oauth2/token with grant_type=refresh_token + the client's own client_id (public client, no secret, no PKCE on refresh). handleToken now branches: refresh_token → idpRefresh; authorization_code → unchanged; anything else → unsupported_grant_type. Passes rotated refresh_token through, falls back to the old one when IAS doesn't rotate. Tests: +6 (refresh proxy happy path, rotation + no-rotation fallback, missing refresh_token 400, IAS-failure 400, real idpRefresh body with fetch mocked). 31 unit tests green.
jung-thomas
marked this pull request as ready for review
October 8, 2026 22:24
jung-thomas
added a commit
that referenced
this pull request
Oct 9, 2026
Resolves the 1.37.0 vs 1.37.1 overlap on the two release-managed files: - .deploy/mta.yaml: keep 1.37.1 (supersedes main's 1.37.0 bump). - hugo/data/whats_new.json: keep the 757-entry digest, which already incorporates main's 752-entry 1.37.0 digest (rebased onto it) plus the 4 new 1.37.1 PRs (#2700/#2701/#2704/#2705). Verified superset: all 1.37.0 entries retained, 4 new entries added, no duplicates.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the
refresh_tokengrant to the consent proxy's/tokenendpoint. Without it, mcp-remote fails on reconnect withunsupported_grant_typeeven though the initial auth succeeded.Live symptom (PROD, 2026-10-08)
Browser consent + IAS login succeeded, initial token issued, transport connected. Then on reconnect:
OAuthError2: unsupported_grant_typefromrefreshAuthorization→executeTokenRequest.Root cause
handleTokenonly acceptedgrant_type=authorization_code. But the discovery doc advertisesrefresh_tokeningrant_types_supported, and we pass IAS'srefresh_tokenthrough to the client verbatim — so the client refreshes against us (its advertised AS). The branch was never implemented → hard 400 on every refresh.Fix
idpRefresh()boundary (mirrorsidpExchange): relays the client'srefresh_tokento IAS/oauth2/tokenwith the client's ownclient_id(public client, no secret, no PKCE on refresh).handleTokenbranches:refresh_token→idpRefresh;authorization_code→ unchanged; else →unsupported_grant_type.refresh_tokenthrough; falls back to the old one when IAS doesn't rotate.Tests
+6: refresh happy path, rotation + no-rotation fallback, missing refresh_token (400), IAS-failure (400), and the real
idpRefreshbody withfetchmocked (the kind of test that caught the last regression). 31 green.Chain
5th consent fix: #2691 + #2693 + #2696 + #2697 + #2698 + this. After deploy, the full mcp-remote lifecycle (auth → use → token refresh → reconnect) completes.