Merge pull request #1697 from BerriAI/litellm_ui_sso_optional

[Feat-UI] Make SSO Optional
This commit is contained in:
Ishaan Jaff 2024-01-30 18:47:35 -08:00 committed by GitHub
commit 4219fe02d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 133 additions and 0 deletions

View file

@ -31,6 +31,18 @@ general_settings:
## 2. Setup SSO/Auth for UI
<Tabs>
<TabItem value="username" label="Quick Start - Username, Password">
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
</TabItem>
<TabItem value="google" label="Google SSO">
@ -73,6 +85,7 @@ MICROSOFT_TENANT="5a39737
```
</TabItem>
</Tabs>
## 4. Use UI

View file

@ -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"])

View file

@ -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 = """
<!DOCTYPE html>
<html>
<head>
<title>LiteLLM Login</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
label {
display: block;
margin-bottom: 8px;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 16px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
input[type="submit"] {
background-color: #4caf50;
color: #fff;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<form action="/login" method="post">
<h2>LiteLLM Login</h2>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<input type="submit" value="Submit">
</form>
</body>
</html>
"""