Okta
Overview
Okta is a pull-based source that collects two kinds of Okta data into a Praxis pipeline:
| Data | Okta API | Google SecOps log type | Default |
|---|---|---|---|
| System Log events — sign-ins, MFA challenges, admin changes, app assignments, policy evaluations | GET /api/v1/logs | OKTA | On |
| User context — every user's current status, profile, manager, department and last login | GET /api/v1/users | OKTA_USER_CONTEXT | Off |
The original Okta JSON is preserved in the log body, so downstream parsers (for example the Google SecOps OKTA and OKTA_USER_CONTEXT parsers) receive the complete record. Every record carries the resource attribute okta.source (system_log or users) so you can route each kind to its own log type.
Supported types: Logs
Supported platforms: Linux, Windows, macOS
Minimum collector version: 0.3.16
Authentication
| Credential type | When to use |
|---|---|
Bearer Token (bearertokenauth) with Auth Scheme = SSWS | The only supported option. Okta API tokens are sent as Authorization: SSWS <token>. |
Create a bearertokenauth credential with two fields:
| Field | Value |
|---|---|
token | An Okta API token created under Security → API → Tokens in the Admin Console. |
scheme | SSWS |
Important: the
schemefield must be set toSSWS. Left blank it defaults toBearer, and Okta rejects every request with401 Unauthorized. Praxis will not deploy this source with any credential type other thanbearertokenauth.
Note: an Okta API token inherits the permissions of the admin who created it, and expires after 30 days of non-use. Create it under a dedicated read-only service account with the Read-Only Administrator role — that covers both the System Log and the Users API.
OAuth 2.0 (okta.logs.read scope) is not yet supported. When it lands it will be additive; existing configurations stay valid.
Basic configuration
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
org_url | string (URI) | Yes | none | Okta org base URL, e.g. https://acme.okta.com. Must be https, host-only — no path. The receiver appends /api/v1/logs and /api/v1/users. |
system_log.enabled | bool | No | true | Collect System Log events. Turn off to run a source that collects only user context. |
users.enabled | bool | No | false | Collect user context. See User context. |
poll_interval | integer (seconds) | No | 30 | How often the System Log cursor is followed. Minimum 5, enforced to protect the org rate limit. |
lookback | integer (seconds) | No | 3600 | Cold start only. How far back the first poll reaches when no cursor is persisted. Cap this to your org's log retention. |
persist_offsets | bool | No | true | Checkpoint the resume cursor so restarts neither duplicate nor skip events. See Resumption. |
limit | integer | No | 1000 | Events fetched per request. Okta's maximum is 1000; values outside 1–1000 are rejected. |
filter | string | No | none | Server-side SCIM filter expression, e.g. eventType eq "user.session.start". Filtering at Okta is far cheaper than dropping records downstream. |
q | string | No | none | Free-text narrowing applied server-side. |
In the pipeline editor, limit, filter and q appear inside the Advanced group, but they are top-level config keys — not nested under advanced.
advanced
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout | integer (seconds) | 30 | HTTP timeout for requests to Okta. |
tls.insecure_skip_verify | bool | false | Disable TLS server certificate verification. Not recommended in production. |
tls.ca_file | string | none | Custom CA bundle for verifying the Okta endpoint. |
User context
Turn on Collect User Context (users.enabled) to send every Okta user to the pipeline.
| Parameter | Type | Default | Description |
|---|---|---|---|
users.mode | scheduled | manual | scheduled | scheduled: fetch all users every users.interval. manual: fetch all users only with Fetch Users Now. |
users.interval | integer (seconds) | 86400 | Time between snapshots in scheduled mode. Minimum 900 (15 minutes). |
users.include_deprovisioned | bool | true | Include deactivated (DEPROVISIONED) users, which Google SecOps marks as DELETED. |
users.sync_request | integer | 0 | Fetch Users Now — the on-demand trigger, shown and used in manual mode only. See below. |
Fetching users on demand
Fetch Users Now appears only when User Fetch Mode is manual, and in that mode it is the only way users are fetched.
- Set User Fetch Mode to
manual. - In Fetch Users Now, enter a number higher than the current one (for example
1→2).
Resumption
Okta's System Log API returns a Link: <url>; rel="next" header carrying an opaque, self-advancing cursor that excludes records already returned. The receiver follows that cursor and persists it only after a page has been delivered downstream — so a crash mid-pagination replays from the last durably-flushed cursor rather than skipping events.
With Persist Offsets on (the default), Praxis automatically provisions a storage extension for this node under the collector's metadata directory (<collector home>/metadata/okta_offsets/<node name>) and wires it for you. Nothing to configure.
Turn it off only for ephemeral collectors or read-only filesystems. Without persistence, every restart cold-starts from now - lookback and re-emits whatever falls in that window.
Output mapping
Each System Log event becomes one log record.
| Okta field | OTel destination |
|---|---|
| (whole event JSON) | LogRecord.Body (string) |
published | LogRecord.Timestamp (falls back to receive time if unparseable) |
severity | LogRecord.SeverityNumber / SeverityText — DEBUG/INFO/WARN/ERROR; anything unrecognised maps to INFO rather than being dropped |
uuid | LogRecord.Attributes["okta.uuid"] |
eventType | LogRecord.Attributes["okta.event_type"] |
severity | LogRecord.Attributes["okta.severity"] |
displayMessage | LogRecord.Attributes["okta.display_message"] |
actor.id / actor.type / actor.alternateId | okta.actor.id / okta.actor.type / okta.actor.alternate_id |
client.ipAddress | LogRecord.Attributes["okta.client.ip"] |
outcome.result / outcome.reason | okta.outcome.result / okta.outcome.reason |
| (constant) | Resource.Attributes["vendor"] = "okta" |
(from org_url host) | Resource.Attributes["okta.org"] |
| (constant) | Resource.Attributes["okta.source"] = "system_log" |
Each user becomes one log record.
| Okta field | OTel destination |
|---|---|
| (whole user JSON) | LogRecord.Body (string) |
| (snapshot start time) | LogRecord.Timestamp — the record describes the user as of that snapshot |
id | LogRecord.Attributes["okta.user.id"] |
status | LogRecord.Attributes["okta.user.status"] |
profile.login | LogRecord.Attributes["okta.user.login"] |
| (constant) | Resource.Attributes["vendor"] = "okta", Resource.Attributes["okta.source"] = "users" |
The promoted attributes exist for routing and filtering — use them in routing conditions and processor matchers without paying to parse the body. The body itself is untouched, so a downstream parser still sees the complete record.
Example configuration
{
"org_url": "https://acme.okta.com", // required
"poll_interval": 30, // default: 30, minimum 5
"lookback": 3600, // default: 3600 (cold start only)
"persist_offsets": true, // default: true
"limit": 1000, // default: 1000 (Okta max)
"filter": "eventType eq \"user.session.start\"",
"users": {
"enabled": true, // default: false
"mode": "scheduled", // default: scheduled
"interval": 86400, // default: 86400, minimum 900
"include_deprovisioned": true, // default: true
"sync_request": 1 // raise and deploy to fetch all users now
},
"advanced": {
"timeout": 30, // default: 30
"tls": { "insecure_skip_verify": false }
}
}
Operational notes
- Rate limits. The System Log API is rate-limited per org (and shared with everything else using the Okta API). A
poll_intervalof 30s atlimit: 1000comfortably sustains a busy org; going below the 5s floor is rejected outright. - Filter at the source.
filterandqare evaluated by Okta before the response is built. Narrowing there costs nothing; dropping the same records in a processor costs a full fetch, parse and pipeline traversal. - Lookback vs retention. On a cold start the receiver asks for
now - lookback. Settinglookbackbeyond your org's System Log retention just produces an empty first page. - Polling, not streaming. For very high volumes on AWS, Okta Log Streaming → S3 ingested via the AWS S3 source is an alternative delivery path.
- Event Hooks are not used. They deliver only a curated subset of event types; the System Log API is the complete source of record.
Metrics
| Metric Name | Description |
|---|---|
prx_src_records_received_total{signal="logs"} | Log records emitted, one per System Log event or user. |
prx_src_bytes_received_total | API response bytes read from Okta. |
prx_src_pipeline_latency_seconds | Arrival → downstream handoff. |
prx_src_errors_total{reason} | Source errors by reason — io for request failures. |
prx_src_parse_errors_total{parser="json"} | Malformed event or user page returned by Okta. |
prx_src_records_dropped_total{reason="backpressure"} | Records dropped because the downstream pipeline refused them. |