auth: issuer mix-up mitigation (#859)

This PR functions as a Reference implementation of
[SEP-2468](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2468)
/ [RFC9207](https://datatracker.ietf.org/doc/rfc9207/).

This PR hardens the MCP OAuth Client functionality against Mix-Up
attacks:
>   Mix-up attacks aim to steal an authorization code or access token by
>   tricking the client into sending the authorization code or access
> token to the attacker instead of the honest authorization or resource
>   server

This PR hardens the client by adding support for a new `iss` parameter
in authorization responses:
- Authorization Servers broadcast support for the `iss` parameter via
the `authorization_response_iss_parameter_supported` metadata parameter
- If the parameter is supported, clients expect to receive the `iss`
parameter in the authorization response
- Clients compare the `iss` parameter in the authorization response to
the `Issuer` parameter in the authorization metadata. The two must match
exactly for the response to be processed.

Fixes #941
This commit is contained in:
Max Gerber 2026-05-27 02:39:08 -07:00 • committed by GitHub
commit 2d47cc9664
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -15,6 +15,7 @@ import (
"maps"
"net/http"
"net/http/httptest"
"net/url"
"slices"
"testing"
@ -178,6 +179,8 @@ func (s *FakeAuthorizationServer) handleMetadata(w http.ResponseWriter, r *http.
CodeChallengeMethodsSupported: []string{"S256"},
ClientIDMetadataDocumentSupported: cimdSupported,
TokenEndpointAuthMethodsSupported: []string{"client_secret_post", "client_secret_basic"},
// Advertise RFC 9207 support: the authorize endpoint includes "iss" in responses.
AuthorizationResponseIssParameterSupported: true,
}
// Set CORS headers for cross-origin client discovery.
w.Header().Set("Access-Control-Allow-Origin", "*")
@ -267,8 +270,9 @@ func (s *FakeAuthorizationServer) handleAuthorize(w http.ResponseWriter, r *http
}
state := r.URL.Query().Get("state")
issuer := s.URL() + s.config.IssuerPath
redirectURL := fmt.Sprintf("%s?code=%s&state=%s", redirectURI, code, state)
redirectURL := fmt.Sprintf("%s?code=%s&state=%s&iss=%s", redirectURI, code, state, url.QueryEscape(issuer))
http.Redirect(w, r, redirectURL, http.StatusFound)
}