Okta Setup for dfe-fetcher
What an Okta administrator needs to configure so dfe-fetcher can read System Log events from an Okta org.
Overview
dfe-fetcher pulls events from the Okta System Log API at
{tenant_url}/api/v1/logs, one Okta org per fetcher instance. It requests a
time-bounded, ascending window (since / until / limit /
sortOrder=ASCENDING) and pages through the RFC 5988 Link: rel="next"
header. An optional service-config filter (Okta OData syntax) narrows
results server-side. Authentication is one of three schemes:
use_ssws_header: true(default on thesources.oktablock) - sendsAuthorization: SSWS <token>using a legacy Okta API token.use_ssws_header: false- sendsAuthorization: Bearer <token>using an OAuth 2.0 access token someone else minted with theokta.logs.readscope.- An API Services app and its key pair, configured as a
sources.restinstance of the same profile: the fetcher mints its own access token, signing a private-key JWT for the client-credentials exchange.
Okta recommends OAuth 2.0 over SSWS for management APIs, and the service-app
key pair over both: an SSWS token is static and carries the permissions of the
admin who minted it, where a service app is granted okta.logs.read alone and
the private half of its key never leaves the deployment. SSWS remains supported
and is the simplest path.
The source is the shipped okta REST profile
(crates/fetcher/profiles/okta.yaml); the sources.okta block below maps
onto an instance of it at load, so the profile's retry policy applies: a 429
or 5xx is retried with backoff (honouring Retry-After), a 401 or 403 ends
the tick, and a tick that fails does not advance the fetch window.
Prerequisites
- Any Okta org (Workforce or Customer Identity). The System Log API is available on all tiers; no add-on is required.
- An administrator account to create the credential:
- SSWS path: an admin who can mint API tokens. The token inherits that admin's privileges, so use a dedicated service account whose role is stable (a deactivated creator deprovisions the token).
- OAuth path: an admin who can create an API Service / OAuth service app
and grant it the
okta.logs.readscope.
- Least-privilege option: the standard Read-only Administrator role can view System Log data and manage its own API token; or use a custom admin role carrying the System Log query permission.
Required Permissions
| Service | Endpoint | Permission or Scope | Notes |
|---|---|---|---|
system_log | GET {tenant_url}/api/v1/logs | SSWS token from a service account with read access to System Log | Token inherits the creating admin's privileges; Read-only Admin or System Log query custom role is sufficient. |
system_log | GET {tenant_url}/api/v1/logs | OAuth scope okta.logs.read | Granted to an API Service app using the client-credentials flow. |
All access is read-only.
Source-Side Setup
Option A - SSWS API token (simplest)
-
Sign in to the Admin Console as the service-account admin.
-
Go to Security -> API, open the Tokens tab, click Create token.
-
Name it
dfe-fetcher. Optionally restrict the origin: pick Any IP, any defined network zone, or specific zones (recommend pinning the fetcher's egress IP/zone). -
Click Copy to clipboard - the token value is shown only once. Store it in your secrets manager.
-
Note: SSWS tokens auto-renew on each use but are revoked after 30 days of inactivity, and are deprovisioned if the creating user is deactivated. Schedule the fetcher so the token is exercised regularly.
# Smoke-test (SSWS):curl -sS "https://your-tenant.okta.com/api/v1/logs?limit=1" \-H "Authorization: SSWS $OKTA_TOKEN" \-H "Accept: application/json"
Option B - OAuth 2.0 service app (recommended)
- Admin Console -> Applications -> Applications -> Create App
Integration -> API Services. Name it
dfe-fetcher. - On the app's General tab, configure the client-credentials flow with a
public/private key pair (JWT client assertion). The assertion is signed
RS256, so generate an RSA key pair - an EC key is refused when the
fetcher reads it. Save the public key in Okta and keep the private key for
the fetcher. Note the key id (
kid) Okta shows beside the public key. - Okta API Scopes tab -> grant
okta.logs.read. - Assign the app an admin role with System Log access (Super Admin, Read-only Admin, or a custom role with System Log query).
- Note the app's client id. The fetcher mints and renews the access token
itself: it signs a short-lived assertion with the private key and posts it to
{tenant_url}/oauth2/v1/token, so nothing in the config expires hourly and no rotation job is needed. Keep the private key in your secrets manager and reference it as a spec.
dfe-fetcher Configuration
Config File
sources:
okta:
enabled: true
tenant_url: "https://your-tenant.okta.com" # no trailing slash
token: "00your_ssws_token_here"
use_ssws_header: true # true = SSWS (default); false = Bearer/OAuth
services:
- name: system_log
config:
filter: 'eventType eq "user.session.start"' # optional OData filter
limit: 100 # per-page, max 1000
topic: "okta"
# filter: 'eventType != "user.session.access_token"' # CEL, hot-reloaded
For an access token someone else mints, set use_ssws_header: false and supply
it as token (preferably via credential_secret).
For the service app, configure the same profile as a sources.rest instance so
the fetcher can hold the key and mint its own token:
sources:
rest:
okta:
profile: okta
topic: "okta"
auth:
mode: oauth2_client_credentials
client_id: "0oa1example"
private_key: "vault:kv/data/okta:private_key"
private_key_id: "the kid Okta shows beside the public key"
vars:
base_url: "https://your-tenant.okta.com"
The private key is the app's own RSA PEM, the one whose public half was uploaded to Okta. Scope and assertion lifetime come from the profile, so nothing about the exchange has to be restated here.
private_key_id is the kid the assertion names itself by, and it is not a
secret. An app with ONE registered key pair is resolved without it. An app with
two is not: Okta reads the kid and answers
The client_assertion JWT kid is invalid. to an assertion that names none. Two
key pairs is what a rotation looks like, so set it before you upload a second
public key, not after - roll it forward as: add the new key pair in Okta, set
private_key and private_key_id to the new pair, confirm a tick, then remove
the old public key from the app.
Environment Variables
Pattern is DFE_FETCHER_SOURCES__OKTA__<FIELD> (double underscores).
DFE_FETCHER_SOURCES__OKTA__ENABLED="true"
DFE_FETCHER_SOURCES__OKTA__TENANT_URL="https://your-tenant.okta.com"
DFE_FETCHER_SOURCES__OKTA__TOKEN="00your_ssws_token_here"
DFE_FETCHER_SOURCES__OKTA__USE_SSWS_HEADER="true"
Secrets Manager
Keep the token out of the config file with credential_secret, which
resolves to the literal token value (SSWS token or OAuth access token):
sources:
okta:
enabled: true
tenant_url: "https://your-tenant.okta.com"
credential_secret: "vault:kv/data/okta:token"
use_ssws_header: true
services:
- name: system_log
topic: "okta"
Verification
-
Health check. The profile's probe calls
GET {tenant_url}/api/v1/users/mewith the configured auth header; a 2xx confirms the token + scheme + tenant URL are correct and reachable. -
e2e smoke test.
crates/fetcher/tests/e2e/smoke_remote.rshas#[ignore]-gated live tests. Export credentials (or put them in.env-cloud), then run:export OKTA_TENANT_URL="https://your-tenant.okta.com"export OKTA_TOKEN="00..."export OKTA_USE_SSWS="true" # or false for OAuth bearercargo test -p dfe-fetcher --test e2e okta_ -- --ignored -
Common failure modes.
401 Unauthorized: wrong scheme - SSWS token sent as Bearer (or vice versa). Flipuse_ssws_headerto match the token type.401on the token exchange withinvalid_client: the public key Okta holds does not pair with the configuredprivate_key, or theclient_idis another app's.401on the token exchange naming thekid: the app has more than one key pair registered and the instance sets noprivate_key_id, or sets one the app does not hold. This is what a half-finished key rotation looks like.403 Forbidden: the token's admin role lacks System Log access, or the OAuth app was not grantedokta.logs.read.404/ connection error:tenant_urltypo, trailing slash, or wrong domain (noteoktapreview.comfor preview orgs). An empty or missingtenant_urlis refused at load, namingsources.okta.- Token silently stops working after a quiet period: SSWS tokens are revoked after 30 days of inactivity.
Cost
dfe-fetcher only reads. The System Log API is not expected to carry an additional charge, though rate limits and any plan-specific terms depend on your Okta org. Confirm any cost implications against your own Okta plan.
References
- Create an API token (Okta Developer): https://developer.okta.com/docs/guides/create-an-api-token/main/
- Manage Okta API tokens (Help Center): https://help.okta.com/oie/en-us/content/topics/security/api.htm
- System Log API reference: https://developer.okta.com/docs/api/openapi/okta-management/management/tags/systemlog
- System Log query parameters: https://developer.okta.com/docs/reference/system-log-query/
- OAuth 2.0 scopes (incl. okta.logs.read): https://developer.okta.com/docs/api/oauth2
- Implement OAuth for Okta with a service app: https://developer.okta.com/docs/guides/implement-oauth-for-okta-serviceapp/main/
- Read-only administrators role: https://help.okta.com/en-us/content/topics/security/administrators-read-only-admin.htm