fix: prevent MCP transport auth header from leaking to downstream OpenAPI APIs (#3260)#3262
Conversation
…nAPI APIs (PrefectHQ#3260) Two issues in OpenAPITool.run(): 1. get_http_headers() does not exclude 'authorization', so the MCP client's auth token is included in forwarded headers. 2. mcp_headers.update() overwrites existing request headers, including the Authorization header that was already set from the httpx client's configured API key. Fix: - Add 'authorization' to exclude_headers in get_http_headers() to prevent MCP transport credentials from being forwarded by default. - Change mcp_headers forwarding to use the same non-overwriting pattern as client headers (only set if key not already present), making the behavior consistent and preventing accidental overwrites. Fixes PrefectHQ#3260
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4bcfb389c4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| "expect", | ||
| "accept", | ||
| # Auth headers should not be forwarded to downstream APIs | ||
| "authorization", |
There was a problem hiding this comment.
Avoid stripping Authorization in shared header helper
Adding authorization to the default exclude_headers in get_http_headers() changes behavior for every caller of this utility, not just OpenAPI tools. Both proxy client transports (src/fastmcp/client/transports/http.py:98 and src/fastmcp/client/transports/sse.py:72) rely on get_http_headers() | self.headers to forward incoming MCP request headers upstream, so a FastMCP proxy in front of an authenticated upstream MCP server will now drop the caller’s bearer token and fail authorization. This is a regression for authenticated proxy deployments; the authorization exclusion should be limited to the OpenAPI forwarding path instead of the shared helper default.
Useful? React with 👍 / 👎.
The authorization exclusion is correct for the default case (OpenAPI tools should not forward MCP transport credentials), but proxy transports need auth headers forwarded to upstream MCP servers. The new `include` parameter lets callers opt specific headers back in despite the default exclusion set. Proxy transports now explicitly request authorization forwarding.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 69d483bc21
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "keep-alive", | ||
| "expect", | ||
| "accept", | ||
| "authorization", |
There was a problem hiding this comment.
Preserve Authorization in default header helper
Adding "authorization" to the default exclusion list makes get_http_headers() drop auth headers for every caller, not just the OpenAPI forwarding path. CurrentHeaders delegates directly to get_http_headers() (src/fastmcp/server/dependencies.py:1051), so tools/middleware that read headers.get("authorization") now silently lose bearer tokens and can fail custom auth logic in HTTP transports. This behavior change should be scoped to the OpenAPI forwarding use case or CurrentHeaders should explicitly opt back in with include={"authorization"}.
Useful? React with 👍 / 👎.
CurrentHeaders is user-facing — tools use it to inspect the caller's auth token for custom logic. Reading a header in your own code is safe; the exclusion is meant to prevent blindly forwarding it to third-party APIs.
|
@stakeswky thank you! making some tweaks to avoid breaking proxy forwarding |
Problem
When an MCP server is created with
FastMCP.from_openapi()and served over Streamable HTTP, theget_http_headers()call inOpenAPITool.run()forwards the incoming MCP transport'sAuthorizationheader to the downstream API, overwriting the API key configured on the httpx client.This means the downstream API receives the MCP client's auth token instead of the intended API key, causing auth failures (503/401/403).
This is a security issue — MCP transport credentials should never leak to downstream third-party APIs.
Root Cause
Two problems in
OpenAPITool.run()(components.py):get_http_headers()does not excludeauthorizationfrom forwarded headersrequest.headers.update(mcp_headers)overwrites existing headers, while the client headers block above usesif key not in request.headers(non-overwriting) — inconsistent behaviorFix
authorizationtoexclude_headersinget_http_headers()(dependencies.py) — MCP transport credentials should not be forwarded to downstream APIs by defaultImpact
Fixes #3260