SEP-2643: Structured Authorization Denials#2643
Conversation
Co-authored-by: Nate Barbettini <[email protected]>
…e's comment on line-209)
…orization-denials
Removes Use Case 3 (additional short-lived credentials) and its related artifacts (Motivation modelcontextprotocol#4, credentialDisposition envelope field, UC3-specific Security Implications subsection, abstract "three scenarios" framing). Adds a Rationale subsection "Future Extensions: Multi-Token Management" pointing to the planned authorization_hint and usage_semantics extension of draft-zehavi-oauth-rar-metadata and complementary WG work on client-side multi-credential handling. Addresses PR review threads L60, L62, L64, L300.
…orization-denials
… identifier section)
|
|
||
| This use case covers denials where the client's credential is valid and does not need to change. Remediation requires an out-of-band user interaction at a URL, such as approving access or selecting resources, that changes server-side state. A representative example is a file picker in a cloud storage service, where the user's OAuth token is unchanged and the server records an approval tied to the user. | ||
|
|
||
| This use case composes with the MCP URL elicitation error (`URLElicitationRequiredError`, JSON-RPC error code `-32042`). The envelope MUST include a `remediationHints` entry of type `url`, and the URL elicitation remains in `data.elicitations` for backwards compatibility. |
There was a problem hiding this comment.
and the URL elicitation remains in
data.elicitationsfor backwards compatibility
Does this imply that it's in a different place going forward? Or is data.elicitations the canonical place a client should read this data from?
|
|
||
| ### Use Case 2 — Credential replacement with broader authorization | ||
|
|
||
| This use case covers denials where the client's existing credential carries insufficient authorization for the requested operation and remediation requires obtaining a new credential. The replacement may involve additional OAuth scopes, a Rich Authorization Requests `authorization_details` object (RFC 9396), or any other mechanism that yields a new credential. In contrast with Use Case 1, the failure cannot be resolved by server-side state changes alone. |
There was a problem hiding this comment.
| This use case covers denials where the client's existing credential carries insufficient authorization for the requested operation and remediation requires obtaining a new credential. The replacement may involve additional OAuth scopes, a Rich Authorization Requests `authorization_details` object (RFC 9396), or any other mechanism that yields a new credential. In contrast with Use Case 1, the failure cannot be resolved by server-side state changes alone. | |
| This use case covers denials where the client's existing credential carries insufficient authorization for the requested operation and remediation requires obtaining a new credential, which may include additional OAuth scopes, a Rich Authorization Request's `authorization_details` object (RFC 9396), or any other mechanism to add required authorization to the new credential. In contrast with Use Case 1, the failure cannot be resolved by server-side state changes alone. |
| } | ||
| ``` | ||
|
|
||
| The client uses the `authorization_details` from the remediation hint to construct an OAuth authorization request, per [RFC 9396#section-2](https://datatracker.ietf.org/doc/html/rfc9396#section-2): |
There was a problem hiding this comment.
| The client uses the `authorization_details` from the remediation hint to construct an OAuth authorization request, per [RFC 9396#section-2](https://datatracker.ietf.org/doc/html/rfc9396#section-2): | |
| The client attempts to locate an existing valid credential matching a provided `authorization_reference`, if provided. If no matching credential is found, the client uses the `authorization_details` from WWW-Authenticate header's `authorization_remediation` parameter, to construct an OAuth authorization request, per [RFC 9396#section-2](https://datatracker.ietf.org/doc/html/rfc9396#section-2): |
|
|
||
| The SEP composes with existing MCP primitives rather than inventing parallel shapes. Use Case 1 reuses MCP URL elicitation (`URLElicitationRequiredError`) and adds the envelope as sibling metadata inside the same error response. Clients that already understand URL elicitation continue to work unchanged, and clients that additionally understand the envelope receive transport-agnostic classification and a correlation handle. | ||
|
|
||
| ### Future Extensions: Multi-Token Management |
There was a problem hiding this comment.
I'd remove this section completely.
UC2 with RAR relies on the rar-metadata draft, which now (version 03) includes the authorization_hint attribute, and was accepted at the OSW in Leipzig on 05/2026.
Therefore I've added to the SEP authorization_hint and its usage which is a one-liner actually.
Therefore I believe this comment can be removed.
|
|
||
| 2. **Support for signaling server-side state remediation**: Some authorization denials are not about the client's credential. The MCP server may deny a request because it has insufficient information to contact an external system, needs confirmation from the user, or another piece of server-side state must be established before the request can succeed. OAuth authorization challenges such as `insufficient_scope` or `invalid_token` are shaped around credential changes and do not cover this case. MCP has URL-mode elicitation as a primitive for out-of-band user interaction, but it is not itself an authorization denial signaling mechanism. | ||
|
|
||
| 3. **Support for structured remediation data at denial**: OAuth 2.0 defines Rich Authorization Requests (RFC 9396) for conveying structured authorization requirements, but only as part of the authorization request flow. Adopted OAuth standards do not yet provide a way to carry such structured requirements back to the client at denial time. An IETF individual draft, [`draft-zehavi-oauth-rar-metadata`](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/), proposes a mechanism for HTTP by defining a new `WWW-Authenticate` error code, `insufficient_authorization_details`, with the structured data placed in a JSON response body. The draft is HTTP-specific, and this SEP proposes to adopt the same pattern at the JSON-RPC layer so it applies across MCP transports. |
There was a problem hiding this comment.
| 3. **Support for structured remediation data at denial**: OAuth 2.0 defines Rich Authorization Requests (RFC 9396) for conveying structured authorization requirements, but only as part of the authorization request flow. Adopted OAuth standards do not yet provide a way to carry such structured requirements back to the client at denial time. An IETF individual draft, [`draft-zehavi-oauth-rar-metadata`](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/), proposes a mechanism for HTTP by defining a new `WWW-Authenticate` error code, `insufficient_authorization_details`, with the structured data placed in a JSON response body. The draft is HTTP-specific, and this SEP proposes to adopt the same pattern at the JSON-RPC layer so it applies across MCP transports. | |
| 3. **Support for structured remediation data at denial**: OAuth 2.0 defines Rich Authorization Requests (RFC 9396) for conveying structured authorization requirements, but only as part of the authorization request flow. Adopted OAuth standards do not yet provide a way to carry such structured requirements back to the client at denial time. An IETF individual draft, [`draft-zehavi-oauth-rar-metadata`](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/05/), proposes a mechanism for HTTP by defining a new `WWW-Authenticate` error code, `insufficient_authorization`, with the structured data placed in the authorization_remediation header error parameter, which contains a base64url-encoded JSON object. The draft is HTTP-specific, and this SEP proposes to adopt the same pattern at the JSON-RPC layer so it applies across MCP transports. |
|
|
||
| When a denied request requires authorization beyond what OAuth scopes can express, the `WWW-Authenticate` challenge can signal that authorization is insufficient but cannot carry the structured requirements themselves. The server MAY supply those requirements inside the envelope so the client can construct its next authorization request directly, without additional metadata discovery. This approach mirrors the error-signaling pattern defined in [OAuth 2.0 RAR Metadata and Error Signaling](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/) at the JSON-RPC layer. | ||
|
|
||
| An MCP server that denies a request because the access token lacks sufficient authorization details MAY return a `remediationHints` entry of type `oauth_authorization_details`. The entry's `authorization_details` member, when present, SHALL be an OAuth Rich Authorization Requests `authorization_details` array as defined in RFC 9396. On HTTP Transports, The server MUST still return an HTTP authorization challenge via `WWW-Authenticate`, which remains authoritative for remediation and discovery. The JSON-RPC remediation hint is supplemental and exists to carry an actionable `authorization_details` object inside the response body. |
There was a problem hiding this comment.
| An MCP server that denies a request because the access token lacks sufficient authorization details MAY return a `remediationHints` entry of type `oauth_authorization_details`. The entry's `authorization_details` member, when present, SHALL be an OAuth Rich Authorization Requests `authorization_details` array as defined in RFC 9396. On HTTP Transports, The server MUST still return an HTTP authorization challenge via `WWW-Authenticate`, which remains authoritative for remediation and discovery. The JSON-RPC remediation hint is supplemental and exists to carry an actionable `authorization_details` object inside the response body. | |
| An MCP server that denies a request because the access token lacks sufficient authorization details returns a transport layer WWW-Authenticate header with appropriate remediation requirements. The client performs OAuth reauthorization using the transport challenge, obtains a new access token which includes the provided `authorization_details`, and retries the original request with the new token in the `Authorization` header. The client echoes the `authorizationContextId` per "Retry Echo via `_meta`". |
|
|
||
| An MCP server that denies a request because the access token lacks sufficient authorization details MAY return a `remediationHints` entry of type `oauth_authorization_details`. The entry's `authorization_details` member, when present, SHALL be an OAuth Rich Authorization Requests `authorization_details` array as defined in RFC 9396. On HTTP Transports, The server MUST still return an HTTP authorization challenge via `WWW-Authenticate`, which remains authoritative for remediation and discovery. The JSON-RPC remediation hint is supplemental and exists to carry an actionable `authorization_details` object inside the response body. | ||
|
|
||
| A client that recognizes the `oauth_authorization_details` hint MAY use the provided `authorization_details` directly in a subsequent OAuth authorization request. A client that does not recognize the hint SHOULD fall back to resource-metadata and authorization-server-metadata discovery to determine how to construct a valid authorization request. |
There was a problem hiding this comment.
| A client that recognizes the `oauth_authorization_details` hint MAY use the provided `authorization_details` directly in a subsequent OAuth authorization request. A client that does not recognize the hint SHOULD fall back to resource-metadata and authorization-server-metadata discovery to determine how to construct a valid authorization request. | |
| A client that recognizes the `WWW-Authenticate` header with `insufficient_authorization` error code and `authorization_remediation` parameter MAY use an existing valid credential matching a provided `authorization_reference` value, if provided. If no matching credential is found, client MAY use the provided `authorization_details` directly in a subsequent OAuth authorization request. A client that does not recognize the hint SHOULD fall back to resource-metadata and authorization-server-metadata discovery to determine how to construct a valid authorization request. |
|
|
||
| ```json | ||
| HTTP/1.1 403 Forbidden | ||
| WWW-Authenticate: Bearer error="insufficient_authorization_details", |
There was a problem hiding this comment.
| WWW-Authenticate: Bearer error="insufficient_authorization_details", | |
| WWW-Authenticate: Bearer error="insufficient_authorization", | |
| resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/payments", | |
| error_description="Additional authorization is required", | |
| authorization_remediation=eyJhdXRob3JpemF0aW9uX2RldGFpbHMiOlt7InR5cGUiOiJkaX... |
| ```json | ||
| HTTP/1.1 403 Forbidden | ||
| WWW-Authenticate: Bearer error="insufficient_authorization_details", | ||
| resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/payments", |
| "authorization": { | ||
| "reason": "insufficient_authorization", | ||
| "authorizationContextId": "authzctx_pay_9f2c", | ||
| "remediationHints": [ |
There was a problem hiding this comment.
Propose remove remediationHints, it's provided in the header
| Consider an MCP server that exposes a `payments.initiate` tool where initiating a payment requires authorization details bound to the payee and amount. The client's current access token does not carry such authorization details, and the call is denied: | ||
|
|
||
| ```json | ||
| HTTP/1.1 403 Forbidden |
There was a problem hiding this comment.
| HTTP/1.1 403 Forbidden | |
| HTTP/1.1 401 Unauthorized |
Motivation and Context
This SEP defines a transport-agnostic JSON-RPC authorization denial envelope for the Model Context Protocol. The envelope complements transport-level authorization challenges (HTTP WWW-Authenticate with Protected Resource Metadata), carrying failure classification, a retry correlation handle, and an extensible set of structured remediation hints for cases the transport cannot easily express. This SEP defines two initial remediation hint types and illustrates their use through three scenarios: URL-based approval composed with MCP URL-mode elicitation, credential replacement using scopes and OAuth Rich Authorization Requests (RFC 9396), and additional short-lived credentials for per-transaction operations like payment initiation in banking. Further remediation hint types can be defined in follow-on SEPs without changes to the envelope.
How Has This Been Tested?
This has been reviewed in the MCP Fine-Grained Authorization Working Group over multiple review cycles.
Discord: MCP FGA WG channel
Please see the Acknowledgments section of the SEP for reviewers.
Breaking Changes
This SEP is fully backward compatible. Existing OAuth clients and libraries do not need to change as the envelope is additive.
Types of changes
Checklist
Additional context
Status: Draft — awaiting sponsor. Will coordinate with FGA WG Lead to update this
AI Assistance Disclosure:: This SEP was written by the author with AI assistance for editorial review (grammar, structural arrangement etc.)