One trick finds the root of any Okta troubles
Whether you’re troubleshooting a technical issue or performing a forensic investigation in your Okta Workforce Identity org, this article introduces a couple of new queries that can quickly get you to the root of the problem.
They arise from the addition of two new key/value pairs in a large number of Okta System Log events, designed to help administrators, auditors and incident responders get their job done faster.
These helpful tricks are brought to you by the letters O, S, I and C. If you haven’t heard, this acronym stands for The Okta Secure Identity Commitment, which is Okta’s long-term initiative to lead the industry in the fight against Identity attacks.
All events using an API token
The first object to take note of is the RootApiTokenId. A RootApiTokenId will first appear as the target.id when you create an API token, irrespective of whether it’s a management API token or an OAuth token in Okta:
eventType eq "system.api_token.create" and target.id eq "[RootApiTokenId]"
From that point on, the same RootApiTokenId will be stamped in the transaction.detail.rootApiTokenId value of every logged event that arises from the use of that API token.
If that token were ever compromised, or causing you some other manner of trouble, you’re able to find all logged API actions performed using that token using a single query:
transaction.detail.rootApiTokenId eq "[insert RootApiTokenId value]"

One thing to note, however: Okta management API tokens, sometimes referred to as static or SSWS Tokens, are often long-lived. They expire if an administrator revokes or rotates them or they aren’t used for 30 days. A token created more than 90 days ago is outside the retention period of Okta System log events, in which case you’ll only find the token creation event if you have been streaming these events to your SIEM and archiving them there. You can otherwise find all active tokens listed in the Okta Admin Console under Security > API > Tokens.
We recommend eschewing static tokens for OAuth2.0 tokens in production applications, given the latter are short-lived, sender-constrained, and can operate independently of the account used to create them. If for any reason OAuth 2.0 is infeasible, static tokens should be created using a dedicated Custom Admin Role that is only granted the minimum permissions required for the integration to function, and use of the token should be allowlisted to a specific IP or IP range where Okta should expect API calls for this app to originate from.
All events during a user session
The second object of interest is the RootSessionId.
Much the same logic applies as with our method of tracking the use of an API token, only now we’re applying the same method to an interactive user session.
The RootSessionId will first appear as the authenticationContext.rootSessionId value when an interactive user successfully validates their identity at primary authentication:
eventType eq "user.session.start" and authenticationContext.rootSessionId eq "[RootSessionId]"
From that point on, the same RootSessionId will be stamped in the authenticationContext.rootSessionId value of every logged event during the user’s interactive session.
If you ever have cause to suspect that session was compromised, you’re able to find all user actions performed using that token using a single query:
authenticationContext.rootSessionId eq "[insert RootSessionId value]"

Wait, this sounds familiar
So what’s the difference between the externalSessionID and the RootSessionId? It sort of sounds like they do the same thing, right?
Well, almost the same thing. The distinction is that some user actions result in creation of a new externalSessionId.
If a user performs some form of factor lifecycle event, for example, they will be challenged to verify their identity using an existing pre-enrolled factor. Once they successfully perform this action, they have effectively commenced a new session. That makes logical sense, but when you’re troubleshooting or in response mode and want to see the bigger picture, searching with only the externalSessionId can result in missed events. The RootSessionId value, by contrast, is added to every user action up until the session expires or the user signs out.
The only exceptions to that rule are system generated events (that is, actions taken by the Okta platform in response to user generated events, but not initiated by a user), or events sourced in or triggered by Okta Workflows, Okta Privileged Access, and Okta Access Requests, which have their own unique identifiers.
To catch any potential edge cases, we suggest that any analysis of session activity also includes a sweep of actions by the IP address or actor in question. The following query can be used to identify any actions that correspond to a given criteria, but without a rootSessionId value.
not(authenticationContext.rootSessionId pr) AND <your logic>
To find all events related to a particular user that didn’t have a rootSessionId value:
not(authenticationContext.rootSessionId pr) and actor.alternateId eq "[username value]"
To find all actions related to a particular IP that didn’t have a rootSessionId value:
not(authenticationContext.rootSessionId pr) and client.IpAddress eq "[IP address value]"
To find all actions where the user was the subject (aka target) of an event:
not(authenticationContext.rootSessionId pr) and target.alternateId eq "[username value]"
Beyond the SOC
The ability to query for all events related to an API token or all events related to a user session also opens up a lot of possibilities for automation.
We recommend revisiting your detection library and your Okta Workflows with these queries in mind: they will help to find some creative solutions to all sorts of problems.