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¶
- Admin console → Applications → Applications → Create App Integration →
API Services. Name it (e.g.
LimaCharlie Cloud Security) and save. - On the app's General tab, set Client authentication to Public key / Private key.
- Under PUBLIC KEYS, choose Add key → Generate new key, then copy the private key in JWK format. This is shown once — keep it.
- On the Okta API Scopes tab, Grant each of the five required scopes (plus any optional ones you want).
- On the app's Admin roles tab, assign an admin role — Read-only Administrator is sufficient for the required scope set.
- 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):
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¶
| 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 |