Reduce Noise when Analyzing User MFA Status with Graph Queries

Erkang Zheng
Mar 17th, 2020

There is no doubt multi-factor authentication (MFA) is a simple and effective way to reduce account compromise, yet only 11% of all enterprise accounts use a MFA solution overall, accordingly to latest data from Microsoft.

Are there users without MFA enabled?

That's a question our security team wants answered and alerted. In our internal JupiterOne account, we have an audit/alert rule configured to check for user accounts without MFA enabled. The J1QL query for that particular rule is written as such:

Find User with mfaEnabled != true
 that !(ASSIGNED|USES|HAS) mfa_device

This checks for any user that does not have the

mfaEnabled

attribute directly on the user entity and does not have a relationship (assigned/has/uses) to an MFA device entity.

Hmm ... so many false positives because of SSO

However, this query by itself has false positives in an environment with Single Sign On (SSO) configured.

For example, our users in Bitbucket, Jira, KnowBe4, and many other apps authenticate via Okta SSO. The users within these app accounts do not have MFA directly configured, and do not have an MFA device directly associated, because users never authenticate directly.

To accurately determine whether any user in these accounts does or does not use MFA for authentication, we will have to correlate them to SSO users and applications configured in Okta. For each user in an account connected via SSO, we have to check the following:

  1. An application is configured in Okta that connects it to the corresponding account
  2. A user from the target account has a matching active SSO user assigned to the SSO application in Okta
  3. The matching active user in Okta has MFA configured/assigned

This configuration scenario can be seen in the following graph, showing my Bitbucket user does not have MFA configured but it is actually authenticating via a connected Okta application, and my Okta user does have a number of MFA devices configured:

We need an automated way to "enrich" my Bitbucket user in the above example so that it is not included in the "user accounts without MFA enabled" alert rule. Here's how we make this work.

Reduce false positives using results from graph queries

First, we can use the following query to check for #1 and #2:

Find User with _key='<unique_key_of_the_user_entity>' as userA
 that has Account
 that connects okta_application
 that assigned okta_user with active=true as userB
where
 userA.name = userB.name or
 userA.username = userB.username or
 userA.email = userB.email

Enrichment: if this query returns a match, we can set

ssoUser: true

on the originating user entity (i.e.

userA

, the provider app user).

If your primary SSO provider is not Okta, replace

okta_application

and

okta_user

in the query above (and below) with the appropriate ones.

Next, we can use a second query to check for condition #3:

Find User with _key='<unique_key>' and ssoUser=true as userA
 that has Account
 that connects okta_application
 that assigned okta_user with active=true as userB
 that (assigned|has|uses) mfa_device
where
 userA.name = userB.name or
 userA.username = userB.username or
 userA.email = userB.email

Enrichment: if this query returns a match, we can set

mfaEnabled: true

on the originating user entity (i.e.

userA

, the provider app user).

You may notice this second query is very similar to the first one. It has an additional relationship traversal at the end:

that (assigned|has|uses) mfa_device

So why not do this in one query? The reason is the additional filter at the beginning:

and ssoUser=true

This filter is added to ensure we only set the

mfaEnabled

property on user entities that were identified as SSO users in the previous step.

Graph enrichment FTW!

Here's my Bitbucket user after the enrichment is complete:

Now we repeat these two steps on each user that is not an Okta user (or whichever is your primary SSO provider — OneLogin, JumpCloud, etc.). This allows us to use our original "user accounts without MFA enabled" alert rule with all false positives virtually eliminated!

Review of the results validated that system accounts did not get the

ssoUser 
or

mfaEnabled 
flag, as intended, and identified a handful of users needing remediation.

Check out the entire code here.

Other articles

JupiterOne's MCP server is now listed in Anthropic's MCP directory, making it easier to bring your security graph into Claude and ask questions in plain English.

John Le
Aug 10th, 2026

AI risk vs. adversarial risk, safety vs. security distinction stopped mattering the moment the models decided the exam was too hard.

Kevin Tonkin
Jul 30th, 2026

Comparing Axonius alternatives? See how six platforms stack up from the security graph to OT/IoT discovery and which one fits your environment.

John Le
Jul 12th, 2026

Tools are silent.
Risks aren't.

See your full security program as one connected picture in a 30-minute demo tailored to your environment.