Validate the RFC 9207 iss Authorization Response Parameter per SEP-2468#431
Open
koic wants to merge 1 commit into
Open
Validate the RFC 9207 iss Authorization Response Parameter per SEP-2468#431koic wants to merge 1 commit into
iss Authorization Response Parameter per SEP-2468#431koic wants to merge 1 commit into
Conversation
…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.
9 tasks
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.
Motivation and Context
SEP-2468 (modelcontextprotocol/modelcontextprotocol#2468, merged for the 2026-07-28 spec release) recommends that authorization servers include the RFC 9207
issparameter in authorization responses and requires MCP clients to validate a presentissagainst 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'scallback_handlermay now return[code, state, iss]in addition to the existing[code, state]. The 3-element form opts into validation; returningiss = nilasserts the caller inspected the authorization response and found noiss.Flow#run!validates the issuer after thestatecheck and beforeexchange_authorization_code, implementing the RFC 9207 Section 2.4 rules: a presentissmust equal the AS metadataissuerexactly (the valueensure_issuer_matches!already pinned to the discovery URL); a nilissfrom a 3-element callback is rejected when the AS metadata advertisesauthorization_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 withAuthorizationErrorbefore the code reaches a token endpoint.issthrough from the redirect URL.Resolves #386.
How Has This Been Tested?
New tests in
test/mcp/client/oauth/flow_test.rb:isscompletes the flow and persists tokensissraisesAuthorizationErrorand never contacts the token endpoint (assert_not_requestedon the token POST)isswithauthorization_response_iss_parameter_supported: trueraises without contacting the token endpoint (fail closed)isssupport (backward compatibility)isswithout the advertisement completesissis 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_handlercontract continues to work unchanged; issuer validation activates only when a callback opts in by returning the 3-element form.Types of changes
Checklist