Skip to content

OAuth client could send credentials to an authorization server chosen by the MCP server

High
maxisbey published GHSA-qx49-fqc8-xw99 Sep 28, 2026

Package

pip mcp (pip)

Affected versions

>= 2.0.0a1, < 2.2.0
>= 1.9.1, < 1.30.0

Patched versions

2.2.0
1.30.0

Description

Summary

In affected versions, the SDK's OAuth client support (mcp.client.auth) let the MCP server a client connected to decide where the client's OAuth credentials were sent:

  • the authorization server metadata issuer was not validated on every discovery path, and
  • stored or pre-provisioned client credentials were not bound to the authorization server they belong to.

A malicious or compromised MCP server could therefore direct them to a token endpoint of its choosing:

  • by naming its own authorization server in its protected resource metadata, or
  • by publishing none and serving metadata that presents the user's real authorization server as the issuer.

It would then receive the client_secret, authorization code and PKCE code_verifier meant for that real server (or, with PrivateKeyJWTOAuthProvider, a signed client assertion).

Am I affected?

Yes, if both of these hold:

  • your application uses the SDK as an MCP client over HTTP with OAuthClientProvider, ClientCredentialsOAuthProvider, PrivateKeyJWTOAuthProvider or the deprecated 1.x RFC7523OAuthClientProvider as the transport's auth handler;
  • it may connect to an MCP server you do not fully trust while holding credentials for a legitimate authorization server (a pre-provisioned secret or signing key, or a stored client registration).

Affected versions:

  • 1.9.1 through 1.29.1: no issuer check and no credential binding on any path.
  • 2.0.0 through 2.1.1: both missing whenever the server publishes no protected resource metadata (the legacy fallback) or answers 403 insufficient_scope.
  • Both lines, on every path: ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider had no way to name the authorization server their credentials belong to.

Not affected:

  • MCP servers built with the SDK
  • stdio clients
  • clients that attach their own tokens or headers

The rating is scored for the unattended providers. With the interactive OAuthClientProvider a person has to start the sign-in; scored that way it is 6.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N).

Fix

Upgrade to mcp 2.2.0 (2.x) or 1.30.0 (1.x) or later.

If you use ClientCredentialsOAuthProvider or PrivateKeyJWTOAuthProvider, upgrading changes nothing until you also pass issuer= (for example issuer="https://auth.example.com"):

  • Without it they still follow whichever authorization server the MCP server advertises.
  • Omitting it now emits a deprecation warning, and it becomes required in 3.0. On 1.30.0 it is a DeprecationWarning, which Python hides by default.
  • The deprecated 1.x RFC7523OAuthClientProvider has no issuer option. Move to one of these two providers.

In the fixed versions the client also:

  • works out which issuer it expects before fetching any authorization server metadata, on every path;
  • refuses metadata whose issuer differs, with OAuthFlowError;
  • binds stored registrations to that issuer, and discards one bound to a different issuer so the client registers again.

After upgrading:

  • Stored registrations: a registration stored without an issuer stays unbound. That includes everything 1.x stored before 1.30.0. Clear stored OAuth client information once so the client registers again. If you placed a pre-registered client in storage yourself, set issuer on that record instead.
  • Past exposure: if such a client may have connected to an untrusted MCP server, rotate its client secret and revoke its tokens at the authorization server.

On earlier versions there is no workaround other than connecting OAuth-enabled clients only to MCP servers you trust.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

CVE ID

No known CVE

Weaknesses

Insufficient Verification of Data Authenticity

The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data. Learn more on MITRE.

Insufficiently Protected Credentials

The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval. Learn more on MITRE.

Credits