mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-23 22:27:10 +00:00
Merge pull request #22786 from milan-berri/fix/custom-sso-handler-user-info
fix: update Okta SSO docs and custom SSO handler example
This commit is contained in:
@@ -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="<your-client-id>"
|
||||
GENERIC_CLIENT_SECRET="<your-client-secret>"
|
||||
GENERIC_AUTHORIZATION_ENDPOINT="https://<your-okta-domain>/oauth2/v1/authorize"
|
||||
GENERIC_TOKEN_ENDPOINT="https://<your-okta-domain>/oauth2/v1/token"
|
||||
GENERIC_USERINFO_ENDPOINT="https://<your-okta-domain>/oauth2/v1/userinfo"
|
||||
PROXY_BASE_URL="https://<your-proxy-base-url>"
|
||||
```
|
||||
|
||||
**Custom Authorization Server** (requires the Okta API Access Management SKU):
|
||||
```bash
|
||||
GENERIC_CLIENT_ID="<your-client-id>"
|
||||
GENERIC_CLIENT_SECRET="<your-client-secret>"
|
||||
GENERIC_AUTHORIZATION_ENDPOINT="https://<your-okta-domain>/oauth2/default/v1/authorize"
|
||||
GENERIC_TOKEN_ENDPOINT="https://<your-okta-domain>/oauth2/default/v1/token"
|
||||
GENERIC_USERINFO_ENDPOINT="https://<your-okta-domain>/oauth2/default/v1/userinfo"
|
||||
PROXY_BASE_URL="https://<your-proxy-base-url>"
|
||||
```
|
||||
|
||||
:::tip
|
||||
You can find all OAuth endpoints at `https://<your-okta-domain>/.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**
|
||||
|
||||
<Image img={require('../../img/okta_security_api.png')} />
|
||||
@@ -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="<your-client-id>"
|
||||
GENERIC_CLIENT_SECRET="<your-client-secret>"
|
||||
GENERIC_AUTHORIZATION_ENDPOINT="https://<your-okta-domain>/oauth2/default/v1/authorize"
|
||||
GENERIC_TOKEN_ENDPOINT="https://<your-okta-domain>/oauth2/default/v1/token"
|
||||
GENERIC_USERINFO_ENDPOINT="https://<your-okta-domain>/oauth2/default/v1/userinfo"
|
||||
GENERIC_CLIENT_STATE="random-string"
|
||||
PROXY_BASE_URL="https://<your-proxy-base-url>"
|
||||
```
|
||||
|
||||
:::tip
|
||||
You can find all OAuth endpoints at `https://<your-okta-domain>/.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://<your-okta-domain>/.well-known/open
|
||||
|-------|-------|----------|
|
||||
| `redirect_uri` error | Redirect URI not configured | Add `<proxy_base_url>/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) |
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="google" label="Google SSO">
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -15,13 +15,11 @@ 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:
|
||||
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}")
|
||||
@@ -32,8 +30,8 @@ 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)
|
||||
|
||||
return SSOUserDefinedValues(
|
||||
models=[],
|
||||
|
||||
Reference in New Issue
Block a user