All guides

DEVOMNITOOLS / Developer guides

Fix Model Context Protocol (MCP) Bearer Auth Flaw (CVE-2026-111771)

Diagnose and patch CVE-2026-111771 in the Model Context Protocol (MCP) TypeScript SDK. Learn why cross-service bearer tokens were accepted and verify claims locally.

DevOmniTools Research Published: Updated: 5 min read

Key takeaways

  • CVE-2026-111771 allowed MCP servers to accept bearer tokens intended for distinct services due to missing audience (aud) enforcement.
  • Verify RFC 7519 aud and iss claims against strict server allowlists rather than trusting signature validity alone.
  • Inspect bearer JWT headers, claims, and expiry in an offline browser sandbox before authorizing external agent connections.

Executive Summary & Threat Profile

On October 20, 2026, GitHub Advisory Database published CVE-2026-111771 (GHSA-rvq5-wwqv-78pq), identifying a critical authentication delegation vulnerability in the official Model Context Protocol (@modelcontextprotocol/sdk) TypeScript implementation. When running MCP servers over remote HTTP transports (such as Server-Sent Events), server bearer authentication accepted valid tokens issued for entirely different services within the same identity ecosystem.

An attacker holding a legitimate user token for a low-privilege internal dashboard could replay that token against a high-privilege MCP server, executing unauthorized tool calls and extracting sensitive prompt context.

Vulnerability Anatomy: Audience Confused Deputy

Under RFC 7519 (JSON Web Token), the aud (Audience) claim identifies the intended recipients of the token. Furthermore, RFC 6750 (OAuth 2.0 Bearer Tokens) dictates that resource servers MUST reject tokens not explicitly intended for their service.

In vulnerable versions of @modelcontextprotocol/sdk, the default HTTP transport verified:

  1. Token format and cryptographic signature integrity using the configured public key or JWKS endpoint.
  2. Token expiration timestamp (exp).

However, it omitted strict evaluation of the aud claim. If an organization used a single Identity Provider (such as Auth0, Keycloak, or Okta) across multiple internal microservices, any valid token issued for Service-A was erroneously accepted by MCP-Server-B.

{
  "iss": "https://auth.company.internal/",
  "sub": "user_dev_8841",
  "aud": "https://analytics-dashboard.company.internal",
  "exp": 1792576800,
  "scope": "read:analytics"
}

Vulnerable MCP servers checked the signature against auth.company.internal, ignored that aud was intended only for analytics-dashboard, and granted agent-level tool access.

Reproducing and Inspecting Tokens Locally

Before deploying security patches, developers should verify their token claims client-side to ensure compliance with intended scopes and target audiences.

  1. Open the DevOmniTools JWT & JWS Decoder.
  2. Paste the bearer token without the HTTP Bearer header prefix.
  3. Verify that:
    • The aud field is a strict match for your MCP server resource URI (e.g., https://mcp.service.internal/v1).
    • The iss points exclusively to your trusted authorization server.
    • The signature algorithm (alg) strictly uses asymmetric keys (RS256, ES256, or EdDSA).

The decoder executes 100% locally in your browser memory, guaranteeing that sensitive test tokens and cryptographic material are never transmitted across remote servers.

Code Walkthrough: Vulnerable vs Patched Implementation

1. Vulnerable MCP Server Configuration (Before)

In affected implementations, middleware passed a generic verification callback without specifying audience constraints:

// ❌ VULNERABLE: Lacks audience (aud) and issuer (iss) enforcement
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import jwt from "jsonwebtoken";

const server = new McpServer({ name: "InternalAgent", version: "1.0.0" });

app.get("/sse", (req, res) => {
  const authHeader = req.headers.authorization;
  const token = authHeader?.split(" ")[1];

  try {
    // Only verifies cryptographic signature; permits any token issued by JWKS
    const decoded = jwt.verify(token, process.env.IDP_PUBLIC_KEY!);
    req.user = decoded;
  } catch (err) {
    return res.status(401).send("Unauthorized");
  }

  const transport = new SSEServerTransport("/messages", res);
  server.connect(transport);
});

2. Hardened Production Configuration (After)

To mitigate CVE-2026-111771, upgrade @modelcontextprotocol/sdk to the latest release and enforce explicit audience and issuer validation options in your verification chain:

// ✅ HARDENED: Strict audience, issuer, and algorithm validation
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import jwt from "jsonwebtoken";

const MCP_RESOURCE_AUDIENCE = "https://mcp.company.internal/api/v1";
const TRUSTED_ISSUER = "https://auth.company.internal/";

app.get("/sse", (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Missing or malformed bearer token" });
  }

  const token = authHeader.substring(7);

  try {
    // Enforces RFC 7519 audience and issuer validation strictly
    const decoded = jwt.verify(token, process.env.IDP_PUBLIC_KEY!, {
      audience: MCP_RESOURCE_AUDIENCE,
      issuer: TRUSTED_ISSUER,
      algorithms: ["RS256", "ES256"],
    });

    req.user = decoded;
  } catch (err) {
    return res.status(403).json({ 
      error: "Forbidden: Token rejected due to invalid audience or expired credentials" 
    });
  }

  const transport = new SSEServerTransport("/messages", res);
  server.connect(transport);
});

Security Comparison Across MCP Transport Layers

Transport LayerExposed ProtocolVulnerable to CVE-2026-111771?Required Mitigation
stdioLocal IPC / UNIX Pipes❌ NoProcess isolation; local CLI arguments only.
SSE (HTTP)Server-Sent Events⚠️ Yes (High)Enforce audience parameter in JWT verify hook.
WebSocketsWSS Handshake⚠️ Yes (Medium)Validate token headers during initial upgrade handshake.
Custom GatewayReverse Proxy / Envoy❌ No (If proxy validates)Validate JWT at Envoy/Ingress using jwt_authn filter with exact audiences.

Audit Checklist for Production Systems

Before deploying MCP agents in automated CI/CD environments, run through this five-point audit:

  1. Package Verification: Run npm list @modelcontextprotocol/sdk to confirm your version includes the security patch.
  2. Explicit Audience String: Never omit the audience verification property when calling verification helpers (jose.jwtVerify() or jwt.verify()).
  3. Entropy Test: For testing authentication handshakes, use DevOmniTools Password & Secret Entropy Tester to ensure shared signing secrets exceed 256 bits of cryptographic entropy.
  4. Scope Authorization: Once audience is confirmed, verify that the scope array contains explicit permissions for the requested tool actions (e.g., tools:execute).
  5. No Telemetry Leakage: Ensure debugging middleware does not print raw bearer authorization tokens to application logs or stdout.
  1. Client requests token
  2. IDP issues JWT with specific aud
  3. MCP server checks signature and aud match
  4. Authorized tool execution
Strict MCP Token Validation Workflow: Unmatched audiences are rejected prior to transport connection.

FAQ

What is the primary root cause of CVE-2026-111771 in MCP servers?

The MCP TypeScript SDK HTTP transport accepted valid cryptographic signatures from a shared Identity Provider without strictly enforcing that the JWT audience (aud) claim specifically matched the target MCP server URI.

How can developers verify whether a token contains the correct audience?

Developers can decode the raw JWT payload client-side using an offline tool like DevOmniTools JWT Decoder to inspect the aud, iss, and scope claims without transmitting credentials across the network.

Are stdio-based MCP servers affected by this vulnerability?

No. Local stdio-based MCP servers communicate over standard process input/output and do not use HTTP bearer authentication middleware. Only HTTP and SSE transports are exposed to this vulnerability.