MFA Enforcement Testing: When Policy Says Required and the Endpoint Says Optional

MFA enforcement testing is the practice of confirming, with a live request, that an endpoint actually requires the additional authentication factor its policy claims to require. An identity audit answers a different question: which accounts are enrolled. Enrollment is a directory fact. Enforcement is a runtime behavior.

Those two things drift apart quietly, and almost all of the tooling built around multi-factor authentication measures the first one and reports it as though it settled the second.

Key takeaways

  • An identity audit answers "who is enrolled." Only a request answers "does this endpoint enforce."
  • Microsoft documented a gap in its own Conditional Access engine where a policy targeting All resources with any resource exclusion was not enforced for sign-ins requesting only baseline scopes. The admin console showed the policy applied.
  • Enforcement is decided per protocol and per endpoint, not once per tenant.
  • The identity community reached this conclusion years ago and built request-based tooling for it. Applications rarely get the same treatment.
  • Inside an application, the step-up check is a line of code in a route, not a tenant setting, and it goes missing the way lines of code go missing.

What "MFA required" means to a policy and what it means to a live endpoint

A policy is a statement of intent held in one system. An endpoint is code that makes a decision when a request arrives. Between those two things sit an identity provider, a token, a set of claims, a session, a gateway, a framework middleware, and whatever the application does with all of it.

Every one of those layers is a place where the intent can be preserved perfectly and the behavior can still come out wrong. Not because someone misconfigured the policy, but because the policy was never consulted on the path the request actually took.

This is why "is MFA required here" is not a question you can answer by reading configuration. It is a question you answer by making the request and observing what comes back.

Enrollment data answers a narrower question than it appears to

The identity side of this problem is real and people are actively working it. The SANS Internet Storm Center published a walkthrough for finding the accounts an MFA rollout missed, using Microsoft Graph to pull authentication method registration details and filter for accounts where IsMfaRegistered is false. It is a clean, practical script for a genuine problem, and the author frames it correctly: in every MFA rollout there comes a point where you think you are closing in on done, and stragglers remain.

But note what the author found while running it. Among the accounts that were enrolled, there were "a number of folks with voiceMobile (ie a voice callback) as their primary MFA."

Those accounts pass the audit. IsMfaRegistered returns true. The dashboard turns green. And the actual second factor is a phone call, which is the weakest option in the catalog and the one most exposed to interception and social engineering.

That is the shape of the whole problem in miniature. The registry field is not the control. It is a record that someone once satisfied a requirement, stored in a system that is not the system making the enforcement decision.

The gap Microsoft documented in its own Conditional Access engine

If the policy engine itself is trustworthy, at least the layer below enrollment is solid. Except Microsoft has published documentation of a case where it was not.

Conditional Access policies that target All resources and include one or more resource exclusions were not enforced for sign-ins that requested only baseline scopes. Those baseline scopes are the ordinary ones: the OpenID Connect set (openid, profile, email, offline_access) and a limited set of directory scopes including User.Read. Microsoft's own description is that these scopes were "automatically excluded from policy enforcement when a resource exclusion existed in an All resources policy."

Read that as an operator. You built a policy. You scoped it to everything. You excluded one application, because there is always one application. The console showed a policy covering All resources. And a client requesting only basic profile scopes signed in without being subject to it.

Nothing was misconfigured. The intent was expressed correctly and the enforcement did not happen. Microsoft began rolling out the corrected enforcement model on June 15, 2026, which is the right outcome and also a reminder that the gap existed, in production, in the most scrutinized identity platform in enterprise software, for as long as it did.

You could not have found that by reading your policy. You could only have found it by signing in and watching what was asked of you.

Enforcement is decided per protocol, not per tenant

The other reason configuration review under-reports is that "the application" is rarely one door.

A Microsoft tenant is not a single authentication surface. It is a collection of them: the Graph API, the Azure Resource Manager API, Exchange Web Services, the web portal, ActiveSync, and on-premises ADFS, each with its own authentication endpoint and its own enforcement path. A policy can cover several of them and leave one single factor.

The identity community reached this conclusion years ago and responded the way you would hope. MFASweep, an open-source PowerShell tool, tests exactly this by attempting authentication against each of those endpoints in turn and reporting which ones let a single factor through. Its documentation states the premise plainly: "Depending on how conditional access policies and other multi-factor authentication settings are configured some protocols may end up being left single factor."

That tool is a request-based enforcement test. It does not read your policy. It knocks on each door.

The interesting question for application security teams is why the same instinct so rarely gets applied to the applications their own organization builds, which have exactly the same property: many endpoints, one intent, and no guarantee that the intent reached all of them.

