From 32b387468470b65561798e8f385eea7106aab051 Mon Sep 17 00:00:00 2001 From: Milan Date: Wed, 4 Mar 2026 16:24:09 +0200 Subject: [PATCH 1/2] fix: update Okta SSO docs and custom SSO handler example 1. Okta SSO docs (admin_ui_sso.md): - Rewrite Step 3 to document both Org Auth Server (free) and Custom Auth Server (paid SKU) as tabbed options - Add Step 4 for GENERIC_CLIENT_STATE and PKCE configuration (moved from troubleshooting into the main guide) - Clarify no_matching_policy error only applies to Custom Auth Server - Deduplicate troubleshooting section to reference Step 4 2. Custom SSO handler (custom_sso.py + custom_sso.md): - Replace broken user_info() call with prisma_client.get_data() - user_info() is a FastAPI route handler requiring Request and UserAPIKeyAuth params, cannot be called directly - Keep new_user/add_new_member as commented-out import references in docs for customers who need them --- docs/my-website/docs/proxy/admin_ui_sso.md | 72 +++++++++++++--------- docs/my-website/docs/proxy/custom_sso.md | 16 ++--- litellm/proxy/custom_sso.py | 7 ++- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/docs/my-website/docs/proxy/admin_ui_sso.md b/docs/my-website/docs/proxy/admin_ui_sso.md index f88d348044..2bd4cf24b4 100644 --- a/docs/my-website/docs/proxy/admin_ui_sso.md +++ b/docs/my-website/docs/proxy/admin_ui_sso.md @@ -41,12 +41,38 @@ After creating the app, copy your **Client ID** and **Client Secret** from the a Ensure users are assigned to the app in the **Assignments** tab. If Federation Broker Mode is enabled, you may need to disable it to assign users manually. -#### Step 3: Configure Authorization Server Access Policy +#### Step 3: Set Environment Variables -:::warning Important -This step is required. Without an Access Policy for your app, users will get a `no_matching_policy` error when attempting to log in. +Set the following environment variables. The only difference between the two Okta authorization servers is the endpoint URLs: + +**Org Authorization Server** (available on all Okta plans, no additional SKU required): +```bash +GENERIC_CLIENT_ID="" +GENERIC_CLIENT_SECRET="" +GENERIC_AUTHORIZATION_ENDPOINT="https:///oauth2/v1/authorize" +GENERIC_TOKEN_ENDPOINT="https:///oauth2/v1/token" +GENERIC_USERINFO_ENDPOINT="https:///oauth2/v1/userinfo" +PROXY_BASE_URL="https://" +``` + +**Custom Authorization Server** (requires the Okta API Access Management SKU): +```bash +GENERIC_CLIENT_ID="" +GENERIC_CLIENT_SECRET="" +GENERIC_AUTHORIZATION_ENDPOINT="https:///oauth2/default/v1/authorize" +GENERIC_TOKEN_ENDPOINT="https:///oauth2/default/v1/token" +GENERIC_USERINFO_ENDPOINT="https:///oauth2/default/v1/userinfo" +PROXY_BASE_URL="https://" +``` + +:::tip +You can find all OAuth endpoints at `https:///.well-known/openid-configuration` ::: +#### Step 3a: Configure Access Policy (Custom Authorization Server only) + +If you are using the Custom Authorization Server, you must configure an Access Policy. Without it, users will get a `no_matching_policy` error. Skip this step if you are using the Org Authorization Server. + 1. Go to **Security** → **API** @@ -62,21 +88,21 @@ This step is required. Without an Access Policy for your app, users will get a ` See [Okta's Access Policy documentation](https://help.okta.com/en-us/content/topics/security/api-access-management/access-policies.htm) for more details. -#### Step 4: Configure LiteLLM Environment Variables +#### Step 4: Configure Okta Security Settings + +**GENERIC_CLIENT_STATE** is recommended for Okta to prevent CSRF attacks: ```bash -GENERIC_CLIENT_ID="" -GENERIC_CLIENT_SECRET="" -GENERIC_AUTHORIZATION_ENDPOINT="https:///oauth2/default/v1/authorize" -GENERIC_TOKEN_ENDPOINT="https:///oauth2/default/v1/token" -GENERIC_USERINFO_ENDPOINT="https:///oauth2/default/v1/userinfo" GENERIC_CLIENT_STATE="random-string" -PROXY_BASE_URL="https://" ``` -:::tip -You can find all OAuth endpoints at `https:///.well-known/openid-configuration` -::: +**PKCE (Proof Key for Code Exchange)** — If your Okta application is configured to require PKCE, enable it by setting: + +```bash +GENERIC_CLIENT_USE_PKCE="true" +``` + +LiteLLM will automatically handle PKCE parameter generation and verification during the OAuth flow. #### Step 5: Test the SSO Flow @@ -91,7 +117,7 @@ You can find all OAuth endpoints at `https:///.well-known/open |-------|-------|----------| | `redirect_uri` error | Redirect URI not configured | Add `/sso/callback` to Sign-in redirect URIs in Okta | | `access_denied` | User not assigned to app | Assign the user in the Assignments tab | -| `no_matching_policy` | Missing Access Policy | Create an Access Policy in the Authorization Server (see Step 3) | +| `no_matching_policy` | Missing Access Policy (Custom Authorization Server only) | Create an Access Policy in the Authorization Server (see Step 3a) | @@ -456,23 +482,9 @@ PROXY_BASE_URL=http://litellm.platform.com PROXY_BASE_URL=litellm.platform.com ``` -**2. For Okta specifically, ensure GENERIC_CLIENT_STATE is set** +**2. For Okta specifically, ensure `GENERIC_CLIENT_STATE` is set and PKCE is configured if required** -Okta requires the `GENERIC_CLIENT_STATE` parameter: - -```bash -GENERIC_CLIENT_STATE="random-string" # Required for Okta -``` - -### Okta PKCE - -If your Okta application is configured to require PKCE (Proof Key for Code Exchange), enable it by setting: - -```bash -GENERIC_CLIENT_USE_PKCE="true" -``` - -This is required when your Okta app settings enforce PKCE for enhanced security. LiteLLM will automatically handle PKCE parameter generation and verification during the OAuth flow. +See [Okta SSO — Step 4: Configure Okta Security Settings](#step-4-configure-okta-security-settings) for details on `GENERIC_CLIENT_STATE` and PKCE configuration. ### Common Configuration Issues diff --git a/docs/my-website/docs/proxy/custom_sso.md b/docs/my-website/docs/proxy/custom_sso.md index 8b7adeb0c5..41ecde6e36 100644 --- a/docs/my-website/docs/proxy/custom_sso.md +++ b/docs/my-website/docs/proxy/custom_sso.md @@ -121,15 +121,14 @@ Use this if you want to run your own code **after** a user signs on to the LiteL Make sure the response type follows the `SSOUserDefinedValues` pydantic object. This is used for logging the user into the Admin UI: ```python -from fastapi import Request from fastapi_sso.sso.base import OpenID from litellm.proxy._types import LitellmUserRoles, SSOUserDefinedValues -from litellm.proxy.management_endpoints.internal_user_endpoints import ( - new_user, - user_info, -) -from litellm.proxy.management_endpoints.team_endpoints import add_new_member +from litellm.proxy import proxy_server + +# These imports are available if you need to create users or manage team membership: +# from litellm.proxy.management_endpoints.internal_user_endpoints import new_user +# from litellm.proxy.management_endpoints.team_endpoints import add_new_member async def custom_sso_handler(userIDPInfo: OpenID) -> SSOUserDefinedValues: @@ -158,8 +157,9 @@ async def custom_sso_handler(userIDPInfo: OpenID) -> SSOUserDefinedValues: ################################################# # Run your custom code / logic here # check if user exists in litellm proxy DB - _user_info = await user_info(user_id=userIDPInfo.id) - print("_user_info from litellm DB ", _user_info) # noqa + if proxy_server.prisma_client is not None: + _user_info = await proxy_server.prisma_client.get_data(user_id=userIDPInfo.id) + print("_user_info from litellm DB ", _user_info) # noqa ################################################# return SSOUserDefinedValues( diff --git a/litellm/proxy/custom_sso.py b/litellm/proxy/custom_sso.py index b2b028dfbe..1419b5551c 100644 --- a/litellm/proxy/custom_sso.py +++ b/litellm/proxy/custom_sso.py @@ -15,7 +15,7 @@ Flow: from fastapi_sso.sso.base import OpenID from litellm.proxy._types import LitellmUserRoles, SSOUserDefinedValues -from litellm.proxy.management_endpoints.internal_user_endpoints import user_info +from litellm.proxy import proxy_server async def custom_sso_handler(userIDPInfo: OpenID) -> SSOUserDefinedValues: @@ -32,8 +32,9 @@ async def custom_sso_handler(userIDPInfo: OpenID) -> SSOUserDefinedValues: # user_groups = extra_fields.get("group", []) # check if user exists in litellm proxy DB - _user_info = await user_info(user_id=userIDPInfo.id) - print("_user_info from litellm DB ", _user_info) # noqa + if proxy_server.prisma_client is not None: + _user_info = await proxy_server.prisma_client.get_data(user_id=userIDPInfo.id) + print("_user_info from litellm DB ", _user_info) # noqa return SSOUserDefinedValues( models=[], From 277ad677c596dde253ea5251b0a61c9b468c2a68 Mon Sep 17 00:00:00 2001 From: Milan Date: Sat, 7 Mar 2026 12:27:16 +0200 Subject: [PATCH 2/2] chore: remove debug print statements from custom_sso.py --- litellm/proxy/custom_sso.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/litellm/proxy/custom_sso.py b/litellm/proxy/custom_sso.py index 1419b5551c..651aed4c56 100644 --- a/litellm/proxy/custom_sso.py +++ b/litellm/proxy/custom_sso.py @@ -20,8 +20,6 @@ from litellm.proxy import proxy_server async def custom_sso_handler(userIDPInfo: OpenID) -> SSOUserDefinedValues: try: - print("inside custom sso handler") # noqa - print(f"userIDPInfo: {userIDPInfo}") # noqa if userIDPInfo.id is None: raise ValueError(f"No ID found for user. userIDPInfo.id is None {userIDPInfo}") @@ -34,7 +32,6 @@ async def custom_sso_handler(userIDPInfo: OpenID) -> SSOUserDefinedValues: # check if user exists in litellm proxy DB if proxy_server.prisma_client is not None: _user_info = await proxy_server.prisma_client.get_data(user_id=userIDPInfo.id) - print("_user_info from litellm DB ", _user_info) # noqa return SSOUserDefinedValues( models=[],