Skip to content

Okta

Private Beta

Cloud Security is currently in Private Beta. Features, APIs, and configuration formats described here may change before general availability. Contact us if you would like access.

Collects the Okta directory as identity posture: users (with MFA factors and admin roles), groups and membership, application inventory and per-user app assignments, and external identity providers (federation / social trust). Optionally, org API-token hygiene and the AI-agent registry.

Auth model: an Okta API Services app integration using OAuth 2.0 client-credentials with private-key JWT. A user-owned SSWS API token is also supported, but the service app is strongly preferred — it has no owning person, no password/MFA lifecycle, and no 30-day-inactivity expiry.

Prerequisites

  • Super Administrator access to the Okta org (creating an API Services app and assigning it an admin role both require it).
  • Your org base URL, e.g. https://example.okta.com. Use the URL you sign in to; note that an org's name and its subdomain can differ.

Required scopes

Granted on the app's Okta API Scopes tab:

Scope Why Preflight check
okta.users.read The user directory — the core identity inventory users
okta.groups.read Groups and membership groups
okta.apps.read Application inventory and assignments apps
okta.roles.read Admin-role enrichment on every user roles
okta.idps.read External identity providers (federation/social trust) idps

okta.roles.read is not optional

The user collector enriches every user with their admin roles inline and treats a roles denial as fatal. Without this scope no users are inventoried at all — not merely "users without roles".

Optional scopes, requested only when you list them explicitly (see Requesting extra scopes):

Scope Unlocks Preflight check
okta.apiTokens.read Org API-token (standing SSWS credential) hygiene api_tokens
okta.aiAgents.read The Okta for AI Agents registry — source-asserted AI-agent classification. Requires the paid Okta for AI Agents subscription ai_agents

Create the API Services app

  1. Admin console → Applications → Applications → Create App Integration → API Services. Name it (e.g. LimaCharlie Cloud Security) and save.
  2. On the app's General tab, set Client authentication to Public key / Private key.
  3. Under PUBLIC KEYS, choose Add key → Generate new key, then copy the private key in JWK format. This is shown once — keep it.
  4. On the Okta API Scopes tab, Grant each of the five required scopes (plus any optional ones you want).
  5. On the app's Admin roles tab, assign an admin roleRead-only Administrator is sufficient for the required scope set.
  6. Copy the app's Client ID from the General tab.

Scopes and an admin role are both required

An API Services app with the scopes granted but no admin role assigned authenticates fine and then 403s on groups and per-user roles. Granting a .read scope does not by itself confer directory read access.

Private-key JWT is mandatory

Okta's org authorization server rejects client-secret authentication for okta.* scopes — it requires private_key_jwt. If you configure the app with a client secret, token minting fails with "Client Credentials requests to the Org Authorization Server must use the private_key_jwt token_endpoint_auth_method". Use Public key / Private key.

DPoP is handled for you

On newly created API Services apps you may find the sender-constrained tokens (DPoP) toggle switched on and not editable. This is not a misconfiguration and needs no credential field: when the token endpoint requires DPoP, the collector detects it and switches to DPoP automatically.

Create the credentials secret

API Services app (recommended) — paste the private JWK as a nested JSON object, no string-escaping needed:

{
  "org_url": "https://example.okta.com",
  "client_id": "<app-client-id>",
  "private_key": { "kty": "RSA", "kid": "...", "n": "...", "e": "AQAB", "d": "...", "p": "...", "q": "..." }
}

A PEM string is also accepted for private_key; in that case add "key_id" with the key's kid.

SSWS token (alternative):

{"org_url": "https://example.okta.com", "api_token": "<SSWS-token>"}

Store it:

limacharlie hive set --hive-name secret --key okta-credentials \
    --input-file okta-secret.json --enabled

Requesting extra scopes

By default the collector requests exactly the five required scopes. To use an optional scope, grant it on the app and list the full scope set in the secret:

{
  "org_url": "https://example.okta.com",
  "client_id": "<app-client-id>",
  "private_key": { "...": "..." },
  "scopes": ["okta.users.read", "okta.roles.read", "okta.groups.read",
             "okta.apps.read", "okta.idps.read", "okta.apiTokens.read"]
}

scopes replaces the default set

The list is used verbatim, and every scope in it must be granted on the app or the token mint fails outright. Always include the five required scopes alongside the optional ones.

Create the provider record

provider.yaml:

provider_type: okta
okta_org_url: "https://example.okta.com"
credentials: hive://secret/okta-credentials
internal_domains: [example.com]
refresh: 6h

In the web app: Add provider → Okta, then set Org URL, Credentials, and Refresh interval.

Verify

limacharlie cloudsec provider test --input-file provider.yaml
Check Required Meaning if it fails
auth The credential was rejected outright (bad key, wrong client ID, wrong org URL). Nothing else is probed.
users okta.users.read denied — no identity inventory.
groups okta.groups.read denied — no groups or membership edges.
apps okta.apps.read denied — no application inventory or app-access edges.
roles okta.roles.read denied — no users are inventoried (role enrichment is fatal to the user collector).
idps External identity-provider posture unavailable.
api_tokens Per-user API-token hygiene unavailable.
ai_agents Source-asserted AI-agent classification unavailable (needs Okta for AI Agents + okta.aiAgents.read).

401 vs 403

A 401 means the credential itself was rejected — the report short-circuits to a single failed auth check. A 403 means you authenticated but lack that scope or admin permission, so auth passes and only that surface fails.

Troubleshooting

provider test result Cause Fix
auth fails: must use the private_key_jwt token_endpoint_auth_method App configured with a client secret Switch the app to Public key / Private key and store the private JWK
groups / roles 403 while auth passes No admin role assigned to the app Assign Read-only Administrator on the app's Admin roles tab
A scope you granted still 403s The org authorization server silently drops ungranted scopes at mint — the token simply lacks it Re-check the Okta API Scopes tab; the 403's WWW-Authenticate header names the missing scope
Token mint fails after adding scopes A listed scope is not granted on the app Grant it, or remove it from the list
auth fails: host unreachable Wrong subdomain (org name ≠ org subdomain) Use the exact URL you sign in to
SSWS token stops working SSWS tokens expire after 30 days of inactivity and die with their owner Move to an API Services app