From 21eaa6e418b244de66f1bd13c05f6d2b167f1c22 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:15:44 +0000 Subject: [PATCH 01/19] Create dpop-extension.mdx --- specification/draft/dpop-extension.mdx | 257 +++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 specification/draft/dpop-extension.mdx diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx new file mode 100644 index 0000000..065a5b1 --- /dev/null +++ b/specification/draft/dpop-extension.mdx @@ -0,0 +1,257 @@ +# DPoP Profile for MCP + +## Introduction + +### Purpose and Scope + +This extension defines DPoP (Demonstrating Proof-of-Possession) support for the Model Context Protocol, enabling sender-constrained access tokens that prevent token replay attacks. This extension builds upon the baseline authorization requirements defined in the main Authorization specification. + +DPoP binds access tokens to a cryptographic key pair controlled by the client. When accessing MCP server resources, clients must prove possession of the private key by providing a signed proof with each request. This prevents unauthorized use of tokens even if they are intercepted or leaked. + +### Extension Requirements + +This extension is OPTIONAL for MCP implementations. When adopted: + +- Implementations MUST conform to all requirements specified in this extension +- Implementations MUST also conform to the baseline authorization requirements +- This extension is specifically designed for HTTP-based transports + +### Standards Compliance + +This extension is based on the following established specifications: + +- OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) +- Digest Fields ([RFC 9530](https://datatracker.ietf.org/doc/html/rfc9530)) +- OAuth 2.0 Authorization Server Metadata ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) + +## DPoP for MCP Resource Access + +### Overview + +DPoP provides application-layer proof-of-possession for OAuth 2.0 access tokens. In the MCP context, clients prove possession of a private key when making JSON-RPC requests to MCP servers. This proof is verified by the MCP server before granting access to protected resources. + +The key enhancement in this MCP profile is the required inclusion of a content digest in the DPoP proof, which binds the proof cryptographically to the specific JSON-RPC request body. This addresses the unique security challenge posed by MCP's single-endpoint architecture. + +### DPoP Proof Requirements + +When accessing protected MCP server resources, clients MUST include a DPoP proof JWT in the `DPoP` HTTP header as specified in [RFC 9449 Section 4](https://datatracker.ietf.org/doc/html/rfc9449#section-4). + +The DPoP proof MUST contain all claims required by [RFC 9449 Section 4.2](https://datatracker.ietf.org/doc/html/rfc9449#section-4.2), including: + +- `jti`: Unique identifier for the DPoP proof +- `htm`: HTTP method (will be `POST` for JSON-RPC requests) +- `htu`: HTTP target URI of the MCP server endpoint +- `iat`: Creation timestamp +- `ath`: Hash of the access token + +Additionally, the DPoP proof MUST include a `content_digest` claim containing the Content-Digest value of the request body as defined in [RFC 9530](https://datatracker.ietf.org/doc/html/rfc9530). + +This requirement addresses MCP's architecture, in which all requests use the same HTTP endpoint and method (JSON-RPC over HTTP POST). Standard DPoP binds proofs to the endpoint and HTTP method, but does not bind them to the request payload, allowing intercepted proofs to be replayed with different message bodies. + +**Note:** The `content_digest` claim is only required when presenting access tokens to MCP servers. It is NOT required (and SHOULD NOT be included) in DPoP proofs sent to the authorization server's token endpoint, as those requests are already protected by PKCE and other OAuth 2.1 security mechanisms. + +### Content Digest Calculation + +The `content_digest` claim value MUST be calculated as follows: + +1. Calculate the SHA-256 hash of the JSON-RPC request body (the raw bytes of the HTTP request body) +2. Base64-encode the hash using the standard base64 encoding (not base64url) +3. Format as: `sha-256=:BASE64_ENCODED_HASH:` + +The format follows the structured field syntax defined in [RFC 9530 Section 2](https://datatracker.ietf.org/doc/html/rfc9530#section-2). + +Example content_digest value: + +``` +sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=: + +``` + +****Note:**** This claim is independent of any `Content-Digest` HTTP header that may be present in the message. The digest is calculated by the client and verified by the MCP server as part of DPoP proof validation. It is not related to HTTP message integrity mechanisms or intermediary-added headers. + +### DPoP Proof Validity Window + +MCP servers MUST accept DPoP proofs with an `iat` claim within ±5 minutes of the server's current time. This accommodates reasonable clock skew between clients and servers while limiting the window for replay attacks. + +MCP servers SHOULD reject DPoP proofs outside this time window with an HTTP 401 response and appropriate error signaling as defined in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). + +### Access Token Presentation + +When making requests to protected MCP server resources, clients MUST: + +1. Include the access token using the `Authorization` header with the `DPoP` authentication scheme as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1) +2. Include a fresh DPoP proof in the `DPoP` header +3. Ensure the DPoP proof includes the `content_digest` claim + +Example request: + +``` +POST /mcp HTTP/1.1 +Host: mcp.example.com +Content-Type: application/json +Authorization: DPoP eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9... +DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6... + +{"jsonrpc":"2.0","method":"tools/list","id":1} + +``` + +### MCP Server Validation + +MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3), with the following additional requirements specific to MCP: + +1. Verify that the `content_digest` claim is present in the DPoP proof +2. Calculate the SHA-256 hash of the received request body +3. Verify that the calculated digest matches the `content_digest` claim value +4. Verify that the `iat` claim is within ±5 minutes of the server's current time +5. Verify that the `htm` claim is `POST` +6. Verify that the `htu` claim matches the MCP server endpoint URI + +If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). + +### Stateless Operation + +This MCP profile of DPoP is designed to enable stateless implementations. MCP servers are NOT required to track `jti` values to detect replay attacks, as the `content_digest` claim provides sufficient protection by binding each proof to a specific request body. + +However, MCP servers MAY choose to implement `jti` tracking for additional security, following the guidance in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). + +## Authorization Server Metadata + +Authorization servers supporting this extension MUST include the `dpop_signing_alg_values_supported` field in their Authorization Server Metadata as defined in [RFC 9449 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9449#section-5.1). + +This field MUST contain a JSON array listing the JWS algorithm values supported for DPoP proof JWTs. Only asymmetric signature algorithms are permitted; the `none` algorithm MUST NOT be included. + +Example Authorization Server Metadata: + +```json +{ + "issuer": "https://auth.example.com", + "authorization_endpoint": "https://auth.example.com/authorize", + "token_endpoint": "https://auth.example.com/token", + "dpop_signing_alg_values_supported": ["ES256", "RS256", "PS256"] +} + +``` + +**Note on Algorithm Selection:** While this specification does not mandate specific algorithms, authorization servers and clients SHOULD support modern, secure algorithms such as ES256 (ECDSA using P-256 and SHA-256). Implementations should avoid deprecated or weak algorithms. The security considerations of chosen algorithms are beyond the scope of this specification; implementers should consult current cryptographic best practices. + +## Client Registration Metadata + +Clients that exclusively use DPoP MAY indicate this through the `dpop_bound_access_tokens` client registration metadata field as defined in [RFC 9449 Section 5.2](https://datatracker.ietf.org/doc/html/rfc9449#section-5.2). + +When this field is set to `true`, the authorization server MUST reject token requests from the client that do not include a valid DPoP proof. + +## Examples + +### Complete DPoP Proof Example + +Below is an example of a decoded DPoP proof JWT suitable for an MCP request: + +**JWT Header:** + +```json +{ + "typ": "dpop+jwt", + "alg": "ES256", + "jwk": { + "kty": "EC", + "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs", + "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA", + "crv": "P-256" + } +} + +``` + +**JWT Payload:** + +```json +{ + "jti": "e1j3V_bKic8-LAEB", + "htm": "POST", + "htu": "https://mcp.example.com/mcp", + "iat": 1699564218, + "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo", + "content_digest": "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:" +} + +``` + +### Complete Request Example + +``` +POST /mcp HTTP/1.1 +Host: mcp.example.com +Content-Type: application/json +Content-Length: 52 +Authorization: DPoP eyJhbGciOiJFUzI1NiIsImtpZCI6IkJlQUxrYiJ9.eyJzdWIiOiJzb21lb25lQGV4YW1wbGUuY29tIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLmV4YW1wbGUuY29tIiwiYXVkIjoiaHR0cHM6Ly9tY3AuZXhhbXBsZS5jb20iLCJuYmYiOjE2OTk1NjQyMTEsImV4cCI6MTY5OTU2NzgxMSwiY25mIjp7ImprdCI6IjBaY09DT1JaTll5LURXcHFxMzBqWnlKR0hUTjBkMkhnbEJWM3VpZ3VBNEkifX0.fT0eG8Qa0nKW8P0qT6YZqVhBnb2r5Q0yX7VqA8Zr1Yk +DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCRnMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERFVXJVMENZR1JEQSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vbWNwLmV4YW1wbGUuY29tL21jcCIsImlhdCI6MTY5OTU2NDIxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNFc05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyIsImNvbnRlbnRfZGlnZXN0Ijoic2hhLTI1Nj06WDQ4RTlxT29rcXFydmR0czhuT0pSSk4zT1dEVW95V3hCZjdrYnU5REJQRTo6In0.mHN0qXKq_EXAMPLE_SIGNATURE_DATA_HERE + +{"jsonrpc":"2.0","method":"tools/list","id":1} + +``` + +### Error Response Example + +When validation fails, the MCP server returns an error response: + +``` +HTTP/1.1 401 Unauthorized +WWW-Authenticate: DPoP error="invalid_dpop_proof", + error_description="Content digest mismatch" +Content-Type: application/json + +{ + "jsonrpc": "2.0", + "error": { + "code": -32001, + "message": "Unauthorized" + }, + "id": 1 +} + +``` + +## Security Considerations + +Implementations adopting this extension MUST follow the security considerations outlined in: + +- [RFC 9449 Section 11 "Security Considerations"](https://datatracker.ietf.org/doc/html/rfc9449#section-11) +- The baseline MCP Authorization specification security requirements +- OAuth 2.1 security best practices + +### Content Digest Protection + +The `content_digest` claim is critical for preventing replay attacks in MCP's single-endpoint architecture. Without it, an attacker who intercepts a valid DPoP proof and access token could replay them with arbitrary JSON-RPC message bodies during the proof's validity window. + +MCP servers MUST validate the content digest before processing any request. Failure to do so negates the primary security benefit of this extension. + +### Proof Validity Window + +The ±5 minute validity window balances security and usability. MCP servers MAY implement stricter windows, but should be aware that overly restrictive windows may cause operational issues due to clock skew in distributed systems. + +For high-security environments, MCP servers SHOULD implement server-provided nonces as described in [RFC 9449 Section 9](https://datatracker.ietf.org/doc/html/rfc9449#section-9) to further limit proof lifetime. + +### Key Storage + +Clients MUST protect DPoP private keys using appropriate security measures. Where possible, private keys SHOULD be stored in hardware security modules or secure enclaves to prevent extraction. If keys can be extracted, an attacker who compromises the client can generate valid DPoP proofs. + +Browser-based clients SHOULD use the Web Crypto API with non-extractable keys to prevent key exfiltration via XSS attacks. + +### Algorithm Security + +Only asymmetric signature algorithms MUST be used for DPoP proofs. Symmetric algorithms and the `none` algorithm MUST NOT be permitted as specified in [RFC 9449 Section 11.6](https://datatracker.ietf.org/doc/html/rfc9449#section-11.6). + +**Open Question:** This specification does not mandate a minimum algorithm strength floor (e.g., requiring ES256 or better). Implementers should carefully evaluate which algorithms to support based on current cryptographic best practices and their security requirements. A future revision of this specification may provide more specific algorithm recommendations. + +### Replay Detection Without State + +This extension is designed to operate statelessly by relying on the `content_digest` claim rather than `jti` tracking. This design choice prioritizes scalability and operational simplicity over the strongest possible replay protection. + +MCP servers requiring maximum replay protection SHOULD implement `jti` tracking despite the operational complexity. The trade-offs are discussed in detail in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). + +### Integration with MCP Security Model + +This extension complements but does not replace the token audience validation requirements specified in the baseline MCP Authorization specification. MCP servers MUST continue to validate that access tokens were specifically issued for them, even when DPoP is used. + +DPoP prevents token theft and replay attacks but does not address confused deputy vulnerabilities or other authorization-level attacks. Implementations must maintain defense in depth by implementing all required security controls. From 485fd15c2bf3ad8ccb394a1e0520f1c20393f811 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:06:28 +0000 Subject: [PATCH 02/19] Clarify DPoP extension documentation and requirements Revised the DPoP extension documentation to clarify the purpose, scope, and requirements for sender-constrained access tokens in the Model Context Protocol. Enhanced explanations of DPoP proof requirements, content digest calculations, and replay protection mechanisms. --- specification/draft/dpop-extension.mdx | 98 +++++++------------------- 1 file changed, 25 insertions(+), 73 deletions(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 065a5b1..c80e7ca 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -4,13 +4,11 @@ ### Purpose and Scope -This extension defines DPoP (Demonstrating Proof-of-Possession) support for the Model Context Protocol, enabling sender-constrained access tokens that prevent token replay attacks. This extension builds upon the baseline authorization requirements defined in the main Authorization specification. - -DPoP binds access tokens to a cryptographic key pair controlled by the client. When accessing MCP server resources, clients must prove possession of the private key by providing a signed proof with each request. This prevents unauthorized use of tokens even if they are intercepted or leaked. +This extension specifies OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) as an optional extension for the Model Context Protocol to support sender-constrained access tokens. DPoP binds OAuth 2.0 access tokens to cryptographic key pairs controlled by MCP clients, requiring clients to demonstrate possession of the corresponding private key with each request. This prevents unauthorized use of tokens even if they are intercepted or leaked. ### Extension Requirements -This extension is OPTIONAL for MCP implementations. When adopted: +This extension is OPTIONAL for MCP implementations. When supported: - Implementations MUST conform to all requirements specified in this extension - Implementations MUST also conform to the baseline authorization requirements @@ -21,59 +19,25 @@ This extension is OPTIONAL for MCP implementations. When adopted: This extension is based on the following established specifications: - OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) -- Digest Fields ([RFC 9530](https://datatracker.ietf.org/doc/html/rfc9530)) - OAuth 2.0 Authorization Server Metadata ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) +- JSON Web Algorithms (JWA) [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518) ## DPoP for MCP Resource Access ### Overview -DPoP provides application-layer proof-of-possession for OAuth 2.0 access tokens. In the MCP context, clients prove possession of a private key when making JSON-RPC requests to MCP servers. This proof is verified by the MCP server before granting access to protected resources. - -The key enhancement in this MCP profile is the required inclusion of a content digest in the DPoP proof, which binds the proof cryptographically to the specific JSON-RPC request body. This addresses the unique security challenge posed by MCP's single-endpoint architecture. - -### DPoP Proof Requirements - -When accessing protected MCP server resources, clients MUST include a DPoP proof JWT in the `DPoP` HTTP header as specified in [RFC 9449 Section 4](https://datatracker.ietf.org/doc/html/rfc9449#section-4). - -The DPoP proof MUST contain all claims required by [RFC 9449 Section 4.2](https://datatracker.ietf.org/doc/html/rfc9449#section-4.2), including: - -- `jti`: Unique identifier for the DPoP proof -- `htm`: HTTP method (will be `POST` for JSON-RPC requests) -- `htu`: HTTP target URI of the MCP server endpoint -- `iat`: Creation timestamp -- `ath`: Hash of the access token +MCP’s current authorization model uses bearer tokens, which can be reused by an unauthorized party if the token is leaked, intercepted or exfiltrated. If an access token is intercepted, through network eavesdropping, compromised logs, or other means, an attacker use the token from a MCP client under its control to access protected MCP resources until the token expires. -Additionally, the DPoP proof MUST include a `content_digest` claim containing the Content-Digest value of the request body as defined in [RFC 9530](https://datatracker.ietf.org/doc/html/rfc9530). +DPoP addresses this by making tokens "sender-constrained" so that even if an attacker obtains an access token, they cannot use it without also possessing or controlling the correspondiong client's private key. This significantly raises the bar for attackers. In the MCP context, MCP clients prove possession of a private key when making JSON-RPC requests to MCP servers. This proof is verified by the MCP server before granting access to protected resources. -This requirement addresses MCP's architecture, in which all requests use the same HTTP endpoint and method (JSON-RPC over HTTP POST). Standard DPoP binds proofs to the endpoint and HTTP method, but does not bind them to the request payload, allowing intercepted proofs to be replayed with different message bodies. +This extension is particularly valuable for: -**Note:** The `content_digest` claim is only required when presenting access tokens to MCP servers. It is NOT required (and SHOULD NOT be included) in DPoP proofs sent to the authorization server's token endpoint, as those requests are already protected by PKCE and other OAuth 2.1 security mechanisms. +- High-security environments handling sensitive data. +- Long-lived access tokens. +- Deployments where token theft risk is elevated. +- Compliance requirements mandating proof of possession or sender constrained tokens. -### Content Digest Calculation - -The `content_digest` claim value MUST be calculated as follows: - -1. Calculate the SHA-256 hash of the JSON-RPC request body (the raw bytes of the HTTP request body) -2. Base64-encode the hash using the standard base64 encoding (not base64url) -3. Format as: `sha-256=:BASE64_ENCODED_HASH:` - -The format follows the structured field syntax defined in [RFC 9530 Section 2](https://datatracker.ietf.org/doc/html/rfc9530#section-2). - -Example content_digest value: - -``` -sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=: - -``` - -****Note:**** This claim is independent of any `Content-Digest` HTTP header that may be present in the message. The digest is calculated by the client and verified by the MCP server as part of DPoP proof validation. It is not related to HTTP message integrity mechanisms or intermediary-added headers. - -### DPoP Proof Validity Window - -MCP servers MUST accept DPoP proofs with an `iat` claim within ±5 minutes of the server's current time. This accommodates reasonable clock skew between clients and servers while limiting the window for replay attacks. - -MCP servers SHOULD reject DPoP proofs outside this time window with an HTTP 401 response and appropriate error signaling as defined in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). +This extension uses DPoP as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449), without introducing MCP-specific claims, request body digests, or additional proof material. This design choice reflects a deliberate trade-off in favor of interoperability, deployability, and alignment with existing OAuth 2.0 security models. It does not preclude the use of the extension mechanisms defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449). ### Access Token Presentation @@ -81,7 +45,6 @@ When making requests to protected MCP server resources, clients MUST: 1. Include the access token using the `Authorization` header with the `DPoP` authentication scheme as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1) 2. Include a fresh DPoP proof in the `DPoP` header -3. Ensure the DPoP proof includes the `content_digest` claim Example request: @@ -98,26 +61,21 @@ DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6... ### MCP Server Validation -MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3), with the following additional requirements specific to MCP: - -1. Verify that the `content_digest` claim is present in the DPoP proof -2. Calculate the SHA-256 hash of the received request body -3. Verify that the calculated digest matches the `content_digest` claim value -4. Verify that the `iat` claim is within ±5 minutes of the server's current time -5. Verify that the `htm` claim is `POST` -6. Verify that the `htu` claim matches the MCP server endpoint URI +MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST verify that the `iat` claim is within ±5 minutes of the server's current time. If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). -### Stateless Operation +### Stateless Operation and DPoP Proof Replay Protection {#stateless-replay-protection} + +[RFC 9449 Section 11.1](https://www.rfc-editor.org/rfc/rfc9449.html#name-dpop-proof-replay) provides specific guidance on replay protection mechanisms that adress the risks of a DPoP Proofs being replayed. -This MCP profile of DPoP is designed to enable stateless implementations. MCP servers are NOT required to track `jti` values to detect replay attacks, as the `content_digest` claim provides sufficient protection by binding each proof to a specific request body. +MCP servers are NOT required to track `jti` values, however, MCP servers MAY choose to implement `jti` tracking for additional security, following the guidance in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). -However, MCP servers MAY choose to implement `jti` tracking for additional security, following the guidance in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). +MCP servers that is not capable of keeping state or perform global `jti` tracking MUST provide DPoP proof replay protection by enforcing short `iat` acceptance windows of ±5 minutes and standard RFC 9449 claim validation. A stateless MCP server may provide additional replay protection by using a server supplied nonce as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449) (e.g. by using an encrypted timestamp as the nonce value which can be decrypted, parsed and verified to be within an acceptable time window when returned in DPoP Proof). For example, the MCP server may create a nonce by encrypting a timestamp using an Authenticated Encryption with Associated Data (AEAD) scheme such as defined in [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518#section-5.2) which can be decrypted, parsed and verified to be within an acceptable time window when the MCP client returns the DPoP Proof to the MCP server). ## Authorization Server Metadata -Authorization servers supporting this extension MUST include the `dpop_signing_alg_values_supported` field in their Authorization Server Metadata as defined in [RFC 9449 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9449#section-5.1). +Authorization servers supporting DPoP MUST include the `dpop_signing_alg_values_supported` field in their Authorization Server Metadata as defined in [RFC 9449 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9449#section-5.1). This field MUST contain a JSON array listing the JWS algorithm values supported for DPoP proof JWTs. Only asymmetric signature algorithms are permitted; the `none` algorithm MUST NOT be included. @@ -172,7 +130,6 @@ Below is an example of a decoded DPoP proof JWT suitable for an MCP request: "htu": "https://mcp.example.com/mcp", "iat": 1699564218, "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo", - "content_digest": "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:" } ``` @@ -185,7 +142,7 @@ Host: mcp.example.com Content-Type: application/json Content-Length: 52 Authorization: DPoP eyJhbGciOiJFUzI1NiIsImtpZCI6IkJlQUxrYiJ9.eyJzdWIiOiJzb21lb25lQGV4YW1wbGUuY29tIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLmV4YW1wbGUuY29tIiwiYXVkIjoiaHR0cHM6Ly9tY3AuZXhhbXBsZS5jb20iLCJuYmYiOjE2OTk1NjQyMTEsImV4cCI6MTY5OTU2NzgxMSwiY25mIjp7ImprdCI6IjBaY09DT1JaTll5LURXcHFxMzBqWnlKR0hUTjBkMkhnbEJWM3VpZ3VBNEkifX0.fT0eG8Qa0nKW8P0qT6YZqVhBnb2r5Q0yX7VqA8Zr1Yk -DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCRnMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERFVXJVMENZR1JEQSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vbWNwLmV4YW1wbGUuY29tL21jcCIsImlhdCI6MTY5OTU2NDIxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNFc05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyIsImNvbnRlbnRfZGlnZXN0Ijoic2hhLTI1Nj06WDQ4RTlxT29rcXFydmR0czhuT0pSSk4zT1dEVW95V3hCZjdrYnU5REJQRTo6In0.mHN0qXKq_EXAMPLE_SIGNATURE_DATA_HERE +DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCRnMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5REFLVXJVMENZR1JEQSIsImNydiI6IlAtMjU2In19LnsianlpIjoiZTFqM1ZfYktpYzgtTEFFQiIsImh0bSI6IlBPU1QiLCJodHUiOiJodHRwczovL21jcC5leGFtcGxlLmNvbS9tY3AiLCJpYXQiOjE2OTk1NjQyMTgsImF0aCI6ImZVSHlPMnIyWjNEWjUzRXNOcldCYjB4V1hvYU55NTlJaUtDQXFrc21RRW8ifQ==.mHN0qXKq_EXAMPLE_SIGNATURE_DATA_HERE {"jsonrpc":"2.0","method":"tools/list","id":1} @@ -220,21 +177,17 @@ Implementations adopting this extension MUST follow the security considerations - The baseline MCP Authorization specification security requirements - OAuth 2.1 security best practices -### Content Digest Protection - -The `content_digest` claim is critical for preventing replay attacks in MCP's single-endpoint architecture. Without it, an attacker who intercepts a valid DPoP proof and access token could replay them with arbitrary JSON-RPC message bodies during the proof's validity window. - -MCP servers MUST validate the content digest before processing any request. Failure to do so negates the primary security benefit of this extension. +In addition, MCP servers MUST provide DPoP proof replay protection as described in {{stateless-replay-protection}} ### Proof Validity Window The ±5 minute validity window balances security and usability. MCP servers MAY implement stricter windows, but should be aware that overly restrictive windows may cause operational issues due to clock skew in distributed systems. -For high-security environments, MCP servers SHOULD implement server-provided nonces as described in [RFC 9449 Section 9](https://datatracker.ietf.org/doc/html/rfc9449#section-9) to further limit proof lifetime. +For high-security environments, MCP servers SHOULD implement server-provided nonces as described in [RFC 9449 Section 9](https://datatracker.ietf.org/doc/html/rfc9449#section-9) to further limit proof lifetime, even if it is stateless (see {{stateless-replay-protection}}). ### Key Storage -Clients MUST protect DPoP private keys using appropriate security measures. Where possible, private keys SHOULD be stored in hardware security modules or secure enclaves to prevent extraction. If keys can be extracted, an attacker who compromises the client can generate valid DPoP proofs. +Clients MUST protect DPoP private keys using appropriate security measures. If hardware security modules or secure enclaves are available, they SHOULD be used to protect private keys to prevent key extraction. If keys can be extracted and exfiltrated, an attacker who compromises the client can generate valid DPoP proofs. Browser-based clients SHOULD use the Web Crypto API with non-extractable keys to prevent key exfiltration via XSS attacks. @@ -246,12 +199,11 @@ Only asymmetric signature algorithms MUST be used for DPoP proofs. Symmetric alg ### Replay Detection Without State -This extension is designed to operate statelessly by relying on the `content_digest` claim rather than `jti` tracking. This design choice prioritizes scalability and operational simplicity over the strongest possible replay protection. +Replay protection options for stateless MCP servers are described in {{stateless-replay-protection}}. MCP servers requiring maximum replay protection SHOULD implement `jti` tracking despite the operational complexity. The trade-offs are discussed in detail in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). -MCP servers requiring maximum replay protection SHOULD implement `jti` tracking despite the operational complexity. The trade-offs are discussed in detail in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). +### DPoP Limitations +DPoP does not prevent token theft, but provides a mechanism that allows detection of replay attacks that utilise a stolen token. In addition, DPoP does not address confused deputy vulnerabilities or other authorization-level attacks. Consequently. implementations must maintain defense in depth by implementing all required security controls. ### Integration with MCP Security Model This extension complements but does not replace the token audience validation requirements specified in the baseline MCP Authorization specification. MCP servers MUST continue to validate that access tokens were specifically issued for them, even when DPoP is used. - -DPoP prevents token theft and replay attacks but does not address confused deputy vulnerabilities or other authorization-level attacks. Implementations must maintain defense in depth by implementing all required security controls. From f2f94539fe4dc28aa53b7caed88f929aba02aec6 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:10:49 +0000 Subject: [PATCH 03/19] Clarify purpose and scope of DPoP extension --- specification/draft/dpop-extension.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index c80e7ca..20fafb1 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -4,7 +4,7 @@ ### Purpose and Scope -This extension specifies OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) as an optional extension for the Model Context Protocol to support sender-constrained access tokens. DPoP binds OAuth 2.0 access tokens to cryptographic key pairs controlled by MCP clients, requiring clients to demonstrate possession of the corresponding private key with each request. This prevents unauthorized use of tokens even if they are intercepted or leaked. +This extension specifies OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) as an optional extension for the Model Context Protocol (MCP) to support sender-constrained access tokens. DPoP binds OAuth 2.0 access tokens to cryptographic key pairs controlled by MCP clients, requiring clients to demonstrate possession of the corresponding private key with each request. This prevents unauthorized use of tokens even if they are intercepted or leaked. ### Extension Requirements From f6a0eb779b4ea4a22c7611dccf8150662a24621f Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Thu, 1 Oct 2026 16:28:26 +0100 Subject: [PATCH 04/19] Add Bearer Scheme Downgrade Protection section Added section on Bearer Scheme Downgrade Protection, specifying that DPoP-bound access tokens must not be used as bearer tokens and detailing the server's response requirements. --- specification/draft/dpop-extension.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 20fafb1..5cd98e0 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -65,6 +65,10 @@ MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https: If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). +### Bearer Scheme Downgrade Protection + +A DPoP-bound access token is sender-constrained and MUST NOT be usable as a bearer token. MCP servers MUST NOT accept a DPoP-bound access token presented using the `Bearer` authentication scheme (see [RFC 9449 Section 7.2](https://datatracker.ietf.org/doc/html/rfc9449#section-7.2)). The MCP server MUST reject such a request with an HTTP 401 response that includes a `WWW-Authenticate` challenge using the `Bearer` ([RFC 6750 Section 3](https://datatracker.ietf.org/doc/html/rfc6750#section-3)) or `DPoP` ([RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1)) scheme, and SHOULD indicate the `invalid_token` error code ([RFC 6750 Section 3.1](https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)). + ### Stateless Operation and DPoP Proof Replay Protection {#stateless-replay-protection} [RFC 9449 Section 11.1](https://www.rfc-editor.org/rfc/rfc9449.html#name-dpop-proof-replay) provides specific guidance on replay protection mechanisms that adress the risks of a DPoP Proofs being replayed. From c7cbbdbbb730152b499d25d5d8094b61719ec16c Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:03:42 +0100 Subject: [PATCH 05/19] Specify error codes for DPoP request rejections Clarified error codes for DPoP proof validation failures and access token issues. See https://github.com/modelcontextprotocol/conformance/pull/528 --- specification/draft/dpop-extension.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 5cd98e0..ed7dc2b 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -65,6 +65,8 @@ MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https: If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). +When rejecting a request, the MCP server SHOULD use the `invalid_dpop_proof`error code for any failure of the [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3) validation steps, including a malformed or duplicated `DPoP` header, and the `invalid_token` error code for failures of the access token itself ([RFC 6750 Section 3.1] https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)). + ### Bearer Scheme Downgrade Protection A DPoP-bound access token is sender-constrained and MUST NOT be usable as a bearer token. MCP servers MUST NOT accept a DPoP-bound access token presented using the `Bearer` authentication scheme (see [RFC 9449 Section 7.2](https://datatracker.ietf.org/doc/html/rfc9449#section-7.2)). The MCP server MUST reject such a request with an HTTP 401 response that includes a `WWW-Authenticate` challenge using the `Bearer` ([RFC 6750 Section 3](https://datatracker.ietf.org/doc/html/rfc6750#section-3)) or `DPoP` ([RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1)) scheme, and SHOULD indicate the `invalid_token` error code ([RFC 6750 Section 3.1](https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)). From 061b1c0689fba361d98832bdca7dc1af9c3bb5c8 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:14:38 +0100 Subject: [PATCH 06/19] Fix formatting of error code references in DPoP section --- specification/draft/dpop-extension.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index ed7dc2b..9a16ad9 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -65,7 +65,7 @@ MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https: If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). -When rejecting a request, the MCP server SHOULD use the `invalid_dpop_proof`error code for any failure of the [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3) validation steps, including a malformed or duplicated `DPoP` header, and the `invalid_token` error code for failures of the access token itself ([RFC 6750 Section 3.1] https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)). +When rejecting a request, the MCP server SHOULD use the `invalid_dpop_proof` error code for any failure of the [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3) validation steps, including a malformed or duplicated `DPoP` header, and the `invalid_token` error code for failures of the access token itself ([RFC 6750 Section 3.1](https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)). ### Bearer Scheme Downgrade Protection From e4458f0f7f52056acc8b4deb2ae49d980740e681 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:57:12 +0100 Subject: [PATCH 07/19] Enhance DPoP extension with MCP server advertisement Added DPoP advertisement section for MCP servers, including requirements for `dpop_signing_alg_values_supported` and `dpop_bound_access_tokens_required` fields in Protected Resource Metadata. --- specification/draft/dpop-extension.mdx | 35 +++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 9a16ad9..6a67e92 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -20,6 +20,7 @@ This extension is based on the following established specifications: - OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) - OAuth 2.0 Authorization Server Metadata ([RFC8414](https://datatracker.ietf.org/doc/html/rfc8414)) +- OAuth 2.0 Protected Resource Metadata ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)) - JSON Web Algorithms (JWA) [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518) ## DPoP for MCP Resource Access @@ -92,7 +93,7 @@ Example Authorization Server Metadata: "issuer": "https://auth.example.com", "authorization_endpoint": "https://auth.example.com/authorize", "token_endpoint": "https://auth.example.com/token", - "dpop_signing_alg_values_supported": ["ES256", "RS256", "PS256"] + "dpop_signing_alg_values_supported": ["ES256", "PS256"] } ``` @@ -175,6 +176,38 @@ Content-Type: application/json ``` +## MCP Server DPoP Advertisement + +MCP clients need a way to learn that an MCP server supports or requires DPoP before presenting credentials. This section defines how MCP servers supporting this extension advertise that capability. The baseline MCP authorization discovery mechanisms (Protected Resource Metadata discovery via the `WWW-Authenticate` header or well-known URIs) are unchanged. + +### Protected Resource Metadata + +MCP servers supporting this extension MUST include the `dpop_signing_alg_values_supported` field in their Protected Resource Metadata ([RFC 9728 Section 2](https://datatracker.ietf.org/doc/html/rfc9728#section-2)). This field MUST contain a non-empty JSON array of registered JWS algorithm values (from the IANA JSON Web Signature and Encryption Algorithms registry) that the server accepts for DPoP proof JWTs. Only asymmetric digital signature algorithms are permitted. The `none` algorithm and symmetric (MAC) algorithms MUST NOT be included. + +An MCP server that requires DPoP-bound access tokens (that is, one that does not accept bearer tokens) MUST set the `dpop_bound_access_tokens_required` field to `true` in its Protected Resource Metadata. Conversely, an MCP server that sets `dpop_bound_access_tokens_required` to `true` MUST reject requests that do not present a DPoP-bound access token with a valid DPoP proof. When the field is absent, the default behavior defined in [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) applies and DPoP-bound access tokens are not required. + +Example Protected Resource Metadata for a server that requires DPoP: + +```json +{ + "resource": "https://mcp.example.com/mcp", + "authorization_servers": ["https://auth.example.com"], + "dpop_signing_alg_values_supported": ["ES256", "PS256"], + "dpop_bound_access_tokens_required": true +} +``` +### WWW-Authenticate Challenge + +When responding to an unauthenticated or failed request with HTTP 401, an MCP server supporting this extension SHOULD include a `DPoP` challenge in the `WWW-Authenticate` header, and that challenge SHOULD include the `algs` parameter signaling the JWS algorithms acceptable for DPoP proof JWTs, as described in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). The `DPoP` challenge MAY appear alongside other challenges, such as a `Bearer` challenge carrying the `resource_metadata` parameter defined in [RFC 9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1). + +Example challenge: + +``` +HTTP/1.1 401 Unauthorized +WWW-Authenticate: DPoP algs="ES256 PS256", + resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp" +``` + ## Security Considerations Implementations adopting this extension MUST follow the security considerations outlined in: From e6a2bbf0c600ea5fb4e0ea362d2e2beb2e5c9039 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:04:06 +0100 Subject: [PATCH 08/19] Document MCP server DPoP advertisement requirements Added details on MCP server DPoP advertisement and requirements for DPoP-bound access tokens. --- specification/draft/dpop-extension.mdx | 64 +++++++++++++------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 6a67e92..4c82886 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -106,6 +106,38 @@ Clients that exclusively use DPoP MAY indicate this through the `dpop_bound_acce When this field is set to `true`, the authorization server MUST reject token requests from the client that do not include a valid DPoP proof. +## MCP Server DPoP Advertisement + +MCP clients need a way to learn that an MCP server supports or requires DPoP before presenting credentials. This section defines how MCP servers supporting this extension advertise that capability. The baseline MCP authorization discovery mechanisms (Protected Resource Metadata discovery via the `WWW-Authenticate` header or well-known URIs) are unchanged. + +### Protected Resource Metadata + +MCP servers supporting this extension MUST include the `dpop_signing_alg_values_supported` field in their Protected Resource Metadata ([RFC 9728 Section 2](https://datatracker.ietf.org/doc/html/rfc9728#section-2)). This field MUST contain a non-empty JSON array of registered JWS algorithm values (from the IANA JSON Web Signature and Encryption Algorithms registry) that the server accepts for DPoP proof JWTs. Only asymmetric digital signature algorithms are permitted. The `none` algorithm and symmetric (MAC) algorithms MUST NOT be included. + +An MCP server that requires DPoP-bound access tokens (that is, one that does not accept bearer tokens) MUST set the `dpop_bound_access_tokens_required` field to `true` in its Protected Resource Metadata. Conversely, an MCP server that sets `dpop_bound_access_tokens_required` to `true` MUST reject requests that do not present a DPoP-bound access token with a valid DPoP proof. When the field is absent, the default behavior defined in [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) applies and DPoP-bound access tokens are not required. + +Example Protected Resource Metadata for a server that requires DPoP: + +```json +{ + "resource": "https://mcp.example.com/mcp", + "authorization_servers": ["https://auth.example.com"], + "dpop_signing_alg_values_supported": ["ES256", "PS256"], + "dpop_bound_access_tokens_required": true +} +``` +### WWW-Authenticate Challenge + +When responding to an unauthenticated or failed request with HTTP 401, an MCP server supporting this extension SHOULD include a `DPoP` challenge in the `WWW-Authenticate` header, and that challenge SHOULD include the `algs` parameter signaling the JWS algorithms acceptable for DPoP proof JWTs, as described in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). The `DPoP` challenge MAY appear alongside other challenges, such as a `Bearer` challenge carrying the `resource_metadata` parameter defined in [RFC 9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1). + +Example challenge: + +``` +HTTP/1.1 401 Unauthorized +WWW-Authenticate: DPoP algs="ES256 PS256", + resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp" +``` + ## Examples ### Complete DPoP Proof Example @@ -176,38 +208,6 @@ Content-Type: application/json ``` -## MCP Server DPoP Advertisement - -MCP clients need a way to learn that an MCP server supports or requires DPoP before presenting credentials. This section defines how MCP servers supporting this extension advertise that capability. The baseline MCP authorization discovery mechanisms (Protected Resource Metadata discovery via the `WWW-Authenticate` header or well-known URIs) are unchanged. - -### Protected Resource Metadata - -MCP servers supporting this extension MUST include the `dpop_signing_alg_values_supported` field in their Protected Resource Metadata ([RFC 9728 Section 2](https://datatracker.ietf.org/doc/html/rfc9728#section-2)). This field MUST contain a non-empty JSON array of registered JWS algorithm values (from the IANA JSON Web Signature and Encryption Algorithms registry) that the server accepts for DPoP proof JWTs. Only asymmetric digital signature algorithms are permitted. The `none` algorithm and symmetric (MAC) algorithms MUST NOT be included. - -An MCP server that requires DPoP-bound access tokens (that is, one that does not accept bearer tokens) MUST set the `dpop_bound_access_tokens_required` field to `true` in its Protected Resource Metadata. Conversely, an MCP server that sets `dpop_bound_access_tokens_required` to `true` MUST reject requests that do not present a DPoP-bound access token with a valid DPoP proof. When the field is absent, the default behavior defined in [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) applies and DPoP-bound access tokens are not required. - -Example Protected Resource Metadata for a server that requires DPoP: - -```json -{ - "resource": "https://mcp.example.com/mcp", - "authorization_servers": ["https://auth.example.com"], - "dpop_signing_alg_values_supported": ["ES256", "PS256"], - "dpop_bound_access_tokens_required": true -} -``` -### WWW-Authenticate Challenge - -When responding to an unauthenticated or failed request with HTTP 401, an MCP server supporting this extension SHOULD include a `DPoP` challenge in the `WWW-Authenticate` header, and that challenge SHOULD include the `algs` parameter signaling the JWS algorithms acceptable for DPoP proof JWTs, as described in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). The `DPoP` challenge MAY appear alongside other challenges, such as a `Bearer` challenge carrying the `resource_metadata` parameter defined in [RFC 9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1). - -Example challenge: - -``` -HTTP/1.1 401 Unauthorized -WWW-Authenticate: DPoP algs="ES256 PS256", - resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp" -``` - ## Security Considerations Implementations adopting this extension MUST follow the security considerations outlined in: From 203c2f7a46bd93da616301557389906acdeb4cce Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:12:12 +0100 Subject: [PATCH 09/19] Fix formatting in DPoP challenge description Removed extra line break in the DPoP challenge section. --- specification/draft/dpop-extension.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 4c82886..8f4fe2d 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -126,9 +126,10 @@ Example Protected Resource Metadata for a server that requires DPoP: "dpop_bound_access_tokens_required": true } ``` + ### WWW-Authenticate Challenge -When responding to an unauthenticated or failed request with HTTP 401, an MCP server supporting this extension SHOULD include a `DPoP` challenge in the `WWW-Authenticate` header, and that challenge SHOULD include the `algs` parameter signaling the JWS algorithms acceptable for DPoP proof JWTs, as described in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). The `DPoP` challenge MAY appear alongside other challenges, such as a `Bearer` challenge carrying the `resource_metadata` parameter defined in [RFC 9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1). +When responding to an unauthenticated or failed request with HTTP 401, an MCP server supporting this extension SHOULD include a `DPoP` challenge in the `WWW-Authenticate` header, and that challenge SHOULD include the `algs` parameter signaling the JWS algorithms acceptable for DPoP proof JWTs, as described in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). The `DPoP` challenge MAY appear alongside other challenges, such as a `Bearer` challenge carrying the `resource_metadata` parameter defined in [RFC 9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1). Example challenge: From 977e07663b1862bda943701d1131e55bf476a283 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 2 Oct 2026 15:13:25 +0100 Subject: [PATCH 10/19] Update DPoP challenge requirements in specification Clarify requirements for DPoP challenge in WWW-Authenticate header. --- specification/draft/dpop-extension.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 8f4fe2d..55cefad 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -131,6 +131,8 @@ Example Protected Resource Metadata for a server that requires DPoP: When responding to an unauthenticated or failed request with HTTP 401, an MCP server supporting this extension SHOULD include a `DPoP` challenge in the `WWW-Authenticate` header, and that challenge SHOULD include the `algs` parameter signaling the JWS algorithms acceptable for DPoP proof JWTs, as described in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). The `DPoP` challenge MAY appear alongside other challenges, such as a `Bearer` challenge carrying the `resource_metadata` parameter defined in [RFC 9728 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9728#section-5.1). +An MCP server that sets `dpop_bound_access_tokens_required` to `true` in its Protected Resource Metadata MUST include a `DPoP` challenge in every `WWW-Authenticate` response it sends. Other challenges MAY appear alongside it, for example a `Bearer` challenge used to deliver error information to a client that attempted bearer authentication, as described in [RFC 9449 Section 7.2](https://datatracker.ietf.org/doc/html/rfc9449#section-7.2). + Example challenge: ``` From 38ea8dcf9672effc9e9072478aaa6aad832c2418 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:10:10 +0100 Subject: [PATCH 11/19] Add authorization code binding recommendation (dpop_jkt) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a SHOULD for MCP clients to bind the authorization code to their DPoP key per RFC 9449 Section 10. RFC 9449 makes this OPTIONAL; this profile recommends it as defense in depth alongside MCP’s mandatory PKCE. Surfaced by the conformance work in modelcontextprotocol/conformance#523; kept separate from #33 so the two can be discussed and merged independently. --- specification/draft/dpop-extension.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 20fafb1..20fac5b 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -59,6 +59,10 @@ DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6... ``` +### Authorization Code Binding + +MCP clients using the authorization code grant SHOULD bind the authorization code to their DPoP key, as described in [RFC 9449 Section 10](https://datatracker.ietf.org/doc/html/rfc9449#section-10). + ### MCP Server Validation MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST verify that the `iat` claim is within ±5 minutes of the server's current time. From db3e520624caabc1d56cfcc215cd246007ecc512 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:16:07 +0100 Subject: [PATCH 12/19] Harmonizing requriements across documents and the conformance suite Clarified DPoP proof validation requirements and replay protection mechanisms for MCP servers, emphasizing the rejection of invalid 'iat' claims and the use of nonces for enhanced security. --- specification/draft/dpop-extension.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 20fafb1..75cb10f 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -61,7 +61,7 @@ DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6... ### MCP Server Validation -MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST verify that the `iat` claim is within ±5 minutes of the server's current time. +MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST reject DPoP proofs whose `iat` claim falls outside ±5 minutes of the server's current time. Servers MAY enforce a narrower acceptance window (see [Proof Validity Window](#proof-validity-window)). If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). @@ -71,7 +71,7 @@ If any validation step fails, the MCP server MUST reject the request with an HTT MCP servers are NOT required to track `jti` values, however, MCP servers MAY choose to implement `jti` tracking for additional security, following the guidance in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). -MCP servers that is not capable of keeping state or perform global `jti` tracking MUST provide DPoP proof replay protection by enforcing short `iat` acceptance windows of ±5 minutes and standard RFC 9449 claim validation. A stateless MCP server may provide additional replay protection by using a server supplied nonce as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449) (e.g. by using an encrypted timestamp as the nonce value which can be decrypted, parsed and verified to be within an acceptable time window when returned in DPoP Proof). For example, the MCP server may create a nonce by encrypting a timestamp using an Authenticated Encryption with Associated Data (AEAD) scheme such as defined in [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518#section-5.2) which can be decrypted, parsed and verified to be within an acceptable time window when the MCP client returns the DPoP Proof to the MCP server). +MCP servers that are not capable of keeping state or performing global `jti` tracking MUST rely on the `iat` acceptance window above (±5 minutes or narrower) together with standard RFC 9449 claim validation as their DPoP proof replay protection. A stateless MCP server may provide additional replay protection by using a server supplied nonce as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449) (e.g. by using an encrypted timestamp as the nonce value which can be decrypted, parsed and verified to be within an acceptable time window when returned in DPoP Proof). For example, the MCP server may create a nonce by encrypting a timestamp using an Authenticated Encryption with Associated Data (AEAD) scheme such as defined in [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518#section-5.2) which can be decrypted, parsed and verified to be within an acceptable time window when the MCP client returns the DPoP Proof to the MCP server). ## Authorization Server Metadata @@ -181,7 +181,7 @@ In addition, MCP servers MUST provide DPoP proof replay protection as described ### Proof Validity Window -The ±5 minute validity window balances security and usability. MCP servers MAY implement stricter windows, but should be aware that overly restrictive windows may cause operational issues due to clock skew in distributed systems. +The ±5 minute bound balances security and usability. MCP servers MAY enforce a narrower window within it, but should be aware that overly restrictive windows may cause operational issues due to clock skew in distributed systems. For high-security environments, MCP servers SHOULD implement server-provided nonces as described in [RFC 9449 Section 9](https://datatracker.ietf.org/doc/html/rfc9449#section-9) to further limit proof lifetime, even if it is stateless (see {{stateless-replay-protection}}). From 6f396437e6c516b5d1e6e53b26550edf21548353 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:25:40 +0100 Subject: [PATCH 13/19] Fix markdown link syntax in dpop-extension.mdx --- specification/draft/dpop-extension.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 75cb10f..1ebc963 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -61,7 +61,7 @@ DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6... ### MCP Server Validation -MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST reject DPoP proofs whose `iat` claim falls outside ±5 minutes of the server's current time. Servers MAY enforce a narrower acceptance window (see [Proof Validity Window](#proof-validity-window)). +MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST reject DPoP proofs whose `iat` claim falls outside ±5 minutes of the server's current time. Servers MAY enforce a narrower acceptance window (see {{proof-validity-window}}). If any validation step fails, the MCP server MUST reject the request with an HTTP 401 response and include appropriate error information in the `WWW-Authenticate` header as specified in [RFC 9449 Section 7.1](https://datatracker.ietf.org/doc/html/rfc9449#section-7.1). @@ -179,7 +179,7 @@ Implementations adopting this extension MUST follow the security considerations In addition, MCP servers MUST provide DPoP proof replay protection as described in {{stateless-replay-protection}} -### Proof Validity Window +### Proof Validity Window {#proof-validity-window} The ±5 minute bound balances security and usability. MCP servers MAY enforce a narrower window within it, but should be aware that overly restrictive windows may cause operational issues due to clock skew in distributed systems. From e66633fd7f584b70d3a1ab8e75117c08a0c88372 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:39:54 +0100 Subject: [PATCH 14/19] Add server-provided nonces section to DPoP extension Added section on server-provided nonces in DPoP proofs, detailing requirements for authorization and MCP servers regarding nonce handling and client behavior. --- specification/draft/dpop-extension.mdx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 1ebc963..0372741 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -59,6 +59,16 @@ DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6... ``` +### Server-Provided Nonces + +Authorization servers and MCP servers MAY require a server-provided nonce in DPoP proofs, as described in [RFC 9449 Section 8](https://datatracker.ietf.org/doc/html/rfc9449#section-8) and [Section 9](https://datatracker.ietf.org/doc/html/rfc9449#section-9). + +When the authorization server responds with `use_dpop_nonce`, the client MUST retry the token request with a DPoP proof that includes the supplied `nonce` ([RFC 9449 Section 8](https://datatracker.ietf.org/doc/html/rfc9449#section-8)). + +When the MCP server responds with `use_dpop_nonce`, the client MUST retry the request with a DPoP proof that includes the supplied `nonce` ([RFC 9449 Section 9](https://datatracker.ietf.org/doc/html/rfc9449#section-9)). + +RFC 9449 describes the client's retry in non-normative terms ("the client is expected to retry"); this profile tightens it to MUST because a client that cannot complete the nonce flow cannot interoperate with authorization servers or MCP servers that require nonces. + ### MCP Server Validation MCP servers MUST validate DPoP proofs according to [RFC 9449 Section 4.3](https://datatracker.ietf.org/doc/html/rfc9449#section-4.3). Implementations conforming to this specification MUST reject DPoP proofs whose `iat` claim falls outside ±5 minutes of the server's current time. Servers MAY enforce a narrower acceptance window (see {{proof-validity-window}}). From 0ee104db1b3fca874fc63f6bfdeffcb0e4225a1e Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:58:30 +0100 Subject: [PATCH 15/19] Add DPoP-bound access tokens section to specification Added section on obtaining DPoP-bound access tokens, detailing requirements for MCP clients to include DPoP proof in token requests. --- specification/draft/dpop-extension.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 0372741..a1df8ce 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -39,6 +39,10 @@ This extension is particularly valuable for: This extension uses DPoP as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449), without introducing MCP-specific claims, request body digests, or additional proof material. This design choice reflects a deliberate trade-off in favor of interoperability, deployability, and alignment with existing OAuth 2.0 security models. It does not preclude the use of the extension mechanisms defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449). +### Obtaining DPoP-Bound Access Tokens + +MCP clients conforming to this extension MUST obtain DPoP-bound access tokens by including a DPoP proof in the `DPoP` header of the token request, as defined in [RFC 9449 Section 5](https://datatracker.ietf.org/doc/html/rfc9449#section-5). RFC 9449 is not modified by this profile: token binding and signaling (`cnf`/`jkt`, `token_type: DPoP`, [Section 6](https://datatracker.ietf.org/doc/html/rfc9449#section-6)), refresh-token binding for public clients ([Section 5](https://datatracker.ietf.org/doc/html/rfc9449#section-5)), and error handling at the token endpoint apply as specified there. + ### Access Token Presentation When making requests to protected MCP server resources, clients MUST: From 77ca5eb1eef13f80eadef2dc62404831f39eee10 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:12:52 +0100 Subject: [PATCH 16/19] Enhance DPoP Proof replay protection details Clarified nonce usage and replay protection mechanisms for DPoP Proofs, including guidance on client binding and stateless server requirements. --- specification/draft/dpop-extension.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index a1df8ce..35e57ae 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -85,6 +85,8 @@ If any validation step fails, the MCP server MUST reject the request with an HTT MCP servers are NOT required to track `jti` values, however, MCP servers MAY choose to implement `jti` tracking for additional security, following the guidance in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). +A nonce constructed this way is valid for any client during its time window. This is consistent with [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1), which permits the same nonce value to be used by multiple clients and accepted multiple times. Replay protection comes from each DPoP proof's key binding and freshness, not from nonce uniqueness. A server MAY nonetheless bind the nonce to a client, for example by encrypting a client identifier together with the timestamp, as additional hardening. The nonce is opaque to clients, so this choice has no interoperability impact. + MCP servers that are not capable of keeping state or performing global `jti` tracking MUST rely on the `iat` acceptance window above (±5 minutes or narrower) together with standard RFC 9449 claim validation as their DPoP proof replay protection. A stateless MCP server may provide additional replay protection by using a server supplied nonce as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449) (e.g. by using an encrypted timestamp as the nonce value which can be decrypted, parsed and verified to be within an acceptable time window when returned in DPoP Proof). For example, the MCP server may create a nonce by encrypting a timestamp using an Authenticated Encryption with Associated Data (AEAD) scheme such as defined in [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518#section-5.2) which can be decrypted, parsed and verified to be within an acceptable time window when the MCP client returns the DPoP Proof to the MCP server). ## Authorization Server Metadata From a7d684b9e2bbcc55bdfd872729047d416f322282 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:44:30 +0100 Subject: [PATCH 17/19] Fix formatting and clarify nonce usage in DPoP extension --- specification/draft/dpop-extension.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 35e57ae..505fb33 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -85,10 +85,10 @@ If any validation step fails, the MCP server MUST reject the request with an HTT MCP servers are NOT required to track `jti` values, however, MCP servers MAY choose to implement `jti` tracking for additional security, following the guidance in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). -A nonce constructed this way is valid for any client during its time window. This is consistent with [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1), which permits the same nonce value to be used by multiple clients and accepted multiple times. Replay protection comes from each DPoP proof's key binding and freshness, not from nonce uniqueness. A server MAY nonetheless bind the nonce to a client, for example by encrypting a client identifier together with the timestamp, as additional hardening. The nonce is opaque to clients, so this choice has no interoperability impact. - MCP servers that are not capable of keeping state or performing global `jti` tracking MUST rely on the `iat` acceptance window above (±5 minutes or narrower) together with standard RFC 9449 claim validation as their DPoP proof replay protection. A stateless MCP server may provide additional replay protection by using a server supplied nonce as defined in [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449) (e.g. by using an encrypted timestamp as the nonce value which can be decrypted, parsed and verified to be within an acceptable time window when returned in DPoP Proof). For example, the MCP server may create a nonce by encrypting a timestamp using an Authenticated Encryption with Associated Data (AEAD) scheme such as defined in [RFC 7518 Section 5.2](https://www.rfc-editor.org/rfc/rfc7518#section-5.2) which can be decrypted, parsed and verified to be within an acceptable time window when the MCP client returns the DPoP Proof to the MCP server). +A nonce constructed this way is valid for any client during its time window. This is consistent with [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1), which permits the same nonce value to be used by multiple clients and accepted multiple times. Replay protection comes from each DPoP proof's key binding and freshness, not from nonce uniqueness. A server MAY nonetheless bind the nonce to a client, for example by encrypting a client identifier together with the timestamp, as additional hardening. The nonce is opaque to clients, so this choice has no interoperability impact. + ## Authorization Server Metadata Authorization servers supporting DPoP MUST include the `dpop_signing_alg_values_supported` field in their Authorization Server Metadata as defined in [RFC 9449 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9449#section-5.1). From a3985dd380cb58eaa59c5be33f5be9d7c0afef37 Mon Sep 17 00:00:00 2001 From: PieterKas <90690777+PieterKas@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:16:13 +0100 Subject: [PATCH 18/19] Enhance DPoP extension documentation with example challenge Add example challenge for server supporting dual authentication schemes with scope and resource metadata. --- specification/draft/dpop-extension.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/specification/draft/dpop-extension.mdx b/specification/draft/dpop-extension.mdx index 55cefad..2446065 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/draft/dpop-extension.mdx @@ -141,6 +141,17 @@ WWW-Authenticate: DPoP algs="ES256 PS256", resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp" ``` +Example challenge from a server accepting both schemes and indicating the required scope: + +``` +HTTP/1.1 401 Unauthorized +WWW-Authenticate: Bearer scope="files:read", + resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp", + DPoP algs="ES256 PS256" +``` + +This example extends the dual-challenge form of [RFC 9449 Section 7.2, Figure 17](https://datatracker.ietf.org/doc/html/rfc9449#section-7.2) with the MCP discovery parameters. The `scope` parameter ([RFC 6750 Section 3](https://datatracker.ietf.org/doc/html/rfc6750#section-3)) is used the same way under either scheme, and `resource_metadata` leads the client to the Protected Resource Metadata, which authoritatively states whether DPoP is required (`dpop_bound_access_tokens_required`) and which algorithms the server accepts. A client can determine, before its first authenticated request, whether to present a DPoP-bound token, with the challenge parameters carrying the same signals reactively. Clients supporting this extension use the `DPoP` challenge. Clients that do not support it continue with the `Bearer` challenge unchanged. + ## Examples ### Complete DPoP Proof Example From ab07775024f3f4f23f7c7cae015197498fd052bd Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Fri, 23 Oct 2026 15:34:20 +0000 Subject: [PATCH 19/19] Add DPoP as an experimental extension Move Pieter Kasselman's DPoP profile from specification/draft/ to specification/experimental/dpop.mdx and list it in README.md. The only changes to the text are the ones Prettier makes, so the format check passes. --- README.md | 6 ++++++ .../{draft/dpop-extension.mdx => experimental/dpop.mdx} | 6 ++---- 2 files changed, 8 insertions(+), 4 deletions(-) rename specification/{draft/dpop-extension.mdx => experimental/dpop.mdx} (99%) diff --git a/README.md b/README.md index b985e20..d2c1b04 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,12 @@ The directory that holds an extension's specification shows its stage: `specific - [Enterprise-Managed Authorization](./specification/stable/enterprise-managed-authorization/2026-07-28/enterprise-managed-authorization.mdx) ([draft](./specification/stable/enterprise-managed-authorization/draft/enterprise-managed-authorization.mdx)) - [OAuth Client Credentials](./specification/stable/oauth-client-credentials/2026-07-28/oauth-client-credentials.mdx) ([draft](./specification/stable/oauth-client-credentials/draft/oauth-client-credentials.mdx)) +### Experimental + +Experimental extensions may change or be withdrawn at any time, without notice. + +- [DPoP Profile for MCP](./specification/experimental/dpop.mdx) + ## Governance This repository follows the [Model Context Protocol Governance](https://modelcontextprotocol.io/community/governance) process, with a dedicated set of Extension Maintainers for authorization extensions, listed in [MAINTAINERS.md](./MAINTAINERS.md). The extensions are associated with the [Authorization Interest Group](https://modelcontextprotocol.io/community/interest-groups/auth). diff --git a/specification/draft/dpop-extension.mdx b/specification/experimental/dpop.mdx similarity index 99% rename from specification/draft/dpop-extension.mdx rename to specification/experimental/dpop.mdx index 2e23759..8edf4f1 100644 --- a/specification/draft/dpop-extension.mdx +++ b/specification/experimental/dpop.mdx @@ -115,7 +115,6 @@ Example Authorization Server Metadata: "token_endpoint": "https://auth.example.com/token", "dpop_signing_alg_values_supported": ["ES256", "PS256"] } - ``` **Note on Algorithm Selection:** While this specification does not mandate specific algorithms, authorization servers and clients SHOULD support modern, secure algorithms such as ES256 (ECDSA using P-256 and SHA-256). Implementations should avoid deprecated or weak algorithms. The security considerations of chosen algorithms are beyond the scope of this specification; implementers should consult current cryptographic best practices. @@ -191,7 +190,6 @@ Below is an example of a decoded DPoP proof JWT suitable for an MCP request: "crv": "P-256" } } - ``` **JWT Payload:** @@ -202,9 +200,8 @@ Below is an example of a decoded DPoP proof JWT suitable for an MCP request: "htm": "POST", "htu": "https://mcp.example.com/mcp", "iat": 1699564218, - "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo", + "ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo" } - ``` ### Complete Request Example @@ -275,6 +272,7 @@ Only asymmetric signature algorithms MUST be used for DPoP proofs. Symmetric alg Replay protection options for stateless MCP servers are described in {{stateless-replay-protection}}. MCP servers requiring maximum replay protection SHOULD implement `jti` tracking despite the operational complexity. The trade-offs are discussed in detail in [RFC 9449 Section 11.1](https://datatracker.ietf.org/doc/html/rfc9449#section-11.1). ### DPoP Limitations + DPoP does not prevent token theft, but provides a mechanism that allows detection of replay attacks that utilise a stolen token. In addition, DPoP does not address confused deputy vulnerabilities or other authorization-level attacks. Consequently. implementations must maintain defense in depth by implementing all required security controls. ### Integration with MCP Security Model