Oct 28, 2026·9 min read·5 visits
The Model Context Protocol (MCP) Python SDK fails to validate the target audience or RFC 8707 resource indicator of incoming OAuth 2.0 bearer tokens in `BearerAuthBackend`. Attackers holding valid tokens issued by a shared Identity Provider for unrelated applications can replay those tokens against an MCP server to achieve unauthorized access and execute privileged tools. Upgrading to versions 1.30.0 or 2.2.0 and explicitly enabling `validate_token_resource=True` in `AuthSettings` is required to remediate the flaw.
CVE-2026-116684 is an incorrect authorization vulnerability (CWE-863) in the Model Context Protocol (MCP) Python SDK (`modelcontextprotocol/python-sdk`). The server-side bearer authentication middleware (`BearerAuthBackend`) verifies token cryptographic signatures and expiration timestamps via `TokenVerifier`, but fails to verify that the token's audience (`aud` claim or RFC 8707 resource indicator) matches the receiving MCP server's configured `resource_server_url`. In multi-service environments sharing a single OAuth 2.0 Authorization Server, an attacker possessing a valid token for an unrelated service can replay it against the MCP server to gain unauthorized access to protected tools, resources, and prompts.
The Model Context Protocol (MCP) Python SDK (mcp) provides the official server and client implementation for building integrations that expose data, tools, and prompt templates to Large Language Model (LLM) applications. When deployed over HTTP transports (such as Server-Sent Events or Streamable HTTP), MCP servers rely on OAuth 2.0 bearer token authentication to restrict access to sensitive capabilities. This authentication layer is managed by the BearerAuthBackend middleware located in src/mcp/server/auth/middleware/bearer_auth.py.
In versions >= 1.10.0, < 1.30.0 and >= 2.0.0, < 2.2.0, BearerAuthBackend suffers from an incorrect authorization vulnerability classified under CWE-863 (Incorrect Authorization). When an MCP server is configured with AuthSettings and a custom or standard TokenVerifier, the middleware validates that the incoming bearer token is cryptographically authentic and un-expired, but fails to verify whether the token was actually issued for the receiving MCP server.
In enterprise architectures, a single OAuth 2.0 Authorization Server or Identity Provider (IdP) routinely issues access tokens for multiple distinct resource servers (APIs, microservices, and MCP servers). Under RFC 7519 (JWT) and RFC 8707 (Resource Indicators for OAuth 2.0), tokens are bound to their intended recipient via the aud (Audience) claim or the resource indicator. Because the MCP SDK omitted checks comparing AccessToken.resource against the server's configured resource_server_url, any valid token issued by the trusted IdP for an unrelated service is accepted by the MCP server, allowing cross-service token replay and unauthorized privilege escalation.
The root cause of CVE-2026-116684 lies in an incomplete authentication state check within BearerAuthBackend.authenticate() and a disconnection between AuthSettings.resource_server_url and the authentication middleware initialization. When a developer configures an MCP server, they supply an AuthSettings object containing issuer_url (the OAuth 2.0 Authorization Server) and resource_server_url (the canonical URL of the MCP server itself).
During request processing, BearerAuthBackend.authenticate() extracts the token from the Authorization: Bearer <token> HTTP header and passes the raw token string to self.token_verifier.verify_token(token). The TokenVerifier returns an AccessToken Pydantic model (defined in mcp.server.auth.provider), which contains token, client_id, scopes, expires_at, and an optional resource attribute representing the RFC 8707 resource indicator or audience.
Prior to the patch, BearerAuthBackend was instantiated solely with self._token_verifier and had no reference to self.settings.auth.resource_server_url. After calling verify_token(), the middleware checked only two conditions: whether the returned auth_info object was None, and whether auth_info.expires_at was less than the current Unix timestamp. It completely ignored auth_info.resource.
Consequently, if the developer's TokenVerifier implementation only verified the cryptographic signature against the IdP's JSON Web Key Set (JWKS) and decoded the claims into AccessToken—expecting the framework to enforce server-level settings—any token bearing a valid signature from the IdP and matching scopes was granted full access to the MCP session.
The vulnerability was resolved across the v2.x branch in commit 0c9136841ff092987a20700ff9c2c9789bda2f40 (PR #3447) and backported to the v1.x maintenance branch in commit 8c2fa6eabacde869d75568e33b9d75568e33b9d466b58c2f512 (PR #3451). The patch modifies three primary components: the authentication settings model, the server middleware wiring, and the bearer authentication backend.
First, src/mcp/server/auth/middleware/bearer_auth.py was updated to accept resource_server_url in its constructor and enforce URL-normalized comparison against auth_info.resource during request authentication:
class BearerAuthBackend(AuthenticationBackend):
def __init__(self, token_verifier: TokenVerifier, *, resource_server_url: AnyHttpUrl | None = None):
self.token_verifier = token_verifier
# Store the target MCP server's canonical resource URL
self.resource_server_url = resource_server_url
async def authenticate(self, conn: HTTPConnection):
auth_header = next(
(val for key, val in conn.headers.raw if key.lower() == b"authorization"),
None,
)
if not auth_header:
return None
parts = auth_header.decode("latin-1").split(" ", 1)
if len(parts) != 2 or parts[0].lower() != "bearer":
return None
token = parts[1]
auth_info = await self.token_verifier.verify_token(token)
if auth_info is None:
return None
if auth_info.expires_at and auth_info.expires_at < int(time.time()):
return None
# PATCHED: Verify that the token's resource indicator matches this server
if self.resource_server_url and not self._issued_for_this_resource(auth_info.resource):
logger.warning(
"Bearer token resource %r is not resource_server_url %s",
auth_info.resource,
self.resource_server_url,
)
return None
return AuthCredentials(auth_info.scopes), AuthenticatedUser(auth_info)
def _issued_for_this_resource(self, resource: str | None) -> bool:
"""Compare as URLs (so case and default-port spelling do not matter), a trailing slash aside."""
try:
token_resource = str(AnyHttpUrl(resource or ""))
except ValidationError:
return False
return token_resource.removesuffix("/") == str(self.resource_server_url).removesuffix("/")Second, src/mcp/server/auth/settings.py introduced the validate_token_resource boolean field on AuthSettings, along with a Pydantic @model_validator that checks configuration consistency and issues a MCPDeprecationWarning if resource_server_url is configured without an explicit validate_token_resource setting:
class AuthSettings(BaseModel):
issuer_url: AnyHttpUrl
resource_server_url: AnyHttpUrl | None = Field(default=None)
validate_token_resource: bool | None = Field(default=None)
@model_validator(mode="after")
def _check_validate_token_resource(self) -> Self:
if self.validate_token_resource and self.resource_server_url is None:
raise ValueError("validate_token_resource requires resource_server_url")
if self.validate_token_resource is None and self.resource_server_url is not None:
warnings.warn(
"`AuthSettings.validate_token_resource` is not set, so bearer tokens are not checked "
"against `resource_server_url`; it will default to True in 3.0 when `resource_server_url` is "
"set. Set it to True to have the server refuse tokens issued for another resource, or to "
"False if your TokenVerifier validates the token's audience itself.",
MCPDeprecationWarning,
stacklevel=3,
)
return selfThird, in src/mcp/server/mcpserver/server.py (and lowlevel/server.py), the AuthenticationMiddleware initialization was updated to conditionally pass resource_server_url to BearerAuthBackend when self.settings.auth.validate_token_resource evaluates to True.
> [!WARNING]
> Fix Completeness & Residual Risk: To preserve backwards compatibility in minor version releases (1.30.0 and 2.2.0), validate_token_resource defaults to None (which behaves as False). Merely upgrading the mcp package without modifying server initialization code leaves the deployment vulnerable to token replay until MCP SDK 3.0 makes True the default.
Exploiting CVE-2026-116684 requires a specific architectural prerequisite: the target MCP server must rely on an OAuth 2.0 Authorization Server / IdP that is shared with at least one other application or resource server, and the attacker must be able to obtain a valid access token for that secondary service.
Consider an enterprise environment where an internal Identity Provider (https://idp.corp.example.com) manages authentication for multiple internal services. Service A is a low-privilege employee profile service (https://profile.corp.example.com), and Service B is a privileged MCP server (https://mcp-admin.corp.example.com/mcp) that exposes database query and infrastructure management tools to AI agents. Both services validate tokens using the IdP's public JWKS endpoint.
profile service) initiates a standard OAuth 2.0 flow against https://idp.corp.example.com requesting an access token for resource=https://profile.corp.example.com. If the IdP permits generic scopes (or if the MCP server does not enforce unique custom scopes via required_scopes), the IdP returns a signed JWT containing:{
"iss": "https://idp.corp.example.com",
"sub": "user-9481",
"aud": "https://profile.corp.example.com",
"client_id": "profile-web-app",
"exp": 1793088000,
"scope": "read write"
}https://mcp-admin.corp.example.com/mcp), supplying the token minted for the profile service in the HTTP Authorization header:POST /mcp HTTP/1.1
Host: mcp-admin.corp.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "execute_sql_query",
"arguments": {
"query": "SELECT * FROM sensitive_credentials;"
}
}
}TokenVerifier verifies the RSA signature against https://idp.corp.example.com/.well-known/jwks.json and populates AccessToken(resource="https://profile.corp.example.com", ...). Because BearerAuthBackend in vulnerable versions fails to compare AccessToken.resource against https://mcp-admin.corp.example.com/mcp, the middleware authenticates the request and executes the privileged execute_sql_query tool.CVE-2026-116684 is scored at CVSS v3.1 6.8 (Medium) with the vector CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N. The Attack Complexity is rated High (AC:H) and Privileges Required is Low (PR:L) because successful exploitation depends on a multi-resource IdP deployment, the attacker possessing credentials to obtain a valid token for a sibling service, and the MCP server's custom TokenVerifier not already performing independent aud claim verification.
When these conditions are met, the Confidentiality (C:H) and Integrity (I:H) impacts are high. Model Context Protocol servers are specifically designed to act as bridges between LLM clients and sensitive backend infrastructure, frequently exposing tools capable of reading internal filesystems, querying production databases, interacting with source control, or invoking cloud provider APIs.
By bypassing resource-level authorization, an attacker who should only have access to a low-sensitivity application within the same identity domain can laterally move into the MCP server context. The attacker can enumerate all registered MCP resources (resources/list, resources/read), extract proprietary prompt templates (prompts/get), and invoke state-modifying tools (tools/call) with the full privileges of the MCP server process.
Remediating CVE-2026-116684 requires a three-step process: upgrading the mcp Python package, explicitly opting into resource validation in AuthSettings, and ensuring the TokenVerifier implementation populates the AccessToken.resource field.
First, upgrade the mcp package to version 1.30.0 (for v1.x deployments) or 2.2.0 (for v2.x deployments). Second, configure validate_token_resource=True alongside resource_server_url in AuthSettings. Third, ensure that the TokenVerifier.verify_token() implementation extracts the aud claim or RFC 8707 resource indicator from the verified token and passes it to AccessToken(resource=...).
from pydantic import AnyHttpUrl
from mcp.server.mcpserver import MCPServer
from mcp.server.auth.settings import AuthSettings
from mcp.server.auth.provider import AccessToken, TokenVerifier
RESOURCE_URL = "https://mcp.example.com/mcp"
class HardenedJWTVerifier(TokenVerifier):
def __init__(self, resource_url: str):
self.resource_url = resource_url
async def verify_token(self, token: str) -> AccessToken | None:
claims = decode_and_verify_jwt_signature(token)
if not claims:
return None
# Handle single string or list of strings in JWT 'aud' claim
aud = claims.get("aud")
audiences = aud if isinstance(aud, list) else [aud] if aud else []
own = self.resource_url.rstrip("/")
# Select matching audience or fallback to first entry so middleware rejects mismatches
matching_resource = next(
(a for a in audiences if a.rstrip("/") == own),
audiences[0] if audiences else None,
)
return AccessToken(
token=token,
client_id=claims.get("client_id", "unknown"),
scopes=claims.get("scope", "").split(),
expires_at=claims.get("exp"),
resource=matching_resource, # CRITICAL: Populate resource for middleware check
)
mcp = MCPServer(
"ProductionMCPServer",
token_verifier=HardenedJWTVerifier(resource_url=RESOURCE_URL),
auth=AuthSettings(
issuer_url=AnyHttpUrl("https://auth.example.com"),
resource_server_url=AnyHttpUrl(RESOURCE_URL),
required_scopes=["mcp:tools"],
validate_token_resource=True, # CRITICAL: Enforce audience validation in BearerAuthBackend
),
)If an immediate upgrade to 1.30.0 or 2.2.0 is not feasible, developers can mitigate the vulnerability directly inside their custom TokenVerifier.verify_token() method. By passing audience=RESOURCE_URL directly into the underlying JWT decoding library (such as PyJWT's jwt.decode(..., audience=...) or Authlib) and returning None if the audience does not match the MCP server's canonical URL, unauthenticated cross-service tokens will be rejected before reaching BearerAuthBackend.
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Model Context Protocol Python SDK (mcp) modelcontextprotocol | >= 1.10.0, < 1.30.0 | 1.30.0 |
Model Context Protocol Python SDK (mcp) modelcontextprotocol | >= 2.0.0, < 2.2.0 | 2.2.0 |
| Attribute | Detail |
|---|---|
| Vulnerability Class | CWE-863: Incorrect Authorization (OAuth 2.0 Audience / Resource Confusion) |
| Affected Component | mcp.server.auth.middleware.bearer_auth.BearerAuthBackend |
| Attack Vector | Network (Cross-Service OAuth 2.0 Bearer Token Replay) |
| CVSS v3.1 Score | 6.8 Medium (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N) |
| Impact | Unauthorized access to MCP tools, resources, and prompts |
| Patched Versions | 1.30.0 (v1.x) and 2.2.0 (v2.x) with validate_token_resource=True |
| Exploit Status | No known public exploits in the wild |
| CISA KEV Status | Not Listed |
The software performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly verify that the actor is authorized to access the specific resource requested.
CVE-2026-116686 is a Server-Side Request Forgery (SSRF) and Denial of Service (DoS) vulnerability in the official Model Context Protocol (MCP) Python SDK (mcp package on PyPI). When an MCP client validates structured tool execution results against a server-provided outputSchema, the SDK invokes the Python jsonschema library with its default URI resolution configuration. An untrusted or compromised MCP server can supply an outputSchema containing external $ref directives (using http://, https://, or file:// schemes), forcing the client process to synchronously fetch internal network resources, read local files, or block the main event loop indefinitely.
CVE-2026-116685 (GHSA-5h93-6whr-6q8j) is a sensitive information disclosure vulnerability in the official Model Context Protocol (MCP) Python SDK (`mcp` on PyPI). In versions 1.8.0 through 1.29.1 and 2.0.0 through 2.1.1, the SDK's HTTP-based client transports—including Streamable HTTP, Server-Sent Events (SSE), and OAuth client flows—unconditionally enable automatic HTTP redirect following (`follow_redirects=True`) across origin boundaries. When a configured endpoint returns a redirect to an external origin, the HTTP client forwards caller-supplied custom authentication headers (such as `X-API-Key`) and, in the case of HTTP 307 or 308 responses, replays the full POST request body containing JSON-RPC payloads or OAuth parameters to the untrusted destination.
CVE-2026-116694 (GHSA-f5m9-jfrj-jjj7) is an unbounded resource allocation vulnerability (CWE-770) in the optional Jaeger propagator component (opentelemetry-propagator-jaeger) of the OpenTelemetry Python SDK prior to version 1.45.0. When extracting distributed tracing context from inbound carriers, JaegerPropagator.extract() processes all headers prefixed with 'uberctx-' without enforcing limits on entry count, per-entry byte size, or aggregate byte size. In environments where transport-level header constraints are absent, relaxed, or bypassed (such as non-HTTP message brokers, RPC frameworks, or web servers with elevated buffer configurations), an unauthenticated remote attacker can supply high-volume or oversized baggage headers to trigger excessive URL unquoting, short-lived immutable dictionary allocations, and CPU starvation.
CVE-2026-116675 is a stored Cross-Site Scripting (XSS) vulnerability in the TinyMCE rich text editor affecting versions 7.9.3 and 8.5.1 through 8.9.2. The flaw resides in the media plugin's serialization logic, where placeholder elements configured with the data-mce-object attribute are converted into real DOM elements using the active window's document context prior to sanitization. When an attacker supplies a media element with an invalid source attribute alongside an inline event handler attribute such as data-mce-p-onerror, the browser resolves the resource and executes the malicious JavaScript before the sanitizer can strip the dangerous attributes.
CVE-2026-116693 is a high-severity Regular Expression Denial of Service (ReDoS) vulnerability affecting the official Datadog Application Performance Monitoring (APM) client library for Node.js, dd-trace. The defect resides in the default regular expression defined by the DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP configuration parameter, which is used to sanitize incoming and outgoing HTTP URL query strings prior to recording trace span metadata. Due to overlapping sub-patterns and unanchored repetition blocks in the JSON Web Token (JWT) and URL-encoded string matching branches, the V8 JavaScript engine's regular expression subsystem (Irregexp) can be forced into catastrophic or high-degree polynomial backtracking. Because regular expression replacement occurs synchronously on the primary Node.js event loop, an unauthenticated remote attacker can send crafted HTTP requests with malicious query strings to consume 100% of the process CPU core, stalling concurrent request processing and rendering the application unavailable.
CVE-2026-116692 (GHSA-26p8-2jq9-3vq9) is a remote code execution vulnerability in MLflow affecting versions 3.12.0rc0 prior to 3.17.0. The flaw stems from an overly permissive module prefix check during the deserialization of third-party GenAI evaluation scorers in mlflow/genai/scorers/base.py. By setting an experiment's artifact root to a local package directory within the Python module search path and uploading a crafted Python file as a run artifact, an attacker can invoke the /ajax-api/3.0/mlflow/scorer/invoke endpoint to dynamically import and execute arbitrary code on the MLflow tracking server.