diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index b1a0b76017b..1d00e721fda 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -2755,11 +2755,12 @@ class SSOAuthenticationHandler: ) if userinfo is None: + if userinfo_endpoint: + detail = "userinfo endpoint failed and no id_token was present in the token response" + else: + detail = "no userinfo endpoint is configured (GENERIC_USERINFO_ENDPOINT) and no id_token was present" raise ProxyException( - message=( - "SSO user info unavailable: userinfo endpoint failed and no id_token " - "was present in the token response." - ), + message=f"SSO user info unavailable: {detail}.", type=ProxyErrorTypes.auth_error, param="userinfo", code=status.HTTP_401_UNAUTHORIZED, diff --git a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py index 379abdbfa7e..fa591b89660 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -4669,6 +4669,32 @@ async def test_pkce_userinfo_falls_back_to_id_token(): assert result["email"] == "jwt@example.com" +@pytest.mark.asyncio +async def test_pkce_userinfo_uses_id_token_when_no_endpoint(): + """When userinfo_endpoint is None, fall back to id_token directly without HTTP call.""" + import base64 + import json as _json + + from litellm.proxy.management_endpoints.ui_sso import SSOAuthenticationHandler + + payload = {"sub": "id_token_user", "email": "id@example.com"} + encoded_payload = ( + base64.urlsafe_b64encode(_json.dumps(payload).encode()).rstrip(b"=").decode() + ) + fake_id_token = f"eyJhbGciOiJSUzI1NiJ9.{encoded_payload}.fakesig" + + # No httpx call should happen when userinfo_endpoint is None + result = await SSOAuthenticationHandler._get_pkce_userinfo( + access_token="some_token", + id_token=fake_id_token, + userinfo_endpoint=None, + additional_headers={}, + ) + + assert result["sub"] == "id_token_user" + assert result["email"] == "id@example.com" + + @pytest.mark.asyncio async def test_pkce_userinfo_raises_when_both_sources_unavailable(): """When userinfo endpoint fails AND no id_token, raise ProxyException."""