-
Notifications
You must be signed in to change notification settings - Fork 414
Description
Issue: X-Upstream-Authorization Header Not Working When Auth Type is None
Problem Statement
The current implementation of the x-upstream-authorization header does not address the core use case where the MCP Gateway is configured with auth_type = "none" but the client needs to pass an Authorization header to the upstream MCP server.
Current Behavior
Based on the code in mcpgateway/utils/passthrough_headers.py (lines 213-224), the x-upstream-authorization header is only processed when the gateway has authentication enabled (auth_type is "basic", "bearer", or "oauth"):
if gateway and gateway.auth_type in ["basic", "bearer", "oauth"]:
# Process x-upstream-authorization headerThis means when auth_type = "none", the special handling for x-upstream-authorization is completely skipped.
Use Case Requirements
Workflow:
- Configure MCP Gateway with
auth_type = "none"(no gateway-level authentication) - Client/agent performs On-Behalf-Of (OBO) authentication with our IDP
- Client obtains an auth token from IDP
- Client wants to pass this token to the upstream MCP server
- Client sends request with:
Authorization: Bearer <gateway_token>(if gateway had auth)x-upstream-authorization: Bearer <mcp_server_token>(for upstream)
- Gateway should forward
Authorization: Bearer <mcp_server_token>to upstream
Root Cause
The logic in get_passthrough_headers() function has a fundamental flaw:
- It only processes
x-upstream-authorizationwhen gateway auth is enabled - When
auth_type = "none", the client's Authorization header should be passed through directly - But the current code skips the
x-upstream-authorizationprocessing entirely
Proposed Solution
Modify the logic in mcpgateway/utils/passthrough_headers.py to handle two scenarios:
Scenario 1: Gateway has auth (current implementation - mostly correct)
- Gateway uses its own auth (basic/bearer/oauth)
- Client can use
x-upstream-authorizationto pass different auth to upstream - This gets renamed to
Authorizationfor upstream
Scenario 2: Gateway has no auth (missing implementation)
When auth_type = "none":
- Check for
x-upstream-authorizationheader - If present, use it as
Authorizationfor upstream (override any client Authorization header) - If not present, pass through client's
Authorizationheader directly (if allowed in passthrough headers)
Suggested Code Change
Replace lines 210-225 in passthrough_headers.py with:
# Special handling for X-Upstream-Authorization header (always enabled)
request_headers_lower = {k.lower(): v for k, v in request_headers.items()} if request_headers else {}
upstream_auth = request_headers_lower.get("x-upstream-authorization")
if upstream_auth:
try:
sanitized_value = sanitize_header_value(upstream_auth)
if sanitized_value:
# Always rename X-Upstream-Authorization to Authorization for upstream
# This works for both auth and no-auth gateways
passthrough_headers["Authorization"] = sanitized_value
logger.debug("Renamed X-Upstream-Authorization to Authorization for upstream passthrough")
except Exception as e:
logger.warning(f"Failed to sanitize X-Upstream-Authorization header: {e}")
elif gateway and gateway.auth_type == "none":
# When gateway has no auth, pass through client's Authorization if present
client_auth = request_headers_lower.get("authorization")
if client_auth and "authorization" not in [h.lower() for h in base_headers.keys()]:
try:
sanitized_value = sanitize_header_value(client_auth)
if sanitized_value:
passthrough_headers["Authorization"] = sanitized_value
logger.debug("Passing through client Authorization header (auth_type=none)")
except Exception as e:
logger.warning(f"Failed to sanitize Authorization header: {e}")Impact Analysis
Benefits
- Enables the OBO authentication pattern with IDP
- Maintains backward compatibility for existing auth scenarios
- Provides clear separation of gateway auth vs upstream auth
- Works consistently regardless of gateway auth configuration
Testing Required
- Test with
auth_type = "none"andx-upstream-authorizationheader - Test with
auth_type = "basic/bearer/oauth"andx-upstream-authorizationheader - Test passthrough of regular
Authorizationheader whenauth_type = "none" - Verify no regression in existing auth flows
Alternative Consideration
Another approach would be to make x-upstream-authorization processing completely independent of gateway auth type, treating it as a universal "override" for upstream Authorization. This would simplify the logic but might have broader implications for existing deployments.