mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-23 00:41:40 +00:00
fix(sso): use active provider for callback env_prefix; dedupe SSO payload util
Bug #7 (ui_sso.py auth_callback): env_prefix and generic_client_id were derived solely from whether OKTA_CLIENT_ID was set, so a Google or Microsoft login with OKTA_CLIENT_ID also configured would route through the generic attribute-mapping branch with OKTA_* env-var lookups. Track the active provider in the elif chain and derive env_prefix / callback client_id from that — Google/Microsoft pass None. Bug #8 (SSOModals.tsx): handleFormSubmit duplicated processSSOSettingsPayload from utils.ts. The two had already diverged (legacy modal missing use_team_mappings / team_ids_jwt_field handling and the provider-supports-role-mappings guard). Replace inline logic with the shared utility.
This commit is contained in:
parent
f8f43a4295
commit
3454b00c28
2 changed files with 29 additions and 54 deletions
|
|
@ -1729,18 +1729,24 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa:
|
|||
|
||||
verbose_proxy_logger.info(f"Redirecting to {redirect_url}")
|
||||
result = None
|
||||
# Track which SSO provider actually handled this callback so attribute
|
||||
# mapping (env_prefix, generic_client_id) reflects the active branch.
|
||||
# Multiple *_CLIENT_ID env vars may be set; the elif chain picks one.
|
||||
active_provider: Optional[str] = None
|
||||
if google_client_id is not None:
|
||||
result = await GoogleSSOHandler.get_google_callback_response(
|
||||
request=request,
|
||||
google_client_id=google_client_id,
|
||||
redirect_url=redirect_url,
|
||||
)
|
||||
active_provider = "google"
|
||||
elif microsoft_client_id is not None:
|
||||
result = await MicrosoftSSOHandler.get_microsoft_callback_response(
|
||||
request=request,
|
||||
microsoft_client_id=microsoft_client_id,
|
||||
redirect_url=redirect_url,
|
||||
)
|
||||
active_provider = "microsoft"
|
||||
|
||||
elif okta_client_id is not None:
|
||||
(
|
||||
|
|
@ -1755,6 +1761,7 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa:
|
|||
sso_jwt_handler=sso_jwt_handler,
|
||||
provider=_OIDC_PROVIDER_OKTA,
|
||||
)
|
||||
active_provider = _OIDC_PROVIDER_OKTA
|
||||
|
||||
elif generic_client_id is not None:
|
||||
(
|
||||
|
|
@ -1768,6 +1775,7 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa:
|
|||
redirect_url=redirect_url,
|
||||
sso_jwt_handler=sso_jwt_handler,
|
||||
)
|
||||
active_provider = _OIDC_PROVIDER_GENERIC
|
||||
|
||||
if result is None:
|
||||
raise HTTPException(
|
||||
|
|
@ -1787,20 +1795,29 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa:
|
|||
# Starlette's cookie_parser already handles RFC 2109 unquoting.
|
||||
cp_return_to: Optional[str] = request.cookies.get("litellm_cp_return_to")
|
||||
|
||||
# Only pass generic_client_id / env_prefix for the OIDC providers; Google
|
||||
# and Microsoft results do not flow through the generic attribute-mapping
|
||||
# branch in `_get_user_email_and_id_from_result`.
|
||||
if active_provider == _OIDC_PROVIDER_OKTA:
|
||||
callback_client_id: Optional[str] = okta_client_id
|
||||
callback_env_prefix = _get_oidc_env_prefix(_OIDC_PROVIDER_OKTA)
|
||||
elif active_provider == _OIDC_PROVIDER_GENERIC:
|
||||
callback_client_id = generic_client_id
|
||||
callback_env_prefix = _get_oidc_env_prefix(_OIDC_PROVIDER_GENERIC)
|
||||
else:
|
||||
callback_client_id = None
|
||||
callback_env_prefix = _get_oidc_env_prefix(_OIDC_PROVIDER_GENERIC)
|
||||
|
||||
return await SSOAuthenticationHandler.get_redirect_response_from_openid(
|
||||
result=result,
|
||||
request=request,
|
||||
received_response=received_response,
|
||||
generic_client_id=okta_client_id or generic_client_id,
|
||||
generic_client_id=callback_client_id,
|
||||
ui_access_mode=ui_access_mode,
|
||||
access_token_payload=access_token_payload,
|
||||
jwt_handler=jwt_handler,
|
||||
return_to=cp_return_to,
|
||||
env_prefix=_get_oidc_env_prefix(
|
||||
_OIDC_PROVIDER_OKTA
|
||||
if okta_client_id is not None
|
||||
else _OIDC_PROVIDER_GENERIC
|
||||
),
|
||||
env_prefix=callback_env_prefix,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
|
|||
import { Modal, Form, Input, Button as Button2, Select, Checkbox } from "antd";
|
||||
import { Text, TextInput } from "@tremor/react";
|
||||
import { getSSOSettings, updateSSOSettings } from "./networking";
|
||||
import { detectSSOProvider, extractRoleMappingFields } from "./Settings/AdminSettings/SSOSettings/utils";
|
||||
import {
|
||||
detectSSOProvider,
|
||||
extractRoleMappingFields,
|
||||
processSSOSettingsPayload,
|
||||
} from "./Settings/AdminSettings/SSOSettings/utils";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
import { parseErrorMessage } from "./shared/errorUtils";
|
||||
|
||||
|
|
@ -150,53 +154,7 @@ const SSOModals: React.FC<SSOModalsProps> = ({
|
|||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
proxy_admin_teams,
|
||||
admin_viewer_teams,
|
||||
internal_user_teams,
|
||||
internal_viewer_teams,
|
||||
default_role,
|
||||
group_claim,
|
||||
use_role_mappings,
|
||||
...rest
|
||||
} = formValues;
|
||||
|
||||
const payload: any = {
|
||||
...rest,
|
||||
};
|
||||
|
||||
// Add role mappings if use_role_mappings is checked
|
||||
if (use_role_mappings) {
|
||||
const provider = rest.sso_provider || "generic";
|
||||
// Helper function to split comma-separated string into array
|
||||
const splitTeams = (teams: string | undefined): string[] => {
|
||||
if (!teams || teams.trim() === "") return [];
|
||||
return teams
|
||||
.split(",")
|
||||
.map((team) => team.trim())
|
||||
.filter((team) => team.length > 0);
|
||||
};
|
||||
|
||||
// Map default role display values to backend values
|
||||
const defaultRoleMapping: Record<string, string> = {
|
||||
internal_user_viewer: "internal_user_viewer",
|
||||
internal_user: "internal_user",
|
||||
proxy_admin_viewer: "proxy_admin_viewer",
|
||||
proxy_admin: "proxy_admin",
|
||||
};
|
||||
|
||||
payload.role_mappings = {
|
||||
provider,
|
||||
group_claim,
|
||||
default_role: defaultRoleMapping[default_role] || "internal_user",
|
||||
roles: {
|
||||
proxy_admin: splitTeams(proxy_admin_teams),
|
||||
proxy_admin_viewer: splitTeams(admin_viewer_teams),
|
||||
internal_user: splitTeams(internal_user_teams),
|
||||
internal_user_viewer: splitTeams(internal_viewer_teams),
|
||||
},
|
||||
};
|
||||
}
|
||||
const payload = processSSOSettingsPayload(formValues);
|
||||
|
||||
// Save SSO settings using the new API
|
||||
await updateSSOSettings(accessToken, payload);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue