At a glance

VendorOkta Workforce Identity
Source typeidentity
Vendor ID (slug)okta
Base URLPer-tenant — https://<your-org>.okta.com. Preview orgs use .oktapreview.com.
Auth methodapi-keyAuthorization: SSWS <token>. The connector overrides the default Bearer prefix for Okta.
Schedule defaultdaily — the 24h KRIs (impossible travel, MFA push, admin sessions) re-window every run.
Recommended forOkta tenants with Behavior Detection enabled — the impossible-travel KRI relies on Okta tagging events with the "Velocity" behavior.
AvailabilityNew in 2026.04.

Required scopes & roles

Draxis authenticates with an Okta SSWS API token issued to a dedicated service-account admin user. That user's admin role is the actual permission boundary — the token inherits it exactly. Use:

  • Read-Only Administrator (Okta's built-in role) — covers every API the connector calls: Users (list + factors + appLinks), Apps, Policies (ACCESS_POLICY type), and the System Log.
  • If you prefer a custom admin role, grant read on: Users, Groups, Apps, Policies, and System Log. Do not grant any manage permission.

Do not use a Super Administrator account to mint the token. Super Admin's permissions are too broad and the token inherits all of them — if it ever leaks, the blast radius is your entire Okta org. Read-Only Administrator keeps the blast radius to read-only.

Setup steps

  1. Create a dedicated service-account user in the Okta Admin Console (Directory → People → Add Person). Name it draxis-connector@your-domain.com or similar — it should be obvious in audit logs who's doing what. Set a long random password. Disable self-service password reset on this user.
  2. Assign the Read-Only Administrator role. Security → Administrators → Add Administrator. Pick the service-account user. Role: Read-Only Administrator. Scope: All resources (the KRI calculations span users, apps, policies, and logs — scoping reduces signal without materially improving security vs. the read-only role).
  3. Enable Okta Behavior Detection (optional but recommended). Security → General → Behavior Detection. Turn on Velocity at minimum — that's what populates the impossible-travel KRI. New-Device and New-Geo-Location are nice to have.
  4. Mint the SSWS API token. Sign in as the service account (not your personal admin), go to Security → API → Tokens → Create Token. Name it draxis-connector. Copy the token value — you cannot retrieve it later. Tokens inherit the role of the user who mints them, which is why step 1 matters: minting with your personal Super Admin account would give the token Super Admin reach.
  5. Note your org URL. Your Okta sign-in URL is https://<your-org>.okta.com — the part before .okta.com is your org slug. Preview/sandbox orgs use .oktapreview.com and Okta's EU customer cells use .okta-emea.com. You'll paste the full origin (scheme + host, no path) into Draxis as the API Base URL.

Wire it into Draxis

  1. Open Settings → Integrations in your tenant.
  2. Click Add integration and pick Identity Provider as the source type.
  3. Pick Okta (Workforce Identity) from the vendor dropdown. Draxis pre-fills the auth method (API Key) and daily schedule.
  4. In API Base URL, paste your org URL (e.g. https://acme.okta.com). No trailing slash; the connector adds /api/v1/... paths itself.
  5. In API Key / Token, paste the SSWS token from step 4. Draxis encrypts it server-side with encryption.key before storage.
  6. Click Test. Green means Draxis reached /api/v1/users?limit=1 and authenticated successfully.
  7. Under KRIs to import, tick the KRIs you want Draxis to manage. All seven okta_* KRIs are checked by default; uncheck any you don’t need (e.g. drop the impossible-travel KRI if your tenant has Behavior Detection disabled — see Quirks). Selected rows are created on save with the seeded thresholds. Unchecking a previously-imported KRI deletes it on save.
  8. Save. The connector runs daily by default; Run now from run history triggers an immediate sync.

KRIs produced

SlugMeaningDerivation
okta_mfa_adoption_pct % of sampled active users enrolled in at least one real MFA factor For each user in GET /users?filter=status eq "ACTIVE" (capped at 2000 by default), fetch /users/{id}/factors and count users with at least one status=ACTIVE factor that is not password or question. Value = round(enrolled / sampled * 100, 1).
okta_phish_resistant_mfa_pct % of MFA-enrolled users whose enrollment includes a phishing-resistant factor Of the users counted as MFA-enrolled, count those with at least one factor where factorType in ("webauthn","u2f") or provider == "FIDO". Value = round(phish_resistant / mfa_enrolled * 100, 1).
okta_inactive_users_with_app_access Suspended users still holding at least one app assignment For each user from GET /users?filter=status eq "SUSPENDED" (capped at 500), fetch /users/{id}/appLinks and count those with a non-empty response.
okta_impossible_travel_24h System Log events tagged with Behavior Detection's "Velocity" anomaly in the last 24h GET /logs?since=<now-24h>&limit=1000 (paginated via Link headers), filter events where debugContext.debugData.behaviors contains Velocity (case-insensitive).
okta_admin_session_outliers_24h Admin sessions (in the 24h window) that lasted more than 8 hours Pair user.session.access_admin_app events with matching user.session.end events by authenticationContext.externalSessionId; count sessions where end - start > 8h. Sessions without a captured end don't count as outliers.
okta_failed_mfa_push_24h Okta Verify push denials in the last 24 hours — MFA-fatigue signal count(logs where eventType == "user.mfa.okta_verify.deny_push" and published > now-24h)
okta_app_policy_exceptions Active ACCESS_POLICY rules that ALLOW access without requiring MFA GET /policies?type=ACCESS_POLICY, then for each policy GET /policies/{id}/rules. Count rules where status == "ACTIVE" and actions.appSignOn.access == "ALLOW" and either verificationMethod is missing or factorMode == "1FA".

Each row is a slug the connector writes to. Draxis creates the matching kri rows automatically when you check them in the KRIs to import section of the integration form — no manual API call or seed script needed. Thresholds shown in the table are the seeded defaults; you can edit them freely in the KRIs tab afterwards.

Vendor quirks

  • Authorization header is SSWS, not Bearer. Okta API tokens predate Bearer convention. The connector overrides Draxis's default api-key header per request; no configuration needed.
  • "Velocity" is Okta's term for impossible travel. It only shows up in System Log events if Behavior Detection is enabled (step 3 above). If you see the KRI flatlined at 0, Behavior Detection is likely off — not that you have no impossible-travel events.
  • MFA adoption is sampled, not exhaustive. The connector fetches factors one user at a time; caps at 2000 users by default to stay comfortably under Okta's per-endpoint rate limits (600 req/min is the most common ceiling). For tenants with >2000 users, override by saving {"user_sample_cap":5000} in Extra Config — the run will just take longer.
  • Phishing-resistant KRI denominator is MFA-enrolled users, not all users. So a tenant with 10% MFA adoption and 100% of those using WebAuthn reads as okta_phish_resistant_mfa_pct = 100, which is technically accurate but misleading. Read it alongside okta_mfa_adoption_pct for a full picture.
  • Admin session outliers only count sessions that ended within the window. If a session started at T-9h and is still live at run time, we don't know it lasted >8h yet. The next day's run will catch it if the session ends then. This is why we report it as "outliers closed in the last 24h" rather than "admin sessions over 8h."
  • Policy exceptions count rules, not users. One ALLOW-without-MFA rule affecting 5000 users and 500 apps still counts as 1. Use the threshold as a rule-count boundary (e.g. “no more than two exceptions”), not a user-count boundary.
  • SSWS tokens inherit the minting user's role at creation time and never change. If you later upgrade the service account to Super Admin, the existing token stays read-only. If you downgrade, the existing token still retains the old role. Rotate the token after any role change.
  • Token rotation is mandatory. Okta expires SSWS tokens after 30 days of inactivity. Because the connector runs daily, that doesn't happen in practice — but if you disable the integration for a month, the token will silently stop working. Either keep the schedule active or budget for a rotation.

Troubleshooting

  • HTTP 401 with E0000011: Invalid token provided — the SSWS value is wrong, expired (30d inactivity), or the service account was deactivated. Mint a new token from the service account.
  • HTTP 403 with E0000006: You do not have permission — the service account's role doesn't include the endpoint the connector hit. Re-check that the user has Read-Only Administrator (or a custom role that reads users, apps, policies, and logs).
  • HTTP 429 on run — you've hit Okta's per-minute rate limit. The connector doesn't retry internally; the run errors. Lower the sample cap via Extra Config, or move the schedule to hourly (smaller chunks) — or open a support request if you want us to add exponential backoff.
  • okta_impossible_travel_24h is always 0 — Behavior Detection is probably disabled. Enable it in Security → General → Behavior Detection, turn on Velocity, and the next run will populate.
  • okta_mfa_adoption_pct is suspiciously low — the sample cap (default 2000) is hitting your userbase's tail. Raise it via Extra Config JSON: {"user_sample_cap":10000}.
  • okta_app_policy_exceptions seems high — it counts every active ACCESS_POLICY rule where an ALLOW action doesn't require MFA. If your org uses a lot of "allow if device is managed" style rules without explicit MFA verification, those count. Review them in the Okta Admin Console — device-trust alone isn't phishing-resistant.
  • rowsSkipped > 0 and rowsWritten = 0 — your tenant hasn't wired KRI rows matching the connector's slugs yet. Open the integration in Settings → Integrations, check the KRIs you want under "KRIs to import", and save.
  • Still stuck? Open a support ticket with the run ID (from Run history) and we'll dig in.