Skip to content

OAuth: a correct but unadvertised RFC 9207 iss fails the whole sign-in (§2.4 asks for validation, not rejection) #1152

Description

@kryptosmatrix

Describe the bug

AuthorizationCodeHandler fails the entire sign-in when an authorization server returns the RFC 9207 iss parameter without advertising authorization_response_iss_parameter_supported in its metadata — even when the iss value is correct.

This happens after the user has completed the browser step, so the failure is maximally annoying: consent has been given, an authorization code has been issued, and the exchange never runs.

auth/authorization_code.go (v1.7.0), in validateIssuerResponse:

} else {
	if iss != "" {
		return fmt.Errorf("authorization server does not advertise RFC 9207 iss parameter support but iss was received in the authorization response")
	}
}

To Reproduce

Steps to reproduce the behavior:

  1. Connect to an MCP server whose authorization server returns iss in the authorization response but omits authorization_response_iss_parameter_supported from its /.well-known/oauth-authorization-server document. https://mcp.sentry.dev/mcp does this today.
  2. Complete the browser sign-in.
  3. The connection fails.

validateIssuerResponse is unexported, so the smallest reproduction is a unit test inside the auth package:

func TestUnadvertisedIssuerIsValidatedNotRejected(t *testing.T) {
	const issuer = "https://mcp.example.com"

	// Fails today: the value is correct and is refused for not having been advertised.
	if err := validateIssuerResponse(issuer, issuer, false); err != nil {
		t.Errorf("correct issuer rejected because it was not advertised: %v", err)
	}
	// Should keep failing: this is the mix-up case the parameter exists for.
	if err := validateIssuerResponse("https://attacker.example", issuer, false); err == nil {
		t.Error("a mismatched issuer must be rejected whether or not it was advertised")
	}
}

Expected behavior

When iss is present, compare it with the expected issuer and reject on mismatch — regardless of the metadata flag. When the server advertised support and no iss arrives, keep the existing error.

RFC 9207 §2.4 conditions the client obligation on the parameter being present, not on it having been advertised: clients "MUST extract the value of the iss parameter from authorization responses they receive if the parameter is present", compare it with the issuer of the server the request was sent to, and reject the response if it does not match.

The duty to advertise sits on the server, in §2.3. A server that returns iss without advertising it is not meeting that duty, but the client-side consequence described in §2.4 is to validate the value that arrived, not to refuse the exchange. §2.4 also notes that RFC 6749 §4.1.2 already requires clients which do not support RFC 9207 to ignore an unrecognised iss — so failing closed here is stricter than both specifications, and strictly less useful than validating: the current code discards a value it could have checked.

Additional context

Observed against three hosted MCP servers on 2026-08-07. None advertises the parameter:

server authorization_response_iss_parameter_supported
https://mcp.sentry.dev absent
https://mcp.linear.app absent
https://mcp.notion.com absent

Sentry additionally returns iss in the authorization response, so signing in to it through this SDK is currently impossible. The other two are affected the moment they start sending one.

Our workaround, in case it is useful to anyone hitting this before it is fixed: we resolve the issuer ourselves from the protected-resource and authorization-server metadata, compare the iss from the redirect against it in our own callback handler (so the RFC 9207 comparison still happens), and then withhold the value from the AuthorizationResult we return from AuthorizationCodeFetcher when the server did not advertise support — so the library never sees a value it will refuse. That works, but it means every caller that wants to talk to these services has to re-implement discovery and the comparison.

Happy to send a PR for the validateIssuerResponse change if the direction looks right to you.

Found while adding MCP client support to Ollama; the SDK is otherwise doing all the heavy lifting of the authorization flow and this is the only interoperability problem we hit against real services.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions