Skip to content

Validate the RFC 9207 iss Authorization Response Parameter per SEP-2468#431

Open
koic wants to merge 1 commit into
modelcontextprotocol:mainfrom
koic:iss_parameter_validation
Open

Validate the RFC 9207 iss Authorization Response Parameter per SEP-2468#431
koic wants to merge 1 commit into
modelcontextprotocol:mainfrom
koic:iss_parameter_validation

Conversation

@koic

@koic koic commented Jun 28, 2026

Copy link
Copy Markdown
Member

Motivation and Context

SEP-2468 (modelcontextprotocol/modelcontextprotocol#2468, merged for the 2026-07-28 spec release) recommends that authorization servers include the RFC 9207 iss parameter in authorization responses and requires MCP clients to validate a present iss against the expected issuer before redeeming the authorization code. This mitigates mix-up attacks in multi-IdP setups, where an attacker-controlled authorization server could otherwise capture a code minted by an honest server.

This mirrors the TypeScript SDK's approach (typescript-sdk#2272, with typescript-sdk#1957 as the reference implementation), adapted to the Ruby provider contract:

  • Provider's callback_handler may now return [code, state, iss] in addition to the existing [code, state]. The 3-element form opts into validation; returning iss = nil asserts the caller inspected the authorization response and found no iss.
  • Flow#run! validates the issuer after the state check and before exchange_authorization_code, implementing the RFC 9207 Section 2.4 rules: a present iss must equal the AS metadata issuer exactly (the value ensure_issuer_matches! already pinned to the discovery URL); a nil iss from a 3-element callback is rejected when the AS metadata advertises authorization_response_iss_parameter_supported: true; a legacy 2-element callback skips the check, so every existing integration keeps working. On any violation the flow aborts with AuthorizationError before the code reaches a token endpoint.
  • The conformance client now returns the 3-element form, plumbing iss through from the redirect URL.

Resolves #386.

How Has This Been Tested?

New tests in test/mcp/client/oauth/flow_test.rb:

  • a matching iss completes the flow and persists tokens
  • a mismatched iss raises AuthorizationError and never contacts the token endpoint (assert_not_requested on the token POST)
  • a nil iss with authorization_response_iss_parameter_supported: true raises without contacting the token endpoint (fail closed)
  • a legacy 2-element callback completes even when the AS advertises iss support (backward compatibility)
  • a nil iss without the advertisement completes
  • a mismatched iss is rejected even without the advertisement (validate-when-present is unconditional)

bundle exec rake (tests, RuboCop, and conformance baseline with the 3-element conformance callback) passes.

Breaking Changes

None. The 2-element callback_handler contract continues to work unchanged; issuer validation activates only when a callback opts in by returning the 3-element form.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

…2468

## Motivation and Context

SEP-2468 (modelcontextprotocol/modelcontextprotocol#2468, merged for the 2026-07-28 spec release) recommends
that authorization servers include the RFC 9207 `iss` parameter in authorization responses and requires MCP clients
to validate a present `iss` against the expected issuer before redeeming the authorization code.
This mitigates mix-up attacks in multi-IdP setups, where an attacker-controlled authorization server could otherwise capture
a code minted by an honest server.

This mirrors the TypeScript SDK's approach (typescript-sdk#2272, with typescript-sdk#1957 as the reference implementation),
adapted to the Ruby provider contract:

- `Provider`'s `callback_handler` may now return `[code, state, iss]` in addition to the existing `[code, state]`.
  The 3-element form opts into validation; returning `iss = nil` asserts the caller inspected the authorization response
  and found no `iss`.
- `Flow#run!` validates the issuer after the `state` check and before `exchange_authorization_code`,
  implementing the RFC 9207 Section 2.4 rules: a present `iss` must equal the AS metadata `issuer` exactly
  (the value `ensure_issuer_matches!` already pinned to the discovery URL); a nil `iss` from a 3-element callback is rejected
  when the AS metadata advertises `authorization_response_iss_parameter_supported: true`; a legacy 2-element callback skips the check,
  so every existing integration keeps working. On any violation the flow aborts with `AuthorizationError` before
  the code reaches a token endpoint.
- The conformance client now returns the 3-element form, plumbing `iss` through from the redirect URL.

Resolves modelcontextprotocol#386.

## How Has This Been Tested?

New tests in `test/mcp/client/oauth/flow_test.rb`:

- a matching `iss` completes the flow and persists tokens
- a mismatched `iss` raises `AuthorizationError` and never contacts the token endpoint (`assert_not_requested` on the token POST)
- a nil `iss` with `authorization_response_iss_parameter_supported: true` raises without contacting the token endpoint (fail closed)
- a legacy 2-element callback completes even when the AS advertises `iss` support (backward compatibility)
- a nil `iss` without the advertisement completes
- a mismatched `iss` is rejected even without the advertisement (validate-when-present is unconditional)

`bundle exec rake` (tests, RuboCop, and conformance baseline with the 3-element conformance callback) passes.

## Breaking Changes

None. The 2-element `callback_handler` contract continues to work unchanged; issuer validation activates only
when a callback opts in by returning the 3-element form.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SEP-2468: Recommend Issuer (iss) Parameter in MCP Auth Responses

1 participant