Skip to main content
INS // Insights

SOC 2 MFA Coverage Evidence for In-Scope Users

Updated September 2026 · 7 min read

SOC 2 MFA coverage evidence is a comparison, not a screenshot of a security setting. The numerator is people in the identity provider whose authentication methods show MFA registered. The denominator is the in-scope population from your system description: the employees and contractors who can reach in-scope systems. A tenant where security defaults are on, and where the intern who still has production access never registered a factor, fails the comparison even though the setting looks enabled.

The compliance lead is usually holding an export of "MFA registered: true" for licensed mailboxes. The auditor asks about the twenty contractors in the system description who are missing from that export. Rutagon builds the join, the gap list, and the exception register with end dates. The packet is filed in the GRC you already run. We do not attest your controls.

Two lists that are not the same list

The IdP list comes from registration details, not from a conditional-access policy's existence. In Microsoft Entra, userRegistrationDetails includes isMfaRegistered and methodsRegistered. isMfaRegistered is a boolean. methodsRegistered is the actual set: authenticator app, FIDO2, SMS, phone, and so on. A policy that says SMS does not count has to look at the methods, because the boolean will still be true for an SMS-only user. We export both. Your policy decides whether SMS satisfies the control. We do not silently upgrade SMS to phishing-resistant.

Microsoft documents the report in userRegistrationDetails. The call is GET /reports/authenticationMethods/userRegistrationDetails. It needs a report or audit-log read permission on the app we use for the extract. A global admin click in the portal is not the pipeline.

The in-scope list comes from the system description and the HR or contractor feed you already trust for joiner-mover-leaver. It is not "everyone with a license" and it is not "everyone who signed in this week." People who should have access and have not signed in are the interesting gaps. People who have a mailbox and are out of scope should not inflate the denominator or the numerator. We write the scope rule in the evidence packet so the next run uses the same denominator.

coverage = in_scope users with an accepted MFA method
           / in_scope users
gap       = in_scope AND no accepted method AND no unexpired exception

Exceptions are a third list. Break-glass accounts, a hardware-key user waiting on a shipment, a vendor integration that cannot enroll. Each exception has an owner and an end date. An exception with a null end is a finding. We do not count exceptions as registered. They are disclosed gaps with a clock. When the clock passes, the row moves to the gap list automatically.

SOC 2 MFA coverage evidence against the in-scope list

The join key is the user principal name or the stable directory id, agreed once. Mismatches are their own bucket: in-scope people with no IdP account, and IdP accounts marked registered who are not in scope. The first bucket is a provisioning miss and an access miss. The second is how a beautiful percentage hides the contractors.

Service accounts and other non-humans are excluded only when they are on the non-human inventory and cannot be used interactively. A shared login with a password that a human types is in scope, and MFA applies, or the shared login has to go. We do not accept "it's a service account" as a label someone typed on a user who signs in from a browser.

Conditional access that requires MFA is a useful control and it is not the coverage artifact. A policy can require MFA for one cloud app and not another. A user can be unregistered if they never hit a protected app. The auditor sampling logical access wants the population registered, not a policy that would challenge them if they happened to open the right application. We can attach the conditional-access assignment as supporting evidence. The primary exhibit remains the join.

Identity Center access review covers the AWS human path, where MFA is enforced at the identity source or at Identity Center. If the identity source is Entra, this coverage report is the upstream evidence and Identity Center should not be a second place someone enrolls a weaker factor and bypasses the IdP. One enrollment point, referenced by the AWS review. Continuous access review then keeps the in-scope list from drifting away from the people who still have standing access. Coverage against a stale HR file is a precise answer to the wrong population.

Privileged users are a highlighted slice, not a separate universe. Permanent eligible admins from an Entra PIM access review should appear in the MFA join with a method your policy accepts for admins, which is often stricter than the general workforce. A global admin with SMS only is visible because we kept methodsRegistered.

How often it runs and what we file

The job runs on the cadence your control describes. Monthly is common. Each run stores the denominator count, the covered count, the gap rows, and the exception rows with their end dates. A percentage without the gap rows cannot be sampled. We file the run in the GRC you already run, attached to the logical-access control you already test. The trend is useful for you. The sample needs one complete run inside the window, with the scope memo beside it.

Gaps are not only an evidence problem. The same list is a work queue: enroll, remove access, or open an exception that expires. We do not close a gap in the GRC because someone promised to enroll. We close it when isMfaRegistered is true and the methods satisfy the policy, or when the user leaves the in-scope list because access was removed. The second read is the same discipline as an access-review revoke.

Rutagon builds the extract and the join. Your identity administrators own enrollment. We do not take over the help desk and we do not claim a SOC 2 attestation. Security automation is the capability: evidence pipelines for the control you already operate.

Production lesson: the first join is humbling. Contractors in a spreadsheet that never reached the IdP, executives excluded from conditional access by a named-user exception with no expiry, and a break-glass account whose exception was "temporary" with no date. We leave those as rows. Cleaning them up is the remediation. Hiding them to print 100 percent is the failure.

Frequently Asked Questions

Why is a conditional-access screenshot not coverage evidence?

A policy can require MFA only for some apps, and users who never hit those apps can remain unregistered. Coverage compares accepted MFA methods to the in-scope population. The policy can sit beside that join as support. It does not replace the denominator.

What is the in-scope list?

The people your system description says can reach in-scope systems, taken from the HR or contractor source you already use. Licensed mailboxes and last-week sign-ins are different lists. We store the scope rule with the evidence so the next run uses the same population.

Does SMS count as enrolled?

isMfaRegistered is true for SMS-only users. If your policy rejects SMS, we use methodsRegistered and those users stay on the gap list. We do not rewrite the boolean. Your control language picks the accepted methods, and the export shows which method each person has.

How do exceptions work?

Each exception has an owner and an end date and is not counted as registered. A null end date is a finding. When the date passes, the user returns to the gap list until they enroll or lose access. Break-glass accounts live here, not in the covered percentage.

Where is the run stored?

Denominator, covered count, gap rows, and exceptions go into the GRC you already run. Rutagon builds the join. We do not enroll tokens for your users and we do not provide the attestation. Closing a gap requires a new registration read or proof the user left the in-scope list.

Ask us to file this evidence in the GRC you already run. Contact Rutagon at rutagon.com/contact, 907-841-8407, or contact@rutagon.com.