mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix: address potential bugs in auth_utils, mcp discoverable endpoints, and mcp auth
- auth_utils.get_request_route: return '/' instead of empty string when raw_path exactly equals root_path so downstream route allowlist checks still see a leading slash - discoverable_endpoints.fetch_upstream_oauth_protected_resource: also cache negative results (no upstream metadata) for a shorter TTL so we don't re-fetch on every discovery request and so the per-key fetch lock can be pruned - user_api_key_auth_mcp: guard the oauth2_headers 401 cold-start passthrough bypass with _has_client_supplied_mcp_auth, matching the parallel bypass in the no-Authorization branch so MCP-auth-bearing requests don't silently downgrade to anonymous admission Co-authored-by: Yassin Kortam <yassin@berri.ai>
This commit is contained in:
parent
77206df140
commit
c949c54c4d
3 changed files with 19 additions and 2 deletions
|
|
@ -268,6 +268,10 @@ class MCPRequestHandler:
|
|||
)
|
||||
if (
|
||||
mcp_servers_from_path is not None
|
||||
and not _has_client_supplied_mcp_auth(
|
||||
mcp_auth_header,
|
||||
mcp_server_auth_headers,
|
||||
)
|
||||
and _is_mcp_passthrough_cold_start(
|
||||
mcp_servers_from_path, client_ip=client_ip
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,8 +31,11 @@ from litellm.types.mcp_server.mcp_server_manager import MCPServer
|
|||
# TTL cache for upstream OAuth metadata fetched from pass-through MCP servers.
|
||||
# Keeps us from hammering the upstream IdP on each discovery request.
|
||||
# Keyed by (server_id, resource_url) → (expires_at_epoch, payload).
|
||||
_OAUTH_METADATA_CACHE: Dict[Tuple[str, str], Tuple[float, dict]] = {}
|
||||
# A payload of ``None`` is a negative-result entry that prevents repeated
|
||||
# upstream fetches when the IdP consistently has no metadata to serve.
|
||||
_OAUTH_METADATA_CACHE: Dict[Tuple[str, str], Tuple[float, Optional[dict]]] = {}
|
||||
_OAUTH_METADATA_CACHE_TTL_SECONDS = 300
|
||||
_OAUTH_METADATA_NEGATIVE_CACHE_TTL_SECONDS = 60
|
||||
_OAUTH_METADATA_CACHE_MAX_SIZE = 128
|
||||
# Per-(server_id, resource_url) async locks so concurrent discovery requests
|
||||
# coalesce onto a single upstream fetch instead of issuing N parallel calls.
|
||||
|
|
@ -830,6 +833,15 @@ async def fetch_upstream_oauth_protected_resource(
|
|||
if len(network_errors) == len(candidates):
|
||||
raise network_errors[-1]
|
||||
|
||||
# Negative-result caching: when no candidate yielded a usable payload,
|
||||
# remember that for a shorter TTL so we don't re-fetch on every
|
||||
# subsequent discovery request (and so the per-key lock can be pruned).
|
||||
now = time.time()
|
||||
_OAUTH_METADATA_CACHE[cache_key] = (
|
||||
now + _OAUTH_METADATA_NEGATIVE_CACHE_TTL_SECONDS,
|
||||
None,
|
||||
)
|
||||
_prune_oauth_metadata_cache(now)
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -518,7 +518,8 @@ def get_request_route(request: Request) -> str:
|
|||
if root_path and (
|
||||
raw_path == root_path or raw_path.startswith(root_path + "/")
|
||||
):
|
||||
return raw_path[len(root_path) :]
|
||||
stripped = raw_path[len(root_path) :]
|
||||
return stripped or "/"
|
||||
return raw_path
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.debug(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue