diff --git a/docs/my-website/docs/proxy/ui.md b/docs/my-website/docs/proxy/ui.md index 0a19c427c2f..6538793c1f1 100644 --- a/docs/my-website/docs/proxy/ui.md +++ b/docs/my-website/docs/proxy/ui.md @@ -31,6 +31,18 @@ general_settings: ## 2. Setup SSO/Auth for UI + + +Set the following in your .env on the Proxy + +```shell +UI_USERNAME=ishaan-litellm +UI_PASSWORD=langchain +``` + +On accessing the LiteLLM UI, you will be prompted to enter your username, password + + @@ -73,6 +85,7 @@ MICROSOFT_TENANT="5a39737 ``` + ## 4. Use UI diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index f1ec2744cd6..edd4232f73c 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -76,6 +76,7 @@ from litellm.proxy.utils import ( get_logging_payload, reset_budget, hash_token, + html_form, ) from litellm.proxy.secret_managers.google_kms import load_google_kms import pydantic @@ -94,6 +95,7 @@ from fastapi import ( BackgroundTasks, Header, Response, + Form, ) from fastapi.routing import APIRouter from fastapi.security import OAuth2PasswordBearer @@ -2958,6 +2960,60 @@ async def google_login(request: Request): ) with microsoft_sso: return await microsoft_sso.get_login_redirect() + else: + # No Google, Microsoft SSO + # Use UI Credentials set in .env + from fastapi.responses import HTMLResponse + + return HTMLResponse(content=html_form, status_code=200) + + +@router.post( + "/login", include_in_schema=False +) # hidden since this is a helper for UI sso login +async def login(request: Request): + try: + import multipart + except ImportError: + subprocess.run(["pip", "install", "python-multipart"]) + + form = await request.form() + username = str(form.get("username")) + password = form.get("password") + ui_username = os.getenv("UI_USERNAME") + ui_password = os.getenv("UI_PASSWORD") + + if username == ui_username and password == ui_password: + user_id = username + response = await generate_key_helper_fn( + **{"duration": "24hr", "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": user_id, "team_id": "litellm-dashboard"} # type: ignore + ) + + key = response["token"] # type: ignore + user_id = response["user_id"] # type: ignore + litellm_dashboard_ui = "https://litellm-dashboard.vercel.app/" + + # if user set LITELLM_UI_LINK in .env, use that + litellm_ui_link_in_env = os.getenv("LITELLM_UI_LINK", None) + if litellm_ui_link_in_env is not None: + litellm_dashboard_ui = litellm_ui_link_in_env + + litellm_dashboard_ui += ( + "?userID=" + + user_id + + "&accessToken=" + + key + + "&proxyBaseUrl=" + + os.getenv("PROXY_BASE_URL") + ) + return RedirectResponse(url=litellm_dashboard_ui) + else: + raise ProxyException( + message=f"Invalid credentials used to access UI. Passed in username: {username}, passed in password: {password}.\nCheck 'UI_USERNAME', 'UI_PASSWORD' in .env file", + type="auth_error", + param="invalid_credentials", + code=status.HTTP_401_UNAUTHORIZED, + ) @app.get("/sso/callback", tags=["experimental"]) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 3ec45203f58..d9194e712ba 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1208,3 +1208,67 @@ async def reset_budget(prisma_client: PrismaClient): await prisma_client.update_data( query_type="update_many", data_list=users_to_reset, table_name="user" ) + + +# LiteLLM Admin UI - Non SSO Login +html_form = """ + + + + LiteLLM Login + + + +
+

LiteLLM Login

+ + + + + +
+ + +"""