Skip to main content

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 the sources.okta block) - sends Authorization: SSWS <token> using a legacy Okta API token.
  • use_ssws_header: false - sends Authorization: Bearer <token> using an OAuth 2.0 access token someone else minted with the okta.logs.read scope.
  • An API Services app and its key pair, configured as a sources.rest instance 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.read scope.
  • 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​

ServiceEndpointPermission or ScopeNotes
system_logGET {tenant_url}/api/v1/logsSSWS token from a service account with read access to System LogToken inherits the creating admin's privileges; Read-only Admin or System Log query custom role is sufficient.
system_logGET {tenant_url}/api/v1/logsOAuth scope okta.logs.readGranted to an API Service app using the client-credentials flow.

All access is read-only.

Source-Side Setup​

Option A - SSWS API token (simplest)​

  1. Sign in to the Admin Console as the service-account admin.

  2. Go to Security -> API, open the Tokens tab, click Create token.

  3. 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).

  4. Click Copy to clipboard - the token value is shown only once. Store it in your secrets manager.

  5. 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"
  1. Admin Console -> Applications -> Applications -> Create App Integration -> API Services. Name it dfe-fetcher.
  2. 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.
  3. Okta API Scopes tab -> grant okta.logs.read.
  4. Assign the app an admin role with System Log access (Super Admin, Read-only Admin, or a custom role with System Log query).
  5. 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/me with 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.rs has #[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 bearer
    cargo test -p dfe-fetcher --test e2e okta_ -- --ignored
  • Common failure modes.

    • 401 Unauthorized: wrong scheme - SSWS token sent as Bearer (or vice versa). Flip use_ssws_header to match the token type.
    • 401 on the token exchange with invalid_client: the public key Okta holds does not pair with the configured private_key, or the client_id is another app's.
    • 401 on the token exchange naming the kid: the app has more than one key pair registered and the instance sets no private_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 granted okta.logs.read.
    • 404 / connection error: tenant_url typo, trailing slash, or wrong domain (note oktapreview.com for preview orgs). An empty or missing tenant_url is refused at load, naming sources.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​