Skip to content

fix(security): add API key authentication to MCP HTTP server - #1932

Open
PiedPiper911 wants to merge 1 commit into
bytedance:mainfrom
PiedPiper911:fix/mcp-http-server-auth-1931
Open

fix(security): add API key authentication to MCP HTTP server#1932
PiedPiper911 wants to merge 1 commit into
bytedance:mainfrom
PiedPiper911:fix/mcp-http-server-auth-1931

Conversation

@PiedPiper911

Copy link
Copy Markdown

Summary

This PR adds built-in API key (Bearer token) authentication to the mcp-http-server package, addressing the unauthenticated-access vulnerability reported in #1931.

Previously, the Express server exposed every MCP endpoint (SSE, message, streamable HTTP) without any built-in authentication. When deployed with host: '0.0.0.0' (common for containerized deployments), any network client could access all registered MCP tools — browser automation, file operations, etc. — without credentials.

Fixes #1931

Security Impact

  • Before: Any client on the network could call initialize and gain full access to all MCP tools when the server was bound to a non-localhost address.
  • After: When an API key is configured, every request must present a matching Authorization: Bearer <token> header. Requests without a valid token are rejected with 401 Unauthorized before reaching any MCP endpoint.
  • The token comparison uses crypto.timingSafeEqual to avoid timing side-channel attacks.
  • A WWW-Authenticate: Bearer header is returned on 401 responses, following RFC 6750.

Implementation Details

  • Added an optional apiKey parameter to startSseAndStreamableHttpMcpServer.
  • The API key is resolved from the apiKey param first, then from the MCP_API_KEY environment variable.
  • Added createApiKeyAuthMiddleware, an Express middleware registered before custom middlewares and all MCP routes. It:
    • Rejects requests with a missing/malformed Authorization header → 401 with a JSON-RPC error body.
    • Rejects requests with an invalid token → 401 with a JSON-RPC error body.
    • Calls next() only when the token matches (constant-time comparison).
  • Added clear inline comments documenting the security behavior.

Backward Compatibility

This change is fully backward compatible:

  • If no API key is configured (neither apiKey param nor MCP_API_KEY env var), the server keeps its previous unauthenticated behavior — no requests are blocked.
  • In that case, a clear security warning is logged at startup, plus an additional warning when binding to a non-localhost address (e.g. host: '0.0.0.0'), so operators are aware of the exposure.
  • Existing callers and the public API surface are unchanged; apiKey is purely additive.

Files Changed

  • packages/agent-infra/mcp-http-server/src/startServer.ts — add apiKey option, Bearer token auth middleware, timing-safe comparison, and security warnings.
  • packages/agent-infra/mcp-http-server/tests/startServer-auth.test.ts — new tests covering: rejection without a token (401), rejection with an invalid token (401), acceptance with a valid token, SSE endpoint protection, and backward-compatible behavior when no key is set.

Usage

Set the MCP_API_KEY environment variable before starting the server:

# Linux / macOS
export MCP_API_KEY="your-strong-random-secret"

# Windows (PowerShell)
$env:MCP_API_KEY = "your-strong-random-secret"

Or pass it programmatically:

import { startSseAndStreamableHttpMcpServer } from 'mcp-http-server';

await startSseAndStreamableHttpMcpServer({
  host: '0.0.0.0',
  port: 8080,
  apiKey: process.env.MCP_API_KEY, // or rely on the env var directly
  createMcpServer: async (req) => {
    // ... create and return your McpServer
  },
});

Clients must then include the token on every request:

curl -X POST http://your-host:8080/mcp \
  -H "Authorization: Bearer your-strong-random-secret" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'

Requests without a valid token receive:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32600,
    "message": "Unauthorized: missing Authorization header. Expected \"Authorization: Bearer <MCP_API_KEY>\"."
  },
  "id": null
}

Happy to adjust the approach (e.g. make auth mandatory when binding to non-localhost, or add HTTPS/TLS support) based on maintainer preference. Thanks for reviewing!

Add built-in Bearer token authentication to the mcp-http-server Express
server to address unauthenticated access to all MCP tools (bytedance#1931).

- Support API key via `apiKey` param or `MCP_API_KEY` env var
- Reject requests without a valid `Authorization: Bearer <token>` (401)
- Use timing-safe comparison to avoid timing side-channels
- Backward compatible: when no key is configured, the server keeps its
  previous behavior but logs a clear security warning (extra warning when
  binding to a non-localhost address)
- Add tests covering auth enforcement and backward compatibility
@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for agent-tars-docs canceled.

Name Link
🔨 Latest commit 5f2f407
🔍 Latest deploy log https://app.netlify.com/projects/agent-tars-docs/deploys/6a5da7f4d106ee0008589138

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for tarko canceled.

Name Link
🔨 Latest commit 5f2f407
🔍 Latest deploy log https://app.netlify.com/projects/tarko/deploys/6a5da7f4b860f6000727ca36

@CLAassistant

CLAassistant commented Jul 22, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@PiedPiper911

Copy link
Copy Markdown
Author

Friendly ping - this PR adds API key auth to the MCP HTTP server endpoint (currently open without authentication). Happy to adjust the approach if the team prefers a different solution. Thanks for your time!

@PiedPiper911

Copy link
Copy Markdown
Author

Hi! Friendly follow-up on this PR — it has been open for over a week. Happy to make any adjustments needed to get this merged. Let me know if there are any concerns with the approach. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants