mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(mcp): consistent www-authenticate casing + SSE toolset scoping
- Normalize the WWW-Authenticate header key emitted by _check_passthrough_upstream_auth to lowercase to match the other 401 emitters in the OAuth pass-through flow. - Mirror the streamable HTTP handler's toolset scoping in handle_sse_mcp: strip client-supplied x-mcp-toolset-id and apply _apply_toolset_scope before _check_passthrough_upstream_auth so the upstream probe list is derived from the fully-authorized server set. - Tighten _has_client_supplied_mcp_auth signature so mcp_server_auth_headers is Optional, matching its caller in process_mcp_request. Co-authored-by: Yassin Kortam <yassin@berri.ai>
This commit is contained in:
parent
e9ff79c96a
commit
7dfef63a02
2 changed files with 22 additions and 3 deletions
|
|
@ -93,7 +93,7 @@ def _is_litellm_auth_admission_error(exc: Exception) -> bool:
|
|||
|
||||
def _has_client_supplied_mcp_auth(
|
||||
mcp_auth_header: Optional[str],
|
||||
mcp_server_auth_headers: Dict[str, Dict[str, str]],
|
||||
mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]],
|
||||
) -> bool:
|
||||
return bool(mcp_auth_header) or bool(mcp_server_auth_headers)
|
||||
|
||||
|
|
|
|||
|
|
@ -3142,7 +3142,7 @@ if MCP_AVAILABLE:
|
|||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Unauthorized",
|
||||
headers={"WWW-Authenticate": www_authenticate},
|
||||
headers={"www-authenticate": www_authenticate},
|
||||
)
|
||||
if probe_status == 403:
|
||||
# Token is valid but the caller lacks permission — do not hint
|
||||
|
|
@ -3311,10 +3311,29 @@ if MCP_AVAILABLE:
|
|||
user_api_key_auth=user_api_key_auth,
|
||||
client_ip=_sse_client_ip,
|
||||
)
|
||||
|
||||
# Strip any client-supplied x-mcp-toolset-id to prevent forgery.
|
||||
scope["headers"] = [
|
||||
(k, v)
|
||||
for k, v in scope.get("headers", [])
|
||||
if k.lower() != b"x-mcp-toolset-id"
|
||||
]
|
||||
|
||||
# Apply toolset scope if set server-side via ContextVar so the
|
||||
# downstream probe list matches the fully-authorized server set
|
||||
# (mirrors the streamable HTTP handler).
|
||||
active_toolset_id = _mcp_active_toolset_id.get()
|
||||
if active_toolset_id and user_api_key_auth is not None:
|
||||
user_api_key_auth = await _apply_toolset_scope(
|
||||
user_api_key_auth, active_toolset_id
|
||||
)
|
||||
|
||||
# Pre-flight auth check for pass-through servers: surface upstream
|
||||
# 401/403 as a proper challenge before the SSE session commits 200
|
||||
# headers, so clients can refresh their OAuth token instead of
|
||||
# being stuck with a silently empty tool list.
|
||||
# being stuck with a silently empty tool list. Must run after
|
||||
# toolset scoping so the probe list is derived from the fully-
|
||||
# authorized server set, not the raw user-supplied names.
|
||||
await _check_passthrough_upstream_auth(
|
||||
scope, user_api_key_auth, mcp_servers, _sse_client_ip
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue