Skip to main content

Overview

There are two ways of authenticating with automations: static bearer token (default) or OAuth 2.0 Client Credentials. For enhanced security, Factory customers may switch to OAuth 2.0 Client Credentials, a machine-to-machine authentication flow in which access tokens are issued by an external identity provider (e.g. Okta, Auth0, Keycloak) and presented to the Automation API. The API validates each token and authorizes requests based on its defined scopes.

Authentication Flow

Setup

To enable this feature, you’ll need to configure your Identity Provider as follows. There are many ways you can configure this based on your Identity Provider. Here are two examples: Client Secret–based Client Authentication (most common) and Private Key JWT Client Authentication.

Client Secret-based Client Authentication (most common)

Step 1: Create Authorization Server

Create an authorization server with audience set to CrewAI AOP URL (e.g. https://crewai.yourdomain.test). If using Okta, follow: Okta Admin Console > Security > API > Add Authorization Server.

Step 2: Create Scope

Under your authorization server settings, create a scope named crewai_automations. If using Okta, follow: Okta Admin Console > Security > API > Select your authorization server > Scopes > Add Scope.

Step 3: Create Access Policy (Okta)

If using Okta, create an access policy to ensure clients can use this authorization server: Okta Admin Console > Security > API > Select your authorization server > Access Policies > Add New Access Policy. Be sure to create at least one rule in your recently created access policy so clients can access it.

Step 4: Enable in CrewAI AOP

On CrewAI AOP, go to Settings > Organization > Toggle OAuth 2.0 for Automations, and fill in your newly created authorization server information.

Step 5: Request Access Token

Use your identity provider to issue a new access token with access to the crewai_automations scope:
curl --request POST \
  --url https://identityprovider.test/oauth2/your-authorization-server/v1/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --user 'your_client_id:your_client_secret' \
  --data 'grant_type=client_credentials' \
  --data 'scope=crewai_automations'

# Response:
# {"token_type":"Bearer","expires_in":3600,"access_token":"eyJraWQiOiI4aldraVl0ZHprZ2pubVIxeEp0WklhSFRUN1BjcUJ1QTU4OHdJaEV3dUIwIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULlN5c3h5WEtTOEd6UWRvS3l6N09kd3hzcnZzeHZqcWZHZHgtTENvbmt1czAiLCJpc3MiOiJodHRwczovL3RyaWFsLTU4NjU0OTAub2t0YS5jb20vb2F1dGgyL2F1c3g2aWVldGlSak5iWDhvNjk3IiwiYXVkIjoiaHR0cHM6Ly9hcHAuY3Jld2FpLmNvbS8iLCJpYXQiOjE3NjI4ODc5MjAsImV4cCI6MTc2Mjg5MTUyMCwiY2lkIjoiMG9heDZlMzZiczlzUlo5dWU2OTciLCJzY3AiOlsiY3Jld2FpX2F1dG9tYXRpb25zIl0sInN1YiI6IjBvYXg2ZTM2YnM5c1JaOXVlNjk3In0.oQwiacyYbpUIB9n1vNQhL-rgXjhtJz7Cq-rIrSv4Q01THTWd60JxgnWZ6iTSQMHytkmsxGQFZDVR63H_SrMQM2h1mKInGv9rX0529LB5XCZ6B-R7cGJRx63OHtVnWcgur9Ebhb3fS7Lkl_3V8xKLSMJlYD4iLjIoxIyrjggKD1TJhmXrm1ZxfjqXCYh2g0ttkNqol_lPT_OCUVFiwGPvJ1YlODOl2WZ_IbP9r6cCpBonbY8iiKFLrSGi0_3qYjlx1WJ9IxnnulsjL5aRUEoUHHIrUdtSLLVwP7FXxoxkhJp-w7RmjaBNjyvdDIk9XsL1DMbMcmr0Aa-Dfldcc6Ts9Q","scope":"crewai_automations"}

Step 6: Authenticate to Automation

Use the generated access token to authenticate to your automation:
curl --request POST \
  --url https://crewai.test/crewai_plus/api/v1/crew/fad205cd-e85f-4222-828d-64c590dcfa76/kickoff \
  --data '{"inputs": {"topic": "ai agents"}}' \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer eyJraWQiOiI4aldraVl0ZHprZ2pubVIxeEp0WklhSFRUN1BjcUJ1QTU4OHdJaEV3dUIwIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULndhMkJRQl9DS0YzazlmYXlrZ0x2MVVfT3Z5UjVtM2N1bWh3ekpNRGF1bkUiLCJpc3MiOiJodHRwczovL3RyaWFsLTU4NjU0OTAub2t0YS5jb20vb2F1dGgyL2F1c3g2aWVldGlSak5iWDhvNjk3IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImlhdCI6MTc2Mjg4OTA2NywiZXhwIjoxNzYyODkyNjY3LCJjaWQiOiIwb2F4NmUzNmJzOXNSWjl1ZTY5NyIsInNjcCI6WyJjcmV3YWlfYXV0b21hdGlvbnMiXSwic3ViIjoiMG9heDZlMzZiczlzUlo5dWU2OTcifQ.VS5hYFmyPL_ajlhcPDetp7dU8QfVMFu7XeFs_8tY4LORBOrqFuYLgWfbJr2WCBF1x-IkJe_BpO44GzQwf_psn4ED6TNdWjIpQ6TgCaubVn4rA9pHnn8FftpOf_2UvJCoIMUW-Vnr49zRNaU_tOLaJq1QQlS09zhvkgcKX1-ky9pjNaRQLGkfXPiNCmgMsv25JohZnv_aY8abmpGbqWkop5eXmh1_rRlV53mXOviqeyr_NqOtgeC1Qav3oGFYmxyhLlLPFThtPvg6PBEymLp-l5KFmoOie5vADfC9M8zXcipzFClpBuEN05dFRYyt8Ao8bWUQLiXwCACYlS6rPxMrFA"

Private Key JWT Client Authentication

Step 1: Create App Integration

Create an app integration in Okta: Okta > Applications > Create App Integration > Select the API Services sign-in method > Give it a name > Save.

Step 2: Configure Client Authentication

On the app integration’s General settings, go to Client Credentials > Client authentication > Select “Public key / Private key”. In that same section, choose “Save keys in Okta” > Add key > Generate new key. Store the private key securely in .pem format, then click Save.

Step 3: Create Authorization Server

Create an authorization server with audience set to CrewAI AOP URL (e.g. https://crewai.yourdomain.test). If using Okta, follow: Okta Admin Console > Security > API > Add Authorization Server.

Step 4: Create Scope

Under your authorization server settings, create a scope named crewai_automations. If using Okta, follow: Okta Admin Console > Security > API > Select your authorization server > Scopes > Add Scope.

Step 5: Create Access Policy (Okta)

If using Okta, create an access policy to ensure clients can use this authorization server: Okta Admin Console > Security > API > Select your authorization server > Access Policies > Add New Access Policy. Be sure to create at least one rule in your recently created access policy so clients can access it.

Step 6: Enable in CrewAI AOP

On CrewAI AOP, go to Settings > Organization > Toggle OAuth 2.0 for Automations, and fill in your newly created authorization server information.

Step 7: Generate Signed JWT

Generate a signed JWT using your private key. The following is a helper script example that demonstrates how to create the client assertion JWT required for authentication:
import jwt
import uuid
import time

OKTA_DOMAIN = "your_okta_domain"
CLIENT_ID = "your_client_id" # You can get this from your Okta App Integration settings.
KID = "your_public_key_id" # You can get this from your Okta App Integration settings.
AUTH_SERVER_ID = "your_authorization_server_id" # You can get this from your Okta Authorization Server.
PATH_TO_PRIVATE_KEY = "/path/to/private/key.pem"

TOKEN_ENDPOINT = f"{OKTA_DOMAIN}/oauth2/{AUTH_SERVER_ID}/v1/token"

with open(PATH_TO_PRIVATE_KEY, "rb") as f:
    private_key = f.read()

now = int(time.time())

payload = {
    "iss": CLIENT_ID,
    "sub": CLIENT_ID,
    "aud": TOKEN_ENDPOINT,
    "exp": now + 300,
    "iat": now,
    "jti": str(uuid.uuid4()),
}

headers = {
    "alg": "RS256",
    "kid": KID,
    "typ": "JWT",
}

client_assertion = jwt.encode(
    payload,
    private_key,
    algorithm="RS256",
    headers=headers,
)

print(client_assertion)

Step 8: Generate Access Token

Use your identity provider to issue a new access token with access to the crewai_automations scope, using the signed JWT from the last step:
curl -X POST https://YOURACCOUNT.okta.com/oauth2/YOUR_AUTHORIZATION_SERVER_ID/v1/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=client_credentials \
  -d client_id="YOUR_CLIENT_ID" \
  -d scope="crewai_automations" \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  -d client_assertion="YOUR_GENERATED_SIGNED_JWT"

# Response:
# {"token_type":"Bearer","expires_in":3600,"access_token":"eyJraWQiOiI4aldraVl0ZHprZ2pubVIxeEp0WklhSFRUN1BjcUJ1QTU4OHdJaEV3dUIwIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULlN5c3h5WEtTOEd6UWRvS3l6N09kd3hzcnZzeHZqcWZHZHgtTENvbmt1czAiLCJpc3MiOiJodHRwczovL3RyaWFsLTU4NjU0OTAub2t0YS5jb20vb2F1dGgyL2F1c3g2aWVldGlSak5iWDhvNjk3IiwiYXVkIjoiaHR0cHM6Ly9hcHAuY3Jld2FpLmNvbS8iLCJpYXQiOjE3NjI4ODc5MjAsImV4cCI6MTc2Mjg5MTUyMCwiY2lkIjoiMG9heDZlMzZiczlzUlo5dWU2OTciLCJzY3AiOlsiY3Jld2FpX2F1dG9tYXRpb25zIl0sInN1YiI6IjBvYXg2ZTM2YnM5c1JaOXVlNjk3In0.oQwiacyYbpUIB9n1vNQhL-rgXjhtJz7Cq-rIrSv4Q01THTWd60JxgnWZ6iTSQMHytkmsxGQFZDVR63H_SrMQM2h1mKInGv9rX0529LB5XCZ6B-R7cGJRx63OHtVnWcgur9Ebhb3fS7Lkl_3V8xKLSMJlYD4iLjIoxIyrjggKD1TJhmXrm1ZxfjqXCYh2g0ttkNqol_lPT_OCUVFiwGPvJ1YlODOl2WZ_IbP9r6cCpBonbY8iiKFLrSGi0_3qYjlx1WJ9IxnnulsjL5aRUEoUHHIrUdtSLLVwP7FXxoxkhJp-w7RmjaBNjyvdDIk9XsL1DMbMcmr0Aa-Dfldcc6Ts9Q","scope":"crewai_automations"}

Step 9: Authenticate to Automation

Use the generated access token to authenticate to your automation:
curl --request POST \
  --url https://crewai.test/crewai_plus/api/v1/crew/fad205cd-e85f-4222-828d-64c590dcfa76/kickoff \
  --data '{"inputs": {"topic": "ai agents"}}' \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer eyJraWQiOiI4aldraVl0ZHprZ2pubVIxeEp0WklhSFRUN1BjcUJ1QTU4OHdJaEV3dUIwIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULndhMkJRQl9DS0YzazlmYXlrZ0x2MVVfT3Z5UjVtM2N1bWh3ekpNRGF1bkUiLCJpc3MiOiJodHRwczovL3RyaWFsLTU4NjU0OTAub2t0YS5jb20vb2F1dGgyL2F1c3g2aWVldGlSak5iWDhvNjk3IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImlhdCI6MTc2Mjg4OTA2NywiZXhwIjoxNzYyODkyNjY3LCJjaWQiOiIwb2F4NmUzNmJzOXNSWjl1ZTY5NyIsInNjcCI6WyJjcmV3YWlfYXV0b21hdGlvbnMiXSwic3ViIjoiMG9heDZlMzZiczlzUlo5dWU2OTcifQ.VS5hYFmyPL_ajlhcPDetp7dU8QfVMFu7XeFs_8tY4LORBOrqFuYLgWfbJr2WCBF1x-IkJe_BpO44GzQwf_psn4ED6TNdWjIpQ6TgCaubVn4rA9pHnn8FftpOf_2UvJCoIMUW-Vnr49zRNaU_tOLaJq1QQlS09zhvkgcKX1-ky9pjNaRQLGkfXPiNCmgMsv25JohZnv_aY8abmpGbqWkop5eXmh1_rRlV53mXOviqeyr_NqOtgeC1Qav3oGFYmxyhLlLPFThtPvg6PBEymLp-l5KFmoOie5vADfC9M8zXcipzFClpBuEN05dFRYyt8Ao8bWUQLiXwCACYlS6rPxMrFA"