diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index 9828311112e..5bba8e4e326 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -2459,7 +2459,11 @@ async def _run_centralized_common_checks( pass_through_endpoints: Final = general_settings.get("pass_through_endpoints", None) if pass_through_endpoints is not None: for endpoint in pass_through_endpoints: - if isinstance(endpoint, dict) and endpoint.get("path", "") == route and endpoint.get("auth") is not True: + if ( + isinstance(endpoint, dict) + and endpoint.get("path", "") == route + and endpoint.get("auth", True) is not True + ): return # No-auth dev mode: master_key unset AND no JWT/OAuth2 auth diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index ea4ede7e513..38becf465cf 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -3116,7 +3116,7 @@ async def _register_pass_through_endpoint( forward_headers: Final = endpoint_data.get("forward_headers") merge_query_params: Final = endpoint_data.get("merge_query_params") default_query_params: Final = endpoint_data.get("default_query_params") - auth: Final[bool | str | None] = endpoint_data.get("auth") + auth: Final[bool | str | None] = endpoint_data.get("auth", True) dependencies = None auth_enforced: Final = auth is not None and str(auth).lower() == "true" diff --git a/tests/test_litellm/proxy/auth/test_user_api_key_auth.py b/tests/test_litellm/proxy/auth/test_user_api_key_auth.py index 0cdbcde6abc..e4eb3c213a9 100644 --- a/tests/test_litellm/proxy/auth/test_user_api_key_auth.py +++ b/tests/test_litellm/proxy/auth/test_user_api_key_auth.py @@ -3960,6 +3960,50 @@ async def test_centralized_common_checks_runs_for_standard_auth(): setattr(_proxy_server_mod, k, v) +@pytest.mark.asyncio +async def test_centralized_common_checks_runs_for_raw_passthrough_without_auth(): + import litellm.proxy.proxy_server as _proxy_server_mod + from fastapi import Request + from starlette.datastructures import URL + + route = "/chat/completions" + token = UserAPIKeyAuth(api_key="sk-test") + request = Request(scope={"type": "http", "headers": []}) + request._url = URL(url=route) + attrs = _proxy_attrs_for_centralized_checks(user_custom_auth=None) + attrs["general_settings"] = { + "reject_clientside_metadata_tags": True, + "pass_through_endpoints": [ + { + "path": route, + "target": "https://example.com", + } + ] + } + originals = { + attribute: getattr(_proxy_server_mod, attribute, None) + for attribute in attrs + } + try: + for attribute, value in attrs.items(): + setattr(_proxy_server_mod, attribute, value) + with pytest.raises(ProxyException) as exc_info: + await _run_centralized_common_checks( + user_api_key_auth_obj=token, + request=request, + request_data={ + "model": "gpt-4o", + "metadata": {"tags": ["client-supplied"]}, + }, + route=route, + ) + finally: + for attribute, value in originals.items(): + setattr(_proxy_server_mod, attribute, value) + + assert exc_info.value.type == ProxyErrorTypes.bad_request_error + + @pytest.mark.asyncio @pytest.mark.parametrize( "route", diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_auth_default.py b/tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_auth_default.py index 44a75c362e5..3955def7eef 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_auth_default.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_auth_default.py @@ -25,11 +25,12 @@ import pytest from fastapi import FastAPI -from litellm.proxy._types import PassThroughGenericEndpoint +from litellm.proxy._types import LiteLLMRoutes, PassThroughGenericEndpoint from litellm.proxy.auth.user_api_key_auth import ( check_api_key_for_custom_headers_or_pass_through_endpoints, ) from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( + _registered_pass_through_routes, _register_pass_through_endpoint, ) @@ -79,6 +80,32 @@ async def test_register_passthrough_with_auth_true_works_for_oss(monkeypatch): ) +@pytest.mark.asyncio +async def test_register_raw_passthrough_defaults_to_authentication(): + path = "/raw-auth-default" + route_key = f"raw-auth-default:exact:{path}:GET" + endpoint: dict[str, object] = { + "id": "raw-auth-default", + "path": path, + "target": "https://example.com", + "methods": ["GET"], + } + + try: + await _register_pass_through_endpoint( + endpoint=endpoint, + app=FastAPI(), + premium_user=False, + visited_endpoints=set(), + ) + assert _registered_pass_through_routes[route_key]["auth"] is True + assert path in LiteLLMRoutes.openai_routes.value + finally: + _registered_pass_through_routes.pop(route_key, None) + if path in LiteLLMRoutes.openai_routes.value: + LiteLLMRoutes.openai_routes.value.remove(path) + + @pytest.mark.asyncio async def test_runtime_check_treats_missing_auth_key_as_authenticated(): # The runtime dispatch in user_api_key_auth pulls