Okta System Log Format: Field Reference With Examples
Published
One record per event in an Okta org — a login attempt, a factor verification, a logout, an account lockout — showing who or what acted, the client and IP behind it, and whether it succeeded.
Sample record
{"eventType":"user.session.end","published":"2024-03-15T17:30:00.000Z","displayMessage":"User logout from Okta","outcome":{"result":"SUCCESS"},"actor":{"alternateId":"alice@corp.com","displayName":"Alice Smith","type":"User"},"client":{"ipAddress":"203.0.113.42","userAgent":{"rawUserAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/123.0.0.0 Safari/537.36","os":"Mac OS X","browser":"Chrome"},"geographicalContext":{"country":"US","city":"Seattle"}},"securityContext":{"asNumber":7224,"asOrg":"Amazon.com","isp":"Amazon Web Services","domain":"aws.com"}},
{"eventType":"user.session.start","published":"2024-03-15T10:15:00.000Z","displayMessage":"User login to Okta","outcome":{"result":"FAILURE","reason":"INVALID_CREDENTIALS"},"actor":{"alternateId":"charlie@corp.com","displayName":"Charlie Brown","type":"User"},"client":{"ipAddress":"203.0.113.99","userAgent":{"rawUserAgent":"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15","os":"iOS","browser":"Mobile Safari"},"geographicalContext":{"country":"US","city":"Chicago"}},"securityContext":{"asNumber":7922,"asOrg":"Comcast","isp":"Comcast Cable","domain":"comcast.net"}},
{"eventType":"user.session.start","published":"2024-03-15T10:15:30.000Z","displayMessage":"User login to Okta","outcome":{"result":"SUCCESS"},"actor":{"alternateId":"charlie@corp.com","displayName":"Charlie Brown","type":"User"},"client":{"ipAddress":"203.0.113.99","userAgent":{"rawUserAgent":"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15","os":"iOS","browser":"Mobile Safari"},"geographicalContext":{"country":"US","city":"Chicago"}},"securityContext":{"asNumber":7922,"asOrg":"Comcast","isp":"Comcast Cable","domain":"comcast.net"}} From the LogTriage parser test corpus — synthetic data, no real hosts or users.
Field reference
| Field | Type | Meaning | Why it matters for security |
|---|---|---|---|
| eventTypee.g. user.session.start | string | Structured name of the event. The samples contain user.session.start, user.authentication.auth_via_mfa, user.session.end and user.account.lock. | The first filter in any hunt — authentication and lockout event types carry the credential-attack signal. |
| publishede.g. 2024-03-15T09:01:00.000Z | datetime (UTC) | When Okta recorded the event, ISO 8601 with milliseconds. | Sort on this rather than on position in the file; exported records are not always in time order. |
| displayMessagee.g. User login to Okta | string | Human-readable description of what happened. | Readable in a timeline, but match on eventType — message text is not a stable key. |
| outcome.resulte.g. SUCCESS | string | Whether the attempt succeeded. Both SUCCESS and FAILURE appear in the samples. | A run of FAILURE events followed by one SUCCESS from the same IP is the credential-stuffing shape. |
| outcome.reasone.g. INVALID_CREDENTIALS | string | Why a failure failed. The samples show INVALID_CREDENTIALS and FACTOR_CHALLENGE_TIMEOUT; the field is absent on the successful events. | INVALID_CREDENTIALS across many accounts from one source is spraying or stuffing; FACTOR_CHALLENGE_TIMEOUT after a successful password means the attacker has the credential but stopped at MFA. |
| actor.alternateIde.g. alice@corp.com | string | Identifier of whoever or whatever triggered the event, normally the user's login name. | Counting distinct values per source IP is how stuffing is found — but on system-generated events this is not the victim. |
| actor.displayNamee.g. Alice Smith | string | Display name of the actor. | Good for timelines, bad as a join key — it is not unique and users can share names. |
| actor.typee.g. User | string | Kind of principal that acted. The samples show User and SystemPrincipal. | SystemPrincipal means Okta itself generated the event, such as the automatic account lockout in the malicious sample. |
| client.ipAddresse.g. 203.0.113.42 | string | Source IP address Okta saw for the request. | The main pivot — grouping failures by IP separates one user mistyping a password from one host hitting many accounts. |
| client.userAgent.rawUserAgente.g. python-requests/2.28.0 | string | Raw User-Agent header sent by the client. | HTTP libraries such as python-requests on interactive logins mean automation, not a person at a keyboard. |
| client.userAgent.ose.g. Mac OS X | string | Operating system Okta parsed out of the user agent; Unknown when it cannot parse one. | os and browser both Unknown usually means a non-browser client such as a script. |
| client.userAgent.browsere.g. Chrome | string | Browser parsed out of the user agent; Unknown when it cannot be identified. | A fleet-wide shift to an unparsed browser on one account is worth checking against the raw agent string. |
| client.geographicalContext.countrye.g. US | string | Country derived from the source IP address. | Two countries for one account within minutes is impossible travel — remember the value is geolocation, not ground truth. |
| client.geographicalContext.citye.g. Seattle | string | City derived from the source IP address. | City-level changes are noisy on mobile and VPN traffic; treat country and ASN as the stronger signals. |
| securityContext.asNumbere.g. 7224 | integer | Autonomous system number Okta associated with the source IP. | One ASN behind failures against many accounts points at a single hosting provider or proxy pool rather than scattered users. |
| securityContext.ispe.g. Comcast Cable | string | ISP name associated with the source IP, alongside asOrg and domain. | Hosting and anonymiser networks on a user login are a strong enrichment signal; residential proxies still look like consumer ISPs. |
What it records
Each record in the Okta System Log is one event in an Okta org: a login attempt, a factor verification, a logout, an account lockout, an administrative change. The record says what happened (eventType, displayMessage), how it ended (outcome.result and, on failures, outcome.reason), who or what caused it (the actor object) and where it came from (client.ipAddress, the parsed client.userAgent, client.geographicalContext and the IP enrichment in securityContext).
One sign-in is usually more than one record. In the benign sample, Alice’s login produces a user.session.start at 09:01:00 and a user.authentication.auth_via_mfa at 09:01:05; her logout is a separate user.session.end eight hours later. Reconstruct sessions by correlating actor.alternateId, client.ipAddress and published, not by reading a single event.
Where to get it
- Admin console → Reports → System Log, which supports search and filtering and a CSV download. Convenient for a quick look, limited for bulk work.
- System Log API,
GET /api/v1/logs, withsinceanduntilparameters and cursor-based pagination through theLinkresponse headers. It returns a JSON array of event objects shaped like the samples on this page (with more fields). - Log streaming / SIEM collectors. Most SIEM integrations poll the API on a schedule; where your tenant supports Okta’s log streaming feature it can push events to a cloud destination instead.
Console retention is finite and depends on your org, so forward events to your SIEM or object storage if you need months of history for investigations that start late.
Parsing pitfalls
outcome.reasononly exists on failures. The SUCCESS records in the samples have noreasonkey at all. Code that reads it unconditionally will throw or silently produce nulls.- Export order is not time order. In the benign sample the 17:30
user.session.endappears before the 10:15 events. Sort onpublishedbefore building a timeline. - The actor is not always the subject. On
user.account.lockthe actor issystem@okta.comwithactor.typeofSystemPrincipal. Grouping lockouts byactor.alternateIdattributes them to Okta rather than to the locked user. - Geolocation and ASN are derived.
client.geographicalContextandsecurityContextcome from IP enrichment, so VPNs, carrier NAT and cloud egress produce plausible but wrong locations — and enrichment labels can be stale or odd. osandbrowsercan beUnknown. They are parsed fromrawUserAgent; keep the raw string, that is wherepython-requests/2.28.0shows up.- Array versus NDJSON. These files are JSON arrays; API pages and streaming destinations may hand you one object per line. Handle both.
Detection ideas
- Credential stuffing: one
client.ipAddresswithoutcome.resultFAILURE andoutcome.reasonINVALID_CREDENTIALS across many distinctactor.alternateIdvalues in minutes — nine accounts in twenty seconds in the malicious sample. This is the Okta credential stuffing rule. - Scripted client:
rawUserAgentof an HTTP library, withosandbrowserbothUnknown, onuser.session.start. - A hit inside the burst: a SUCCESS
user.session.startfrom the same IP that just produced the failures — that account’s password is known. - Password valid, MFA held: a successful
user.session.startfollowed byuser.authentication.auth_via_mfawith FAILURE and reason FACTOR_CHALLENGE_TIMEOUT. Reset the password anyway; MFA was the only thing standing in the way. - Lockout clusters: several
user.account.lockevents sharing oneclient.ipAddress. - Impossible travel: successful sessions for one
actor.alternateIdfrom two distantclient.geographicalContext.countryvalues within an implausible interval.
The same reasoning applies to Microsoft estates with different field names — see the Entra ID sign-in log reference. To run these checks on an export without uploading it anywhere, use the browser log analyzer.
Detections and guides for Okta System Log
Frequently Asked Questions
- Why does a user.account.lock event show system@okta.com as the actor?
- Because Okta, not the user, performed the lockout, so actor.type is SystemPrincipal and actor.alternateId is the system account. The locked user is not in the actor object of that event — in full API records the affected account appears in the target array, and in trimmed exports like the sample you have to identify the victim from the failed logins immediately before, correlating on client.ipAddress and published.
- What is the difference between user.session.start and user.authentication.auth_via_mfa?
- They are separate records for separate stages. In the benign sample Alice's login is a user.session.start with SUCCESS at 09:01:00 followed by user.authentication.auth_via_mfa with SUCCESS five seconds later. A successful session.start with a failed auth_via_mfa, as in the malicious sample, means the password was accepted and only the second factor stopped the attacker.
- How do I tell a user fumbling their password from credential stuffing?
- Group on client.ipAddress and count distinct actor.alternateId values. One account failing repeatedly from its usual IP, browser and city is a human; one IP producing INVALID_CREDENTIALS against nine different accounts in twenty seconds with a python-requests user agent, as in the malicious sample, is automated credential testing.
- Do the samples show every field an Okta System Log record contains?
- No. These files are trimmed to the fields that matter for authentication analysis. Records returned by the System Log API also carry identifiers and context such as uuid, severity, transaction and the target array, so write parsers that ignore unknown keys rather than assuming this exact shape.
Analyze a Okta System Log file in your browser
The free analyzer detects this format, parses every field above and flags attack patterns — the file never leaves your machine. For threat-intel enrichment and an AI-written incident report, run it through LogTriage.