mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
feat: add cross-origin SSO cookie handoff for control plane
- Add return_to param to /sso/key/generate for cross-origin redirect - Store return_to in httpOnly cookie during SSO redirect - Read cookie in /sso/callback and pass to OpenID handler - Redirect to control plane with token in URL, delete cookie after use
This commit is contained in:
parent
d04f8bcc45
commit
5543718caf
1 changed files with 44 additions and 1 deletions
|
|
@ -301,6 +301,7 @@ async def google_login(
|
|||
source: Optional[str] = None,
|
||||
key: Optional[str] = None,
|
||||
existing_key: Optional[str] = None,
|
||||
return_to: Optional[str] = None,
|
||||
): # noqa: PLR0915
|
||||
"""
|
||||
Create Proxy API Keys using Google Workspace SSO. Requires setting PROXY_BASE_URL in .env
|
||||
|
|
@ -394,13 +395,22 @@ async def google_login(
|
|||
is True
|
||||
):
|
||||
verbose_proxy_logger.info(f"Redirecting to SSO login for {redirect_url}")
|
||||
return await SSOAuthenticationHandler.get_sso_login_redirect(
|
||||
sso_redirect = await SSOAuthenticationHandler.get_sso_login_redirect(
|
||||
redirect_url=redirect_url,
|
||||
microsoft_client_id=microsoft_client_id,
|
||||
google_client_id=google_client_id,
|
||||
generic_client_id=generic_client_id,
|
||||
state=cli_state,
|
||||
)
|
||||
if return_to is not None and sso_redirect is not None:
|
||||
sso_redirect.set_cookie(
|
||||
key="litellm_cp_return_to",
|
||||
value=return_to,
|
||||
max_age=600,
|
||||
httponly=True,
|
||||
samesite="lax",
|
||||
)
|
||||
return sso_redirect
|
||||
elif ui_username is not None:
|
||||
# No Google, Microsoft SSO
|
||||
# Use UI Credentials set in .env
|
||||
|
|
@ -1306,12 +1316,19 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa:
|
|||
request=request, key=key_id, existing_key=existing_key, result=result
|
||||
)
|
||||
|
||||
# Control-plane cross-origin: read return_to from cookie
|
||||
cp_return_to: Optional[str] = None
|
||||
cookie_return_to = request.cookies.get("litellm_cp_return_to")
|
||||
if cookie_return_to:
|
||||
cp_return_to = cookie_return_to.strip('"')
|
||||
|
||||
return await SSOAuthenticationHandler.get_redirect_response_from_openid(
|
||||
result=result,
|
||||
request=request,
|
||||
received_response=received_response,
|
||||
generic_client_id=generic_client_id,
|
||||
ui_access_mode=ui_access_mode,
|
||||
return_to=cp_return_to,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -2352,6 +2369,7 @@ class SSOAuthenticationHandler:
|
|||
received_response: Optional[dict] = None,
|
||||
generic_client_id: Optional[str] = None,
|
||||
ui_access_mode: Optional[Dict] = None,
|
||||
return_to: Optional[str] = None,
|
||||
) -> RedirectResponse:
|
||||
import jwt
|
||||
|
||||
|
|
@ -2528,6 +2546,31 @@ class SSOAuthenticationHandler:
|
|||
master_key or "",
|
||||
algorithm="HS256",
|
||||
)
|
||||
|
||||
# Control-plane cross-origin: redirect back to the control plane UI
|
||||
# with the token in the URL (cookie won't work cross-origin)
|
||||
if return_to is not None:
|
||||
from urllib.parse import urlencode, urlparse
|
||||
|
||||
parsed = urlparse(return_to)
|
||||
if parsed.scheme not in ("http", "https"):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="return_to must be an HTTP(S) URL",
|
||||
)
|
||||
separator = "&" if "?" in return_to else "?"
|
||||
redirect_url = (
|
||||
return_to
|
||||
+ separator
|
||||
+ urlencode({"login": "success", "token": jwt_token})
|
||||
)
|
||||
verbose_proxy_logger.info(
|
||||
f"Cross-origin SSO: redirecting to control plane at {parsed.netloc}"
|
||||
)
|
||||
redirect_response = RedirectResponse(url=redirect_url, status_code=303)
|
||||
redirect_response.delete_cookie("litellm_cp_return_to")
|
||||
return redirect_response
|
||||
|
||||
if user_id is not None and isinstance(user_id, str):
|
||||
litellm_dashboard_ui += "?login=success"
|
||||
verbose_proxy_logger.info(f"Redirecting to {litellm_dashboard_ui}")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue