flawopen.com/AI Security/mcp-python-sdk-oauth-issuer-confusion
MCP Python SDK OAuth Issuer-Validation Flaw
Certain Python SDK HTTP OAuth clients could misroute credentials. Upgrade to mcp 1.30.0 or 2.2.0; machine clients must set issuer=.[1], [3], [4]
A courier should send a sealed key only to the bank named on a directory card the company already trusts. Here, the MCP server could influence the card, and some client paths did not compare its issuer with the expected bank before sending OAuth material. The fix checks the issuer first and binds stored credentials to it.[1], [2]
Core Concepts & Subsystem Terms
MCP HTTP OAuth client- A Python application that connects to an MCP server over HTTP and lets the SDK manage OAuth. This advisory does not affect SDK-built MCP servers, stdio clients, or clients that attach their own tokens or headers.[1]
Authorization-server issuer- The stable identifier for the OAuth service that issues tokens. A client must compare discovered metadata with an issuer it already expects, not accept the value supplied by the server it is connecting to.[1], [2]
Protected Resource Metadata (PRM)- Metadata that can advertise authorization servers for a protected resource. The Python SDK also supports a legacy fallback when PRM is absent.[1], [2]
Framework mappings
Step-by-Step Attack Flow
Check whether this client is in scope
The affected setup is a Python mcp HTTP client using an SDK OAuth provider while it holds a stored registration, client secret, signing key, or other provider credential and may connect to an MCP server it does not fully trust. SDK-built servers, stdio clients, and clients attaching their own tokens or headers are not affected.[1]
Match the exact release line and discovery path
mcp 1.9.1–1.29.1 lacked issuer validation and credential binding on every discovery path. mcp 2.0.0–2.1.1 was affected when PRM was absent and the legacy fallback ran, or during 403 insufficient_scope step-up discovery. In both release lines, the machine-to-machine providers had no issuer= setting for their pre-provisioned credentials. The advisory package range starts at prerelease 2.0.0a1 and is <2.2.0, so affected 2.0.0 prereleases are also in scope.[1], [3], [4]
The connected server influences issuer discovery
A malicious or compromised MCP server can advertise an authorization server in PRM, or omit PRM and supply metadata on a legacy path. Before the fix, affected paths did not consistently compare that metadata's issuer with an independently expected issuer, and stored or pre-provisioned credentials were not consistently bound to their issuer.[1], [2]
OAuth material can reach the selected token endpoint
Depending on provider and flow, the client could send a client secret, an authorization code and PKCE verifier, or a signed client assertion to the selected endpoint. The advisory scores unattended providers CVSS 3.1 7.5; its interactive provider is scored 6.5 because a person must start sign-in. The advisory says no CVE has been assigned.[1]
Upgrade, pin, and re-register
Upgrade 1.x to mcp 1.30.0 or later, or 2.x to 2.2.0 or later. Set issuer= for ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider; migrate the deprecated 1.x RFC7523OAuthClientProvider. Clear old registrations that lack issuer binding and register again.[1], [2], [3], [4]
MCP Python SDK OAuth exposure and action matrix
| Client configuration | Documented affected condition | Required action |
|---|---|---|
HTTP OAuth client using mcp 1.9.1–1.29.1 | All discovery paths lacked issuer validation and consistent credential-to-issuer binding. | Upgrade to mcp 1.30.0 or later; verify the version actually deployed. |
HTTP OAuth client using mcp 2.0.0–2.1.1 stable releases | Affected on the no-PRM legacy fallback and 403 insufficient_scope step-up discovery paths. The advisory package range starts at prerelease 2.0.0a1 and is <2.2.0, so affected 2.0.0 prereleases are also in scope. | Upgrade to mcp 2.2.0 or later; verify the version actually deployed. |
ClientCredentialsOAuthProvider or PrivateKeyJWTOAuthProvider with pre-provisioned credentials | Affected release lines lacked an issuer= setting to bind these credentials to their expected authorization server. | After upgrading, set issuer= explicitly. Migrate the deprecated 1.x RFC7523OAuthClientProvider. |
| SDK-built server, stdio client, or client-supplied token/header | Not affected by this advisory. | No action for this specific flaw; reassess if the application also uses an affected HTTP OAuth client. |
Root Cause Analysis
The trust failure had two linked parts: affected discovery paths did not consistently compare the authorization server's metadata issuer with an issuer expected independently of the MCP server, and stored or pre-provisioned credentials were not bound to their intended issuer. A malicious or compromised MCP server could therefore steer a token exchange toward an endpoint it controlled and receive OAuth material held by the client. The fixes derive the expected issuer before discovery, reject mismatches, and bind stored registrations to that issuer; unattended machine-to-machine providers also require an explicit issuer=.[1], [2]
Source Code: Flaw vs. Secure Implementation
This source-grounded configuration comparison shows the new issuer parameter on the fixed machine-to-machine provider. It is available in mcp 1.30.0 and 2.2.0, not in the affected versions, so upgrade before using this setting. The fixed SDK derives an expected issuer before metadata discovery, rejects mismatches, and binds stored registrations to that issuer. Clear registrations created without issuer binding and re-register; if an affected client may have connected to an untrusted MCP server, rotate its client secret and revoke its tokens.[2], [3], [4]
Source excerpt: mcp.client.auth — machine-to-machine provider configuration
import os
from mcp.client.auth import ClientCredentialsOAuthProvider
auth = ClientCredentialsOAuthProvider(
client_id=os.environ["MCP_CLIENT_ID"],
client_secret=os.environ["MCP_CLIENT_SECRET"],
)
import os
from mcp.client.auth import ClientCredentialsOAuthProvider
auth = ClientCredentialsOAuthProvider(
client_id=os.environ["MCP_CLIENT_ID"],
client_secret=os.environ["MCP_CLIENT_SECRET"],
issuer=os.environ["MCP_AUTH_ISSUER"],
)
# Use mcp >= 1.30.0 (1.x) or >= 2.2.0 (2.x).
Engineering & System Hardening Checklist
- ✓Inventory Python MCP HTTP clients and resolve the installed
mcpversion, including transitive dependencies, containers, and deployed lockfiles.[1] - ✓Upgrade affected
mcp1.x clients to 1.30.0 or later and 2.x clients to 2.2.0 or later; do not infer the SDK version from the MCP server or host application version.[1], [3], [4] - ✓Pass a fixed, deployment-controlled
issuer=toClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProvider; migrate off the deprecated 1.xRFC7523OAuthClientProvider.[1], [2] - ✓Clear stored OAuth registrations created before issuer binding, then register again. If a pre-registered client record is managed externally, bind its issuer explicitly.[1], [2]
- ✓FlawOpen implementation guidance: Treat untrusted MCP servers as trust boundaries that may receive credentials. Do not connect them to clients holding production OAuth secrets or signing keys unless policy explicitly authorizes it.[1]
- ✓If an affected client may have connected to an untrusted MCP server, rotate its client secret and revoke tokens at the authorization server.[1]
- ✓Add regression coverage for 1.x and 2.x discovery paths, mismatched issuers, missing PRM,
403 insufficient_scope, issuer-bound registrations, and machine providers withoutissuer=.[2] - ✓Review this advisory's scope before declaring other clients affected: SDK-built servers, stdio clients, and clients attaching their own tokens or headers are listed as not affected.[1]
Sources
- Model Context Protocol Python SDK advisory GHSA-qx49-fqc8-xw99
- MCP Python SDK pull request #3398: issuer validation and credential binding
- MCP Python SDK v1.30.0 release
- MCP Python SDK v2.2.0 release
- MITRE CWE-345: Insufficient Verification of Data Authenticity
- MITRE CWE-522: Insufficiently Protected Credentials
- The Hacker News: Official MCP Python SDK flaw can let malicious servers steal OAuth credentials
- Cycode coverage of the MCP Python SDK OAuth issue