The application layer has its own version of this

Once a user is authenticated, most applications ask a second question for a small number of operations: change the email address, rotate the password, add a payout destination, invite an administrator. These are step-up operations, and they are supposed to demand fresh proof rather than trusting an existing session.

OWASP lists the failure directly. Under API2:2023 Broken Authentication, an API is vulnerable if it "allows users to change their email address, current password, or do any other sensitive operations without asking for password confirmation."

That check is implemented per handler. It is a line of code in a route, not a tenant setting. Which means it is subject to all the ordinary reasons a line of code goes missing: a route added after the pattern was established, an internal service that was never meant to be reachable, a mobile client path that took a shortcut, a v1 endpoint left running for a customer who has not migrated.

The policy documentation will not show you any of that, because the policy documentation is not where the decision lives.

Three ways to ask, and what each one proves

Identity auditPolicy and Conditional Access reviewRequest-based enforcement testing
Question it answersWhich accounts are enrolled, and in what factorWhat the configured intent isWhat the endpoint does when a request arrives
Evidence producedDirectory stateConfiguration stateA request and response pair
Catches per-protocol gapsNoSometimes, if you know to lookYes
Catches application step-up gapsNoNoYes
Catches enrolled-but-weak factorsYesNoPartially
Effort to run continuouslyLow, scriptableLow, scriptableHigher, needs credentials and a test path

None of these is a substitute for the others, and the table is not an argument that the first two are wasted work. Enrollment reporting is the only thing that finds the accounts nobody covered. Policy review is the only thing that finds intent that was never expressed. Request-based testing is the only thing that finds the distance between the intent and the behavior.

Most programs run the first two and infer the third.

Configuration is a claim. A request is evidence. When the two disagree, the request is right.

That principle has a version inside security tooling itself. When a dynamic scan is configured to run fully authenticated, the configuration says authenticated; whether the scanner was holding a valid session at the moment it made each request is a separate fact, and a clean report and an unreached application look remarkably similar from the outside. The fix is the same discipline applied one layer down: prove the login with a check that fails the pipeline rather than trusting the configuration that describes it.

Related readingPart of our series on modern application security testing. Start with What You Should Know About Application Security Testing, then read Your Scan Passed Because It Never Logged In on verifying that an authenticated scan actually authenticated, and Authentication Is Not Authorization on the question a valid session still leaves unanswered.

Frequently asked questions

What is MFA enforcement testing?

MFA enforcement testing confirms with a live request that an endpoint requires the additional authentication factor its policy specifies. It is distinct from MFA enrollment reporting, which shows which accounts have registered a factor. Enrollment is directory state. Enforcement is what happens when a request arrives.

Is an IAM audit enough to prove MFA is enforced?

No. An IAM audit proves accounts are enrolled and can surface accounts a rollout missed, which is necessary work. It cannot show whether a specific endpoint or protocol honors the requirement at request time, because the audit reads the identity directory rather than the system making the enforcement decision.

Why would an endpoint accept a session that never completed MFA?

Common causes include a route that validates only that a session exists rather than inspecting the authentication context claims inside the token, a protocol or client path that predates the current policy, a service excluded from a policy scope, and an internal endpoint that was assumed unreachable. None of these look like a misconfiguration in the policy console.

What is step-up authentication and where does it usually break?

Step-up authentication demands fresh proof before a sensitive operation, such as changing an email address or adding an administrator, rather than trusting an existing session. It usually breaks per handler: on routes added after the pattern was set, on older API versions kept alive for compatibility, and on alternate client paths such as mobile or partner integrations.

Does DAST test MFA enforcement?

Not directly, and the distinction is worth keeping. A dynamic scanner authenticates as a user and tests the application surface that session can reach. Proving that an endpoint rejects a session lacking a required authentication context is a comparison across two authentication states, which is closer to an authorization test than a vulnerability check. Treat it as its own targeted exercise rather than something a general scan covers.

Who owns MFA enforcement testing, identity or application security?

Both, at different layers. Identity owns enrollment, policy, and the tenant level protocol surface. Application security owns the step-up checks inside the application's own routes, which no identity tool can see. The gap between the two teams is where most of these findings live.

How often should enforcement be re-tested?

Treat it as a regression test rather than a one time audit. Enforcement gaps are reintroduced by ordinary change: a new route, a new client, a policy exclusion added under deadline. Confirming the control once says nothing about whether it survived the next release.

Test what your endpoints actually enforce.

Start free, or book a demo to see NightVision derive your API inventory from source and test it fully authenticated against the endpoints your application really serves.