From 0586d83c16ff43d5770b064a365574c7a6960b68 Mon Sep 17 00:00:00 2001 From: AlexSze Date: Fri, 24 Apr 2026 17:49:53 +0800 Subject: [PATCH 1/7] add fix to auth error --- litellm/proxy/management_endpoints/ui_sso.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index 46e7963da7c..2be0158c6cd 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -1308,6 +1308,14 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa: """Verify login""" verbose_proxy_logger.info(f"Starting SSO callback with state: {state}") + oauth_error = request.query_params.get("error") + oauth_error_description = request.query_params.get("error_description") + if oauth_error: + raise HTTPException( + status_code=401, + detail=f"OAuth error: {oauth_error}, error_description: {oauth_error_description}", + ) + # Check if this is a CLI login (state starts with our CLI prefix) from litellm.constants import LITELLM_CLI_SESSION_TOKEN_PREFIX from litellm.proxy._types import LiteLLM_JWTAuth From f7406b441f5c017bc5f1ec61b2397b1918d4b9d5 Mon Sep 17 00:00:00 2001 From: AlexSze Date: Fri, 24 Apr 2026 18:02:00 +0800 Subject: [PATCH 2/7] add test --- .../proxy/management_endpoints/test_ui_sso.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 eecfcaa035b..c95adc73ed1 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -2150,6 +2150,26 @@ class TestCLIKeyRegenerationFlow: result=mock_result, ) + @pytest.mark.asyncio + async def test_auth_callback_raises_on_oauth_error(self): + """Test that auth_callback returns a 401 when the provider redirects with an OAuth error""" + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = MagicMock(spec=Request) + mock_request.query_params = { + "error": "access_denied", + "error_description": "User denied consent", + } + + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state="test-state") + + assert exc_info.value.status_code == 401 + assert ( + exc_info.value.detail + == "OAuth error: access_denied, error_description: User denied consent" + ) + def test_get_redirect_url_does_not_include_existing_key_in_url(self): """Test that redirect URL generation does NOT include existing_key in URL (uses state parameter instead)""" from litellm.proxy.management_endpoints.ui_sso import SSOAuthenticationHandler From d9b264ba139c71ebf0aad8eb16359aedc75fb42e Mon Sep 17 00:00:00 2001 From: AlexSze Date: Fri, 24 Apr 2026 18:40:31 +0800 Subject: [PATCH 3/7] Refactor auth error handling in auth_callback to improve clarity and change status code to 400 --- litellm/proxy/management_endpoints/ui_sso.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index 2be0158c6cd..d35e65be9bb 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -1311,9 +1311,14 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa: oauth_error = request.query_params.get("error") oauth_error_description = request.query_params.get("error_description") if oauth_error: + oauth_error_detail = f"OAuth error: {oauth_error}" + if oauth_error_description: + oauth_error_detail += ( + f", error_description: {oauth_error_description}" + ) raise HTTPException( - status_code=401, - detail=f"OAuth error: {oauth_error}, error_description: {oauth_error_description}", + status_code=400, + detail=oauth_error_detail, ) # Check if this is a CLI login (state starts with our CLI prefix) From 895f771397c3505a7234dacd22559ef45787df30 Mon Sep 17 00:00:00 2001 From: Alex Sze <51705750+AlexSze@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:45:19 +0800 Subject: [PATCH 4/7] Update tests/test_litellm/proxy/management_endpoints/test_ui_sso.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- tests/test_litellm/proxy/management_endpoints/test_ui_sso.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c95adc73ed1..3220e0069b5 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -2168,7 +2168,7 @@ class TestCLIKeyRegenerationFlow: assert ( exc_info.value.detail == "OAuth error: access_denied, error_description: User denied consent" - ) + assert exc_info.value.status_code == 400 def test_get_redirect_url_does_not_include_existing_key_in_url(self): """Test that redirect URL generation does NOT include existing_key in URL (uses state parameter instead)""" From a1275a9223d5f087e9120a7c36ac6103b5c2374d Mon Sep 17 00:00:00 2001 From: Alex Sze <51705750+AlexSze@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:48:43 +0800 Subject: [PATCH 5/7] Update tests/test_litellm/proxy/management_endpoints/test_ui_sso.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- tests/test_litellm/proxy/management_endpoints/test_ui_sso.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 3220e0069b5..3aaaebe0fb6 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -2164,11 +2164,11 @@ class TestCLIKeyRegenerationFlow: with pytest.raises(HTTPException) as exc_info: await auth_callback(request=mock_request, state="test-state") - assert exc_info.value.status_code == 401 + assert exc_info.value.status_code == 400 assert ( exc_info.value.detail == "OAuth error: access_denied, error_description: User denied consent" - assert exc_info.value.status_code == 400 + ) def test_get_redirect_url_does_not_include_existing_key_in_url(self): """Test that redirect URL generation does NOT include existing_key in URL (uses state parameter instead)""" From b83c9a59dc410b68904df902119fa0ac37da2f72 Mon Sep 17 00:00:00 2001 From: Alex Sze Date: Mon, 1 Jun 2026 15:50:22 +0800 Subject: [PATCH 6/7] lint --- litellm/proxy/management_endpoints/ui_sso.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index d35e65be9bb..76761d49360 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -1313,9 +1313,7 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa: if oauth_error: oauth_error_detail = f"OAuth error: {oauth_error}" if oauth_error_description: - oauth_error_detail += ( - f", error_description: {oauth_error_description}" - ) + oauth_error_detail += f", error_description: {oauth_error_description}" raise HTTPException( status_code=400, detail=oauth_error_detail, From 390a6021f9a3d59e4f923993c6a63da36bc8cc28 Mon Sep 17 00:00:00 2001 From: Alex Sze Date: Mon, 1 Jun 2026 15:56:14 +0800 Subject: [PATCH 7/7] fix test --- tests/test_litellm/proxy/management_endpoints/test_ui_sso.py | 1 + 1 file changed, 1 insertion(+) 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 3aaaebe0fb6..f7bf507c6da 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -2115,6 +2115,7 @@ class TestCLIKeyRegenerationFlow: # Mock request (no query params needed - existing_key is in state) mock_request = MagicMock(spec=Request) + mock_request.query_params = {} # CLI state with existing_key embedded: {PREFIX}:{key}:{existing_key} cli_state = f"{LITELLM_CLI_SESSION_TOKEN_PREFIX}:sk-new-session-key-456:sk-existing-cli-key-123"