-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[spec] Auth: Confused deputy #407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[spec] Auth: Confused deputy #407
Conversation
|
Previous discussion:
|
Co-authored-by: Den Delimarsky 🌺 <[email protected]>
| An attacker can exploit configurations where an MCP server acts as an intermediary between MCP clients and a protected third-party API, leading to the confused deputy problem. The attacker can acquire and exchange a stolen authorization code for access tokens for the MCP server without the user's consent. Consult [Security Best Practices section 2.1](/specification/draft/basic/security_best_practices) for a more detailed description. | ||
|
|
||
| MCP proxy servers that use a static client ID for third-party services MUST require explicit | ||
| approval for each dynamically registered client before forwarding requests to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wording "explicit approval" is ambiguous about what constitutes approval. Suggested rephrasing: MCP proxy servers that use a static client ID for third-party services MUST prompt the end user for consent for each dynamically registered client before forwarding the user to the third-party authorization server (where the third-party may also prompt for its own user consent).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would trim this down a bit more:
MCP proxy servers using static client IDs **MUST** obtain user consent for each
dynamically registered client before forwarding to third-party authorization
servers (which may require additional consent).
| #### 2.1.4 Mitigation | ||
|
|
||
| MCP proxy servers that use a static client ID for third-party services MUST require explicit | ||
| approval for each dynamically registered client before forwarding requests to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment as above about ambiguity of "explicit approval"
|
This is great. One thing that took me a bit to get my head around when this was first raised was that it's the "cookie present, consent skipped" that makes the attack different from the normal third-party proxy OAuth flow.
Not sure if it's possible to highlight this more explicitly for the user? Potentially breaking the diagram into two makes it easier to understand? DetailsNormal OAuth proxy usage (preserves user consent)sequenceDiagram
participant UA as User-Agent (Browser)
participant MC as MCP Client
participant M as MCP Proxy Server
participant TAS as Third-Party Authorization Server
Note over UA,M: Initial Auth flow completed
Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server
M->>UA: Redirect to third party authorization server
UA->>TAS: Authorization request (client_id: mcp-proxy)
TAS->>UA: Authorization consent screen
Note over UA: Review consent screen
UA->>TAS: Approve
TAS->>UA: Set consent cookie for client ID: mcp-proxy
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
UA->>M: 3P Authorization code
Note over M,TAS: Exchange 3P code for 3P token
Note over M: Generate MCP authorization code
M->>UA: Redirect to MCP Client with MCP authorization code
Note over M,UA: Exchange code for token, etc.
Malicious OAuth proxy usage (skips user consent)sequenceDiagram
participant UA as User-Agent (Browser)
participant A as Attacker
participant M as MCP Proxy Server
participant TAS as Third-Party Authorization Server
Note over UA,M: Step 2: Attack (leveraging existing cookie)
A->>M: Dynamically register malicious client, redirect_uri: attacker.com
A->>UA: Sends malicious link
UA->>TAS: Authorization request (client_id: mcp-proxy) + consent cookie
TAS->>TAS: Cookie present, consent skipped
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
UA->>M: 3P Authorization code
Note over M,TAS: Exchange 3P code for 3P token
Note over M: Generate MCP authorization code
M->>UA: Redirect to attacker.com with MCP Authorization code
UA->>A: MCP Authorization code delivered to attacker.com
Note over M,A: Attacker exchanges MCP code for MCP token
A->>M: Attacker impersonates user to MCP server
I'm sure someone who is better at mermaid diagrams could do a decent job of that. But otherwise, this is great. FWIW, I sketched out what an example consent dialog needs to look like: cloudflare/ai#99. Not sure if there's a better place to leave examples like that, or whether it's something that should be added to the SDKs? |
|
@geelen we're working on bootstrapping a separate section for security-related documentation that should better accomodate examples such as the dialog you're referring to. FWIW, I took inspiration from that and am building something similar for our (Microsoft) samples as well since the problem is the same. This almost makes me think that we need a more generic solution here (outside teh scope of the spec). |
| An attacker can exploit configurations where an MCP server acts as an intermediary between MCP clients and a protected third-party API, leading to the confused deputy problem. The attacker can acquire and exchange a stolen authorization code for access tokens for the MCP server without the user's consent. Consult [Security Best Practices section 2.1](/specification/draft/basic/security_best_practices) for a more detailed description. | ||
|
|
||
| MCP proxy servers that use a static client ID for third-party services MUST require explicit | ||
| approval for each dynamically registered client before forwarding requests to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would trim this down a bit more:
MCP proxy servers using static client IDs **MUST** obtain user consent for each
dynamically registered client before forwarding to third-party authorization
servers (which may require additional consent).
| **MCP Proxy Server** | ||
| : An MCP server that acts as an intermediary between MCP clients and a protected | ||
| third-party API. The MCP proxy server provides MCP functionality while delegating | ||
| API operations to a third-party API server. The MCP proxy server acts as a single OAuth client to the third-party API server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be trimmed down:
MCP proxy server connects MCP clients to third-party APIs,
offering MCP features while delegating operations and acting
as a single OAuth client to the third-party API server.
| API operations to a third-party API server. The MCP proxy server acts as a single OAuth client to the third-party API server. | ||
|
|
||
| **Third-Party Authorization Server** | ||
| : The authorization server that protects access to the third-party API. This server may not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also can be trimmed down a bit:
Authorization server that protects the third-party API.
It may lack dynamic client registration support, requiring MCP
proxy to use a static client ID for all requests.
|
|
||
| **Static Client ID** | ||
| : A fixed OAuth 2.0 client identifier used by the MCP proxy server when communicating with | ||
| the third-party authorization server, shared across all MCP clients. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure "shared across all MCP clients" is the right framing here. It's used by the server to request access tokens for the server (I don't think we're talking about them being passed upstream to the client)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah I see, yea I meant "shared" as in "used for all of them", not necessarily exposed to them, will edit
|
|
||
| 1. A user authenticates normally through the MCP proxy server to access the third-party API | ||
| 2. During this legitimate flow, the third-party authorization server sets a cookie on the user agent | ||
| indicating consent for the static client ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really going to be the same client ID re-used across all clients? We're not requiring the MCP server to do an in-server registration of clients?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the MCP server's client ID that it gets from e.g. the Github API (which doesn't support DCR). So it will be the MCP server is acting as a client to the Github API with a static client ID, meanwhile MCP clients have different client ID's per client.
Co-authored-by: Den Delimarsky 🌺 <[email protected]>
|
@geelen good call! I split it up and put a red rect over the cookie consent skipping to make it more obvious where the bad thing happens. ty @dend & @D-McAdams , ptal |

Motivation and Context
Breaking out of #387 to avoid merge conflicts. This adds a security consideration about the Confused Deputy problem mentioned in #265