Skip to content

[Bug]: X-Upstream-Authorization Header Not Working When Auth Type is None #1104

@crivetimihai

Description

@crivetimihai

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 header

This means when auth_type = "none", the special handling for x-upstream-authorization is completely skipped.

Use Case Requirements

Workflow:

  1. Configure MCP Gateway with auth_type = "none" (no gateway-level authentication)
  2. Client/agent performs On-Behalf-Of (OBO) authentication with our IDP
  3. Client obtains an auth token from IDP
  4. Client wants to pass this token to the upstream MCP server
  5. Client sends request with:
    • Authorization: Bearer <gateway_token> (if gateway had auth)
    • x-upstream-authorization: Bearer <mcp_server_token> (for upstream)
  6. 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-authorization when 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-authorization processing 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-authorization to pass different auth to upstream
  • This gets renamed to Authorization for upstream

Scenario 2: Gateway has no auth (missing implementation)

When auth_type = "none":

  • Check for x-upstream-authorization header
  • If present, use it as Authorization for upstream (override any client Authorization header)
  • If not present, pass through client's Authorization header 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

  1. Enables the OBO authentication pattern with IDP
  2. Maintains backward compatibility for existing auth scenarios
  3. Provides clear separation of gateway auth vs upstream auth
  4. Works consistently regardless of gateway auth configuration

Testing Required

  1. Test with auth_type = "none" and x-upstream-authorization header
  2. Test with auth_type = "basic/bearer/oauth" and x-upstream-authorization header
  3. Test passthrough of regular Authorization header when auth_type = "none"
  4. 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.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriageIssues / Features awaiting triage

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions