mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-06 19:18:16 +00:00
Compare commits
2 Commits
docs/sso-c
...
devin/1774
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25a13a0dd1 | ||
|
|
e21c506214 |
1303
docs/docs.json
1303
docs/docs.json
File diff suppressed because it is too large
Load Diff
550
docs/en/enterprise/features/sso.mdx
Normal file
550
docs/en/enterprise/features/sso.mdx
Normal file
@@ -0,0 +1,550 @@
|
||||
---
|
||||
title: Single Sign-On (SSO)
|
||||
icon: "key"
|
||||
description: Configure enterprise SSO authentication for CrewAI Platform — SaaS and Factory
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
CrewAI Platform supports enterprise Single Sign-On (SSO) across both **SaaS (AMP)** and **Factory (self-hosted)** deployments. SSO enables your team to authenticate using your organization's existing identity provider, enforcing centralized access control, MFA policies, and user lifecycle management.
|
||||
|
||||
### Supported Providers
|
||||
|
||||
| Provider | SaaS | Factory | Protocol | CLI Support |
|
||||
|---|---|---|---|---|
|
||||
| **WorkOS** | ✅ (default) | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
| **Microsoft Entra ID** (Azure AD) | ✅ (enterprise) | ✅ | OAuth 2.0 / SAML 2.0 | ✅ |
|
||||
| **Okta** | ✅ (enterprise) | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
| **Auth0** | ✅ (enterprise) | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
| **Keycloak** | — | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
|
||||
### Key Capabilities
|
||||
|
||||
- **SAML 2.0 and OAuth 2.0 / OIDC** protocol support
|
||||
- **Device Authorization Grant** flow for CLI authentication
|
||||
- **Role-Based Access Control (RBAC)** with custom roles and per-resource permissions
|
||||
- **MFA enforcement** delegated to your identity provider
|
||||
- **User provisioning** through IdP assignment (users/groups)
|
||||
|
||||
---
|
||||
|
||||
## SaaS SSO
|
||||
|
||||
### Default Authentication
|
||||
|
||||
CrewAI's managed SaaS platform (AMP) uses **WorkOS** as the default authentication provider. When you sign up at [app.crewai.com](https://app.crewai.com), authentication is handled through `login.crewai.com` — no additional SSO configuration is required.
|
||||
|
||||
### Enterprise Custom SSO
|
||||
|
||||
Enterprise SaaS customers can configure SSO with their own identity provider (Entra ID, Okta, Auth0). Contact your CrewAI account team to enable custom SSO for your organization. Once configured:
|
||||
|
||||
1. Your team members authenticate through your organization's IdP
|
||||
2. Access control and MFA policies are enforced by your IdP
|
||||
3. The CrewAI CLI automatically detects your SSO configuration via `crewai enterprise configure`
|
||||
|
||||
### CLI Defaults (SaaS)
|
||||
|
||||
| Setting | Default Value |
|
||||
|---|---|
|
||||
| `enterprise_base_url` | `https://app.crewai.com` |
|
||||
| `oauth2_provider` | `workos` |
|
||||
| `oauth2_domain` | `login.crewai.com` |
|
||||
|
||||
---
|
||||
|
||||
## Factory SSO Setup
|
||||
|
||||
Factory (self-hosted) deployments require you to configure SSO by setting environment variables in your Helm `values.yaml` and registering an application in your identity provider.
|
||||
|
||||
### Microsoft Entra ID (Azure AD)
|
||||
|
||||
<Steps>
|
||||
<Step title="Register an Application">
|
||||
1. Go to [portal.azure.com](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**
|
||||
2. Configure:
|
||||
- **Name:** `CrewAI` (or your preferred name)
|
||||
- **Supported account types:** Accounts in this organizational directory only
|
||||
- **Redirect URI:** Select **Web**, enter `https://<your-domain>/auth/entra_id/callback`
|
||||
3. Click **Register**
|
||||
</Step>
|
||||
|
||||
<Step title="Collect Credentials">
|
||||
From the app overview page, copy:
|
||||
- **Application (client) ID** → `ENTRA_ID_CLIENT_ID`
|
||||
- **Directory (tenant) ID** → `ENTRA_ID_TENANT_ID`
|
||||
</Step>
|
||||
|
||||
<Step title="Create Client Secret">
|
||||
1. Navigate to **Certificates & Secrets** → **New client secret**
|
||||
2. Add a description and select expiration period
|
||||
3. Copy the secret value immediately (it won't be shown again) → `ENTRA_ID_CLIENT_SECRET`
|
||||
</Step>
|
||||
|
||||
<Step title="Grant Admin Consent">
|
||||
1. Go to **Enterprise applications** → select your app
|
||||
2. Under **Security** → **Permissions**, click **Grant admin consent**
|
||||
3. Ensure **Microsoft Graph → User.Read** is granted
|
||||
</Step>
|
||||
|
||||
<Step title="Configure App Roles (Recommended)">
|
||||
Under **App registrations** → your app → **App roles**, create:
|
||||
|
||||
| Display Name | Value | Allowed Member Types |
|
||||
|---|---|---|
|
||||
| Member | `member` | Users/Groups |
|
||||
| Factory Admin | `factory-admin` | Users/Groups |
|
||||
|
||||
<Note>
|
||||
The `member` role grants login access. The `factory-admin` role grants admin panel access. Roles are included in the JWT automatically.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Assign Users">
|
||||
1. Under **Properties**, set **Assignment required?** to **Yes**
|
||||
2. Under **Users and groups**, assign users/groups with the appropriate role
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "entra_id"
|
||||
|
||||
secrets:
|
||||
ENTRA_ID_CLIENT_ID: "<Application (client) ID>"
|
||||
ENTRA_ID_CLIENT_SECRET: "<Client Secret>"
|
||||
ENTRA_ID_TENANT_ID: "<Directory (tenant) ID>"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
To allow `crewai login` via Device Authorization Grant:
|
||||
|
||||
1. Under **Authentication** → **Advanced settings**, enable **Allow public client flows**
|
||||
2. Under **Expose an API**, add an Application ID URI (e.g., `api://crewai-cli`)
|
||||
3. Add a scope (e.g., `read`) with **Admins and users** consent
|
||||
4. Under **Manifest**, set `accessTokenAcceptedVersion` to `2`
|
||||
5. Add environment variables:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
ENTRA_ID_DEVICE_AUTHORIZATION_CLIENT_ID: "<Application (client) ID>"
|
||||
ENTRA_ID_CUSTOM_OPENID_SCOPE: "<scope URI, e.g. api://crewai-cli/read>"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### Okta
|
||||
|
||||
<Steps>
|
||||
<Step title="Create App Integration">
|
||||
1. Open Okta Admin Console → **Applications** → **Create App Integration**
|
||||
2. Select **OIDC - OpenID Connect** → **Web Application** → **Next**
|
||||
3. Configure:
|
||||
- **App integration name:** `CrewAI SSO`
|
||||
- **Sign-in redirect URI:** `https://<your-domain>/auth/okta/callback`
|
||||
- **Sign-out redirect URI:** `https://<your-domain>`
|
||||
- **Assignments:** Choose who can access (everyone or specific groups)
|
||||
4. Click **Save**
|
||||
</Step>
|
||||
|
||||
<Step title="Collect Credentials">
|
||||
From the app details page:
|
||||
- **Client ID** → `OKTA_CLIENT_ID`
|
||||
- **Client Secret** → `OKTA_CLIENT_SECRET`
|
||||
- **Okta URL** (top-right corner, under your username) → `OKTA_SITE`
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Authorization Server">
|
||||
1. Navigate to **Security** → **API**
|
||||
2. Select your authorization server (default: `default`)
|
||||
3. Under **Access Policies**, add a policy and rule:
|
||||
- In the rule, under **Scopes requested**, select **The following scopes** → **OIDC default scopes**
|
||||
4. Note the **Name** and **Audience** of the authorization server
|
||||
|
||||
<Warning>
|
||||
The authorization server name and audience must match `OKTA_AUTHORIZATION_SERVER` and `OKTA_AUDIENCE` exactly. Mismatches cause `401 Unauthorized` or `Invalid token: Signature verification failed` errors.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "okta"
|
||||
|
||||
secrets:
|
||||
OKTA_CLIENT_ID: "<Okta app client ID>"
|
||||
OKTA_CLIENT_SECRET: "<Okta client secret>"
|
||||
OKTA_SITE: "https://your-domain.okta.com"
|
||||
OKTA_AUTHORIZATION_SERVER: "default"
|
||||
OKTA_AUDIENCE: "api://default"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
1. Create a **new** app integration: **OIDC** → **Native Application**
|
||||
2. Enable **Device Authorization** and **Refresh Token** grant types
|
||||
3. Allow everyone in your organization to access
|
||||
4. Add environment variable:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
OKTA_DEVICE_AUTHORIZATION_CLIENT_ID: "<Native app client ID>"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Device Authorization requires a **Native Application** — it cannot use the Web Application created for browser-based SSO.
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### Keycloak
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Client">
|
||||
1. Open Keycloak Admin Console → navigate to your realm
|
||||
2. **Clients** → **Create client**:
|
||||
- **Client type:** OpenID Connect
|
||||
- **Client ID:** `crewai-factory` (suggested)
|
||||
3. Capability config:
|
||||
- **Client authentication:** On
|
||||
- **Standard flow:** Checked
|
||||
4. Login settings:
|
||||
- **Root URL:** `https://<your-domain>`
|
||||
- **Valid redirect URIs:** `https://<your-domain>/auth/keycloak/callback`
|
||||
- **Valid post logout redirect URIs:** `https://<your-domain>`
|
||||
5. Click **Save**
|
||||
</Step>
|
||||
|
||||
<Step title="Collect Credentials">
|
||||
- **Client ID** → `KEYCLOAK_CLIENT_ID`
|
||||
- Under **Credentials** tab: **Client secret** → `KEYCLOAK_CLIENT_SECRET`
|
||||
- **Realm name** → `KEYCLOAK_REALM`
|
||||
- **Keycloak server URL** → `KEYCLOAK_SITE`
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "keycloak"
|
||||
|
||||
secrets:
|
||||
KEYCLOAK_CLIENT_ID: "<client ID>"
|
||||
KEYCLOAK_CLIENT_SECRET: "<client secret>"
|
||||
KEYCLOAK_SITE: "https://keycloak.yourdomain.com"
|
||||
KEYCLOAK_REALM: "<realm name>"
|
||||
KEYCLOAK_AUDIENCE: "account"
|
||||
# Only set if using a custom base path (pre-v17 migrations):
|
||||
# KEYCLOAK_BASE_URL: "/auth"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Keycloak includes `account` as the default audience in access tokens. For most installations, `KEYCLOAK_AUDIENCE=account` works without additional configuration. See [Keycloak audience documentation](https://www.keycloak.org/docs/latest/authorization_services/index.html) if you need a custom audience.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
1. Create a **second** client:
|
||||
- **Client type:** OpenID Connect
|
||||
- **Client ID:** `crewai-factory-cli` (suggested)
|
||||
- **Client authentication:** Off (Device Authorization requires a public client)
|
||||
- **Authentication flow:** Check **only** OAuth 2.0 Device Authorization Grant
|
||||
2. Add environment variable:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
KEYCLOAK_DEVICE_AUTHORIZATION_CLIENT_ID: "<CLI client ID>"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### WorkOS
|
||||
|
||||
<Steps>
|
||||
<Step title="Configure in WorkOS Dashboard">
|
||||
1. Create an application in the [WorkOS Dashboard](https://dashboard.workos.com)
|
||||
2. Configure the redirect URI: `https://<your-domain>/auth/workos/callback`
|
||||
3. Note the **Client ID** and **AuthKit domain**
|
||||
4. Set up organizations in the WorkOS dashboard
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "workos"
|
||||
|
||||
secrets:
|
||||
WORKOS_CLIENT_ID: "<WorkOS client ID>"
|
||||
WORKOS_AUTHKIT_DOMAIN: "<your-authkit-domain.authkit.com>"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### Auth0
|
||||
|
||||
<Steps>
|
||||
<Step title="Create Application">
|
||||
1. In the [Auth0 Dashboard](https://manage.auth0.com), create a new **Regular Web Application**
|
||||
2. Configure:
|
||||
- **Allowed Callback URLs:** `https://<your-domain>/auth/auth0/callback`
|
||||
- **Allowed Logout URLs:** `https://<your-domain>`
|
||||
3. Note the **Domain**, **Client ID**, and **Client Secret**
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "auth0"
|
||||
|
||||
secrets:
|
||||
AUTH0_CLIENT_ID: "<Auth0 client ID>"
|
||||
AUTH0_CLIENT_SECRET: "<Auth0 client secret>"
|
||||
AUTH0_DOMAIN: "<your-tenant.auth0.com>"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
1. Create a **Native** application in Auth0 for Device Authorization
|
||||
2. Enable the **Device Authorization** grant type under application settings
|
||||
3. Configure the CLI with the appropriate audience and client ID
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## CLI Authentication
|
||||
|
||||
The CrewAI CLI supports SSO authentication via the **Device Authorization Grant** flow. This allows developers to authenticate from their terminal without exposing credentials.
|
||||
|
||||
### Quick Setup
|
||||
|
||||
For Factory installations, the CLI can auto-configure all OAuth2 settings:
|
||||
|
||||
```bash
|
||||
crewai enterprise configure https://your-factory-url.app
|
||||
```
|
||||
|
||||
This command fetches the SSO configuration from your Factory instance and sets all required CLI parameters automatically.
|
||||
|
||||
Then authenticate:
|
||||
|
||||
```bash
|
||||
crewai login
|
||||
```
|
||||
|
||||
<Note>
|
||||
Requires CrewAI CLI version **1.6.0** or higher for Entra ID, **0.159.0** or higher for Okta, and **1.9.0** or higher for Keycloak.
|
||||
</Note>
|
||||
|
||||
### Manual CLI Configuration
|
||||
|
||||
If you need to configure the CLI manually, use `crewai config set`:
|
||||
|
||||
```bash
|
||||
# Set the provider
|
||||
crewai config set oauth2_provider okta
|
||||
|
||||
# Set provider-specific values
|
||||
crewai config set oauth2_domain your-domain.okta.com
|
||||
crewai config set oauth2_client_id your-client-id
|
||||
crewai config set oauth2_audience api://default
|
||||
|
||||
# Set the enterprise base URL
|
||||
crewai config set enterprise_base_url https://your-factory-url.app
|
||||
```
|
||||
|
||||
### CLI Configuration Reference
|
||||
|
||||
| Setting | Description | Example |
|
||||
|---|---|---|
|
||||
| `enterprise_base_url` | Your CrewAI instance URL | `https://crewai.yourcompany.com` |
|
||||
| `oauth2_provider` | Provider name | `workos`, `okta`, `auth0`, `entra_id`, `keycloak` |
|
||||
| `oauth2_domain` | Provider domain | `your-domain.okta.com` |
|
||||
| `oauth2_client_id` | OAuth2 client ID | `0oaqnwji7pGW7VT6T697` |
|
||||
| `oauth2_audience` | API audience identifier | `api://default` |
|
||||
|
||||
View current configuration:
|
||||
|
||||
```bash
|
||||
crewai config list
|
||||
```
|
||||
|
||||
### How Device Authorization Works
|
||||
|
||||
1. Run `crewai login` — the CLI requests a device code from your IdP
|
||||
2. A verification URL and code are displayed in your terminal
|
||||
3. Your browser opens to the verification URL
|
||||
4. Enter the code and authenticate with your IdP credentials
|
||||
5. The CLI receives an access token and stores it locally
|
||||
|
||||
---
|
||||
|
||||
## Role-Based Access Control (RBAC)
|
||||
|
||||
CrewAI Platform provides granular RBAC that integrates with your SSO provider.
|
||||
|
||||
### Permission Model
|
||||
|
||||
| Permission | Description |
|
||||
|---|---|
|
||||
| **Read** | View resources (dashboards, automations, logs) |
|
||||
| **Write** | Create and modify resources |
|
||||
| **Manage** | Full control including deletion and configuration |
|
||||
|
||||
### Resources
|
||||
|
||||
Permissions can be scoped to individual resources:
|
||||
|
||||
- **Usage Dashboard** — Platform usage metrics and analytics
|
||||
- **Automations Dashboard** — Crew and flow management
|
||||
- **Environment Variables** — Secret and configuration management
|
||||
- **Individual Automations** — Per-automation access control
|
||||
|
||||
### Roles
|
||||
|
||||
- **Predefined roles** come out of the box with standard permission sets
|
||||
- **Custom roles** can be created with any combination of permissions
|
||||
- **Per-resource assignment** — limit specific automations to individual users or roles
|
||||
|
||||
### Factory Admin Access
|
||||
|
||||
For Factory deployments using Entra ID, admin access is controlled via App Roles:
|
||||
|
||||
- Assign the `factory-admin` role to users who need admin panel access
|
||||
- Assign the `member` role for standard platform access
|
||||
- Roles are communicated via JWT claims — no additional configuration needed after IdP setup
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Invalid Redirect URI
|
||||
|
||||
**Symptom:** Authentication fails with a redirect URI mismatch error.
|
||||
|
||||
**Fix:** Ensure the redirect URI in your IdP exactly matches the expected callback URL:
|
||||
|
||||
| Provider | Callback URL |
|
||||
|---|---|
|
||||
| Entra ID | `https://<domain>/auth/entra_id/callback` |
|
||||
| Okta | `https://<domain>/auth/okta/callback` |
|
||||
| Keycloak | `https://<domain>/auth/keycloak/callback` |
|
||||
| WorkOS | `https://<domain>/auth/workos/callback` |
|
||||
| Auth0 | `https://<domain>/auth/auth0/callback` |
|
||||
|
||||
### CLI Login Fails (Device Authorization)
|
||||
|
||||
**Symptom:** `crewai login` returns an error or times out.
|
||||
|
||||
**Fix:**
|
||||
- Verify that Device Authorization Grant is enabled in your IdP
|
||||
- For Okta: ensure you have a **Native Application** (not Web) with Device Authorization grant
|
||||
- For Entra ID: ensure **Allow public client flows** is enabled
|
||||
- For Keycloak: ensure the CLI client has **Client authentication: Off** and only Device Authorization Grant enabled
|
||||
- Check that `*_DEVICE_AUTHORIZATION_CLIENT_ID` environment variable is set on the server
|
||||
|
||||
### Token Validation Errors
|
||||
|
||||
**Symptom:** `Invalid token: Signature verification failed` or `401 Unauthorized` after login.
|
||||
|
||||
**Fix:**
|
||||
- **Okta:** Verify `OKTA_AUTHORIZATION_SERVER` and `OKTA_AUDIENCE` match the authorization server's Name and Audience exactly
|
||||
- **Entra ID:** Ensure `accessTokenAcceptedVersion` is set to `2` in the app manifest
|
||||
- **Keycloak:** Verify `KEYCLOAK_AUDIENCE` matches the audience in your access tokens (default: `account`)
|
||||
|
||||
### Admin Consent Not Granted (Entra ID)
|
||||
|
||||
**Symptom:** Users can't log in, see "needs admin approval" message.
|
||||
|
||||
**Fix:** Go to **Enterprise applications** → your app → **Permissions** → **Grant admin consent**. Ensure `User.Read` is granted for Microsoft Graph.
|
||||
|
||||
### 403 Forbidden After Login
|
||||
|
||||
**Symptom:** User authenticates successfully but gets 403 errors.
|
||||
|
||||
**Fix:**
|
||||
- Check that the user is assigned to the application in your IdP
|
||||
- For Entra ID with **Assignment required = Yes**: ensure the user has a role assignment (Member or Factory Admin)
|
||||
- For Okta: verify the user or their group is assigned under the app's **Assignments** tab
|
||||
|
||||
### CLI Can't Reach Factory Instance
|
||||
|
||||
**Symptom:** `crewai enterprise configure` fails to connect.
|
||||
|
||||
**Fix:**
|
||||
- Verify the Factory URL is reachable from your machine
|
||||
- Check that `enterprise_base_url` is set correctly: `crewai config list`
|
||||
- Ensure TLS certificates are valid and trusted
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
### Common
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `AUTH_PROVIDER` | Authentication provider: `entra_id`, `okta`, `workos`, `auth0`, `keycloak`, `local` |
|
||||
|
||||
### Microsoft Entra ID
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `ENTRA_ID_CLIENT_ID` | ✅ | Application (client) ID from Azure |
|
||||
| `ENTRA_ID_CLIENT_SECRET` | ✅ | Client secret from Azure |
|
||||
| `ENTRA_ID_TENANT_ID` | ✅ | Directory (tenant) ID from Azure |
|
||||
| `ENTRA_ID_DEVICE_AUTHORIZATION_CLIENT_ID` | CLI only | Client ID for Device Authorization Grant |
|
||||
| `ENTRA_ID_CUSTOM_OPENID_SCOPE` | CLI only | Custom scope from "Expose an API" (e.g., `api://crewai-cli/read`) |
|
||||
|
||||
### Okta
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `OKTA_CLIENT_ID` | ✅ | Okta application client ID |
|
||||
| `OKTA_CLIENT_SECRET` | ✅ | Okta client secret |
|
||||
| `OKTA_SITE` | ✅ | Okta organization URL (e.g., `https://your-domain.okta.com`) |
|
||||
| `OKTA_AUTHORIZATION_SERVER` | ✅ | Authorization server name (e.g., `default`) |
|
||||
| `OKTA_AUDIENCE` | ✅ | Authorization server audience (e.g., `api://default`) |
|
||||
| `OKTA_DEVICE_AUTHORIZATION_CLIENT_ID` | CLI only | Native app client ID for Device Authorization |
|
||||
|
||||
### WorkOS
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `WORKOS_CLIENT_ID` | ✅ | WorkOS application client ID |
|
||||
| `WORKOS_AUTHKIT_DOMAIN` | ✅ | AuthKit domain (e.g., `your-domain.authkit.com`) |
|
||||
|
||||
### Auth0
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `AUTH0_CLIENT_ID` | ✅ | Auth0 application client ID |
|
||||
| `AUTH0_CLIENT_SECRET` | ✅ | Auth0 client secret |
|
||||
| `AUTH0_DOMAIN` | ✅ | Auth0 tenant domain (e.g., `your-tenant.auth0.com`) |
|
||||
|
||||
### Keycloak
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `KEYCLOAK_CLIENT_ID` | ✅ | Keycloak client ID |
|
||||
| `KEYCLOAK_CLIENT_SECRET` | ✅ | Keycloak client secret |
|
||||
| `KEYCLOAK_SITE` | ✅ | Keycloak server URL |
|
||||
| `KEYCLOAK_REALM` | ✅ | Keycloak realm name |
|
||||
| `KEYCLOAK_AUDIENCE` | ✅ | Token audience (default: `account`) |
|
||||
| `KEYCLOAK_BASE_URL` | Optional | Base URL path (e.g., `/auth` for pre-v17 migrations) |
|
||||
| `KEYCLOAK_DEVICE_AUTHORIZATION_CLIENT_ID` | CLI only | Public client ID for Device Authorization |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Installation Guide](/installation) — Get started with CrewAI
|
||||
- [Quickstart](/quickstart) — Build your first crew
|
||||
- [RBAC Setup](/enterprise/features/rbac) — Detailed role and permission management
|
||||
@@ -1021,7 +1021,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return structured_response
|
||||
|
||||
@@ -1030,7 +1030,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return full_response
|
||||
|
||||
@@ -1045,7 +1045,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return full_response
|
||||
|
||||
@@ -1066,7 +1066,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return full_response
|
||||
|
||||
@@ -1217,7 +1217,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=messages,
|
||||
)
|
||||
return structured_response
|
||||
|
||||
@@ -1258,7 +1258,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return structured_response
|
||||
|
||||
@@ -1289,7 +1289,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return text_response
|
||||
|
||||
@@ -1312,7 +1312,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return text_response
|
||||
|
||||
@@ -1361,7 +1361,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=messages,
|
||||
)
|
||||
return structured_response
|
||||
|
||||
@@ -1396,7 +1396,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return structured_response
|
||||
|
||||
@@ -1425,7 +1425,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return text_response
|
||||
|
||||
@@ -1447,7 +1447,7 @@ class LLM(BaseLLM):
|
||||
call_type=LLMCallType.LLM_CALL,
|
||||
from_task=from_task,
|
||||
from_agent=from_agent,
|
||||
messages=params["messages"],
|
||||
messages=params.get("messages", []),
|
||||
)
|
||||
return text_response
|
||||
|
||||
|
||||
@@ -1024,3 +1024,109 @@ async def test_usage_info_streaming_with_acall():
|
||||
assert llm._token_usage["total_tokens"] > 0
|
||||
|
||||
assert len(result) > 0
|
||||
|
||||
|
||||
def test_non_streaming_response_no_keyerror_when_messages_missing_from_params():
|
||||
"""Test that _handle_non_streaming_response does not raise KeyError when
|
||||
params dict lacks a 'messages' key. Covers the fix for issue #5164."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
mock_message = MagicMock()
|
||||
mock_message.content = "Test response"
|
||||
mock_message.tool_calls = []
|
||||
mock_choice = MagicMock()
|
||||
mock_choice.message = mock_message
|
||||
mock_response = MagicMock()
|
||||
mock_response.choices = [mock_choice]
|
||||
mock_response.usage = MagicMock()
|
||||
|
||||
with patch("litellm.completion", return_value=mock_response):
|
||||
# Pass params WITHOUT "messages" key — before the fix this raised KeyError
|
||||
result = llm._handle_non_streaming_response(params={"model": "gpt-4o-mini"})
|
||||
|
||||
assert result == "Test response"
|
||||
|
||||
|
||||
def test_non_streaming_response_uses_validated_messages_for_litellm_response_model():
|
||||
"""Test that _handle_non_streaming_response uses the locally validated
|
||||
'messages' variable (not params['messages']) in the response_model + is_litellm
|
||||
branch. Covers the fix for issue #5164."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
class DummyModel(BaseModel):
|
||||
answer: str
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
params = {"model": "gpt-4o-mini", "messages": messages}
|
||||
|
||||
mock_result = MagicMock()
|
||||
mock_result.model_dump_json.return_value = '{"answer": "ok"}'
|
||||
|
||||
with patch(
|
||||
"crewai.utilities.internal_instructor.InternalInstructor"
|
||||
) as MockInstructor:
|
||||
instance = MockInstructor.return_value
|
||||
instance.to_pydantic.return_value = mock_result
|
||||
|
||||
result = llm._handle_non_streaming_response(
|
||||
params=params, response_model=DummyModel
|
||||
)
|
||||
|
||||
assert result == '{"answer": "ok"}'
|
||||
|
||||
|
||||
def test_non_streaming_response_with_response_model_no_keyerror():
|
||||
"""Test that _handle_non_streaming_response does not raise KeyError
|
||||
in the response_model + is_litellm branch when messages key is missing.
|
||||
Before the fix, this would raise KeyError at the _handle_emit_call_events call."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
class DummyModel(BaseModel):
|
||||
answer: str
|
||||
|
||||
# No "messages" key in params — should raise ValueError, not KeyError
|
||||
params = {"model": "gpt-4o-mini"}
|
||||
|
||||
with pytest.raises(ValueError, match="Messages are required"):
|
||||
llm._handle_non_streaming_response(params=params, response_model=DummyModel)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_non_streaming_response_no_keyerror_when_messages_missing():
|
||||
"""Test that _ahandle_non_streaming_response does not raise KeyError when
|
||||
params dict lacks a 'messages' key. Covers the async fix for issue #5164."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
mock_message = MagicMock()
|
||||
mock_message.content = "Async response"
|
||||
mock_message.tool_calls = []
|
||||
mock_choice = MagicMock()
|
||||
mock_choice.message = mock_message
|
||||
mock_response = MagicMock()
|
||||
mock_response.choices = [mock_choice]
|
||||
mock_response.usage = MagicMock()
|
||||
|
||||
with patch("litellm.acompletion", return_value=mock_response):
|
||||
result = await llm._ahandle_non_streaming_response(
|
||||
params={"model": "gpt-4o-mini"}
|
||||
)
|
||||
|
||||
assert result == "Async response"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_non_streaming_response_with_response_model_no_keyerror():
|
||||
"""Test that _ahandle_non_streaming_response does not raise KeyError
|
||||
in the response_model + is_litellm branch when messages key is missing.
|
||||
Covers the async fix for issue #5164."""
|
||||
llm = LLM(model="gpt-4o-mini", is_litellm=True)
|
||||
|
||||
class DummyModel(BaseModel):
|
||||
answer: str
|
||||
|
||||
params = {"model": "gpt-4o-mini"}
|
||||
|
||||
with pytest.raises(ValueError, match="Messages are required"):
|
||||
await llm._ahandle_non_streaming_response(
|
||||
params=params, response_model=DummyModel
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user