-
Notifications
You must be signed in to change notification settings - Fork 615
[BUG][SECURITY]: Fallback auth token precedence differs from middleware in stateful sessionsΒ #3019
Description
π Bug Summary
The stateful session fallback auth path in _get_request_context_or_default() resolves user identity through require_auth_override() β require_auth(), which prioritizes cookie tokens over Authorization headers. The primary middleware auth path (streamable_http_auth) uses Authorization header only. If a request carries both a valid Authorization header and a different valid cookie JWT, the middleware authenticates as the header user but the fallback authenticates as the cookie user, causing identity drift.
π§© Affected Component
- Federation or Transports
-
mcpgateway- API
π Steps to Reproduce
- Enable stateful sessions (
USE_STATEFUL_SESSIONS=true) - Send a request to
/servers/{id}/mcp/with:Authorization: Bearer <token-for-user-A>- Cookie
jwt_token=<token-for-user-B>
- On the initial request,
streamable_http_authauthenticates as user A (header only) - On a subsequent request where ContextVars are stale,
_get_request_context_or_default()falls back and authenticates as user B (cookie takes precedence inrequire_auth)
π€ Expected Behavior
The fallback auth path should use the same token precedence as the middleware β Authorization header first, cookie as fallback only when no header is present.
π Code References
- Middleware (header-only):
mcpgateway/transports/streamablehttp_transport.py:2200-2205 - Fallback calls
require_auth_override:mcpgateway/transports/streamablehttp_transport.py:1002 require_authtoken precedence (cookie first):mcpgateway/utils/verify_credentials.py:389-401
π‘ Suggested Fix
In _get_request_context_or_default(), align token precedence with middleware by only passing the cookie when no Authorization header is present:
auth_header = req_headers.get("authorization")
cookie_token = request.cookies.get("jwt_token") if not auth_header else NoneOr create a dedicated require_auth_header_first() variant that matches the middleware's precedence.
π Practical Risk Assessment
Low practical risk β this only triggers when:
- Stateful session ContextVars are stale (fallback fires)
- Request carries BOTH a valid Authorization header AND a different valid cookie JWT
- MCP SDK clients use headers (not cookies); browser Admin UI uses cookies (not MCP transport)
The scenario of both being present with different identities is unusual in practice.
| Key | Value |
|---|---|
| Version or commit | main (pre-existing, noted during PR #2974 review) |
| Runtime | Python 3.13 |
| Severity | Medium (requires unusual conditions, but could cause authorization inconsistency) |