Skip to content

mcp-auth strips Authorization header injected by set-headers policy #2866

Description

@renuka-fernando

Description

The mcp-auth policy reads and validates the client's downstream Authorization header (delegating to jwt-auth), then removes that header before the request is forwarded upstream. The removal is emitted during the request-body phase (mcp-auth OnRequestBody copies jwt-auth's HeadersToRemove=["authorization"] into its UpstreamRequestModifications).

If a set-headers policy is also configured to set an upstream Authorization header, that value is set during the request-header phase. The kernel runs every policy's header phase before any policy's body phase, so the mcp-auth body-phase removal always executes after the set-headers header-phase set. The net result is that the backend receives no Authorization header, even though set-headers was configured to inject one.

Concrete scenario:

  • mcp-auth validates the client's Authorization header (downstream credential).
  • set-headers sets Authorization to a backend credential to be forwarded upstream.
  • Expected: client credential is validated and stripped; backend receives the set-headers value.
  • Actual: backend receives no Authorization header — the set-headers value is dropped by mcp-auth's body-phase removal.

Key point: this is phase-based, not order-based. Reordering the two policies does not help, because set-headers always runs in the header phase and mcp-auth's removal always runs in the body phase — the removal always wins.

Note: validation itself is unaffected — mcp-auth/jwt-auth validate against the Downstream snapshot (the client's real header), so a peer policy rewriting the live header does not break validation. Only the removal step is buggy.

Note: today the Authorization removal is driven by jwt-auth's forwardToken default (true), which relocates the token to x-forwarded-authorization and removes the original Authorization. Even forwardToken=false removes it. So there is no existing setting that says "validate but leave Authorization untouched for a downstream policy to own." Additionally, mcp-auth does not currently expose forwardToken at all — it falls through to jwt-auth's default via a maps.Clone(params) pass-through — so users cannot opt out.

Proposed fix

Introduce a forwardToken param on mcp-auth — the same name as jwt-auth's param, but as an enum (not a bool) with these values:

  • send — forward the validated token (equivalent to jwt-auth's forwardToken=true).
  • drop — remove the Authorization header entirely (equivalent to jwt-auth's forwardToken=false).
  • auto (default) — only remove the header if, at body phase, the live Authorization value still equals the Downstream snapshot value (i.e. no peer policy rewrote it). If a peer policy (e.g. set-headers) injected a different value, preserve it.

auto is a genuinely new third behavior that neither jwt-auth nor mcp-auth has today, and it fixes the collision described above out of the box while keeping the secure default of stripping the client credential when nothing else claims the header.

While implementing, confirm the interaction with jwt-auth's existing forwardToken / forwardedTokenHeader (default x-forwarded-authorization) path so the same token is not double-handled.

Exposing the remaining jwt-auth forwarding params (headerName, forwardedTokenHeader, forwardTokenStripScheme) on mcp-auth for consistency is tracked separately in #2867.

Version

mcp-auth v1.1.2

Related Issue

#2867

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions