From 865529c53f9e593602de5c74f848d6b6674a5299 Mon Sep 17 00:00:00 2001
From: tanjiro <56165694+NANDINI-star@users.noreply.github.com>
Date: Thu, 17 Jul 2025 19:09:24 +0900
Subject: [PATCH] fix sso logout
- add a new login page with sso button
---
litellm/proxy/management_endpoints/ui_sso.py | 356 ++++++++++++++++++-
1 file changed, 340 insertions(+), 16 deletions(-)
diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py
index e2836ca191f..9fdb05c4bc9 100644
--- a/litellm/proxy/management_endpoints/ui_sso.py
+++ b/litellm/proxy/management_endpoints/ui_sso.py
@@ -72,11 +72,13 @@ router = APIRouter()
@router.get("/sso/key/generate", tags=["experimental"], include_in_schema=False)
-async def google_login(request: Request, source: Optional[str] = None, key: Optional[str] = None): # noqa: PLR0915
+async def serve_login_page(request: Request, source: Optional[str] = None, key: Optional[str] = None, error: Optional[str] = None):
"""
Create Proxy API Keys using Google Workspace SSO. Requires setting PROXY_BASE_URL in .env
PROXY_BASE_URL should be the your deployed proxy endpoint, e.g. PROXY_BASE_URL="https://litellm-production-7002.up.railway.app/"
Example:
+ Serves a unified login page with options for both normal
+ username/password login and SSO.
"""
from litellm.proxy.proxy_server import (
premium_user,
@@ -94,6 +96,316 @@ async def google_login(request: Request, source: Optional[str] = None, key: Opti
if is_disabled:
return admin_ui_disabled()
+ ####### Check if user is a Enterprise / Premium User for SSO #######
+ sso_available = False
+ if (
+ microsoft_client_id is not None
+ or google_client_id is not None
+ or generic_client_id is not None
+ ):
+ if premium_user is True:
+ sso_available = True
+
+ ####### Detect DB + MASTER KEY in .env #######
+ missing_env_vars = show_missing_vars_in_env()
+ if missing_env_vars is not None:
+ return missing_env_vars
+
+ # Build the unified login page HTML
+ error_message = ""
+ if error == "1":
+ error_message = """
+
+ ⚠️ Invalid username or password. Please try again.
+
+ """
+
+ sso_button = ""
+ if sso_available:
+ sso_button = """
+
+ """
+
+ # Get the base URL for form action - CHANGE THIS TO POINT TO /login
+ proxy_base_url = os.getenv("PROXY_BASE_URL", "")
+ server_root_path = os.getenv("SERVER_ROOT_PATH", "")
+ if server_root_path != "":
+ proxy_base_url += server_root_path
+ form_action = proxy_base_url + "/sso/key/generate" # CHANGE BACK to /sso/key/generate
+
+ unified_login_html = f"""
+
+
+
+
+ LiteLLM Login
+
+
+
+
+
+
+
+
+ """
+
+ from fastapi.responses import HTMLResponse
+ return HTMLResponse(content=unified_login_html, status_code=200)
+
+
+@router.get("/sso/login", tags=["experimental"], include_in_schema=False)
+async def sso_login_redirect(request: Request, source: Optional[str] = None, key: Optional[str] = None):
+ """
+ Handles SSO login redirect - this is what the "Login with SSO" button points to
+ """
+ from litellm.proxy.proxy_server import premium_user, user_custom_ui_sso_sign_in_handler
+
+ microsoft_client_id = os.getenv("MICROSOFT_CLIENT_ID", None)
+ google_client_id = os.getenv("GOOGLE_CLIENT_ID", None)
+ generic_client_id = os.getenv("GENERIC_CLIENT_ID", None)
+
####### Check if user is a Enterprise / Premium User #######
if (
microsoft_client_id is not None
@@ -108,12 +420,6 @@ async def google_login(request: Request, source: Optional[str] = None, key: Opti
code=status.HTTP_403_FORBIDDEN,
)
- ####### Detect DB + MASTER KEY in .env #######
- missing_env_vars = show_missing_vars_in_env()
- if missing_env_vars is not None:
- return missing_env_vars
- ui_username = os.getenv("UI_USERNAME")
-
# get url from request - always use regular callback, but set state for CLI
redirect_url = SSOAuthenticationHandler.get_redirect_url_for_sso(
request=request,
@@ -155,16 +461,9 @@ async def google_login(request: Request, source: Optional[str] = None, key: Opti
generic_client_id=generic_client_id,
state=cli_state,
)
- elif ui_username is not None:
- # No Google, Microsoft SSO
- # Use UI Credentials set in .env
- from fastapi.responses import HTMLResponse
-
- return HTMLResponse(content=html_form, status_code=200)
else:
- from fastapi.responses import HTMLResponse
-
- return HTMLResponse(content=html_form, status_code=200)
+ # No SSO configured, redirect back to login page
+ return RedirectResponse(url="/sso/key/generate", status_code=303)
def generic_response_convertor(
@@ -1889,3 +2188,28 @@ async def debug_sso_callback(request: Request):
)
return HTMLResponse(content=html_content)
+
+
+@router.post("/sso/key/generate", tags=["experimental"], include_in_schema=False)
+async def process_login(request: Request):
+ """
+ Process username/password login from the unified login page
+ """
+ try:
+ # Get form data
+ form_data = await request.form()
+ username = form_data.get("username")
+ password = form_data.get("password")
+
+ if not username or not password:
+ return RedirectResponse(url="/sso/key/generate?error=1", status_code=303)
+
+ # Import the actual login function from proxy_server
+ from litellm.proxy.proxy_server import login
+
+ # Call the real login function that handles all the authentication properly
+ return await login(request)
+
+ except Exception as e:
+ verbose_proxy_logger.error(f"Error processing login: {e}")
+ return RedirectResponse(url="/sso/key/generate?error=1", status_code=303)