Conditional Access and Zero Trust
Deciding access per request instead of per network: zero trust principles, signals such as user, device, location and risk, conditional access policies, device compliance, and rolling policies out safely.
Zero trust in practice
Zero trust is explained as a security model in Network Security in Depth: no user, device or network location is trusted by default, and every access request is evaluated on its own. The old model drew a perimeter around the office network and trusted whatever was inside. That stopped working once people worked from home, data moved to SaaS applications and attackers learned to get inside with one phished password. Identity became the new perimeter, and the identity provider is where most access decisions are now made.
Microsoft summarizes zero trust in three principles:
- Verify explicitly: decide on every request using all available signals — who, from which device, where, how risky — not on the network the request comes from.
- Use least privilege access: just enough access, just in time, for a limited session (see Authorization Models and Privileged Access Management).
- Assume breach: design as if an attacker is already inside — segment access, encrypt, log everything and limit how far one compromised account can reach.
Two public references are worth knowing. NIST SP 800-207, "Zero Trust Architecture" (August 2020), describes the logical components: a policy decision point that makes the access decision and a policy enforcement point that carries it out — exactly the roles that conditional access and the applications play below. The CISA Zero Trust Maturity Model (version 2.0) organizes the work into five pillars, identity among them, with three cross-cutting capabilities, and four maturity stages: Traditional, Initial, Advanced and Optimal. Zero trust is not a product you buy; it is a direction you move in, pillar by pillar.
Conditional access
Conditional access is a policy engine in an identity provider that evaluates each sign-in or token request against if-then rules: if the request matches conditions about the user, the application, the device, the location and the risk, then block it, allow it, or allow it only after extra controls are satisfied. In Microsoft Entra ID it requires an Entra ID P1 license (risk-based conditions need P2). Other identity providers have the same idea under other names, such as sign-on or authentication policies.
Two details explain many surprises. First, conditional access runs after the first factor: a user with a wrong password never reaches it, and a blocked sign-in still shows a correct password in the logs — a useful clue in an investigation. Second, it acts when a token is issued. A user who already holds a valid token keeps access until it expires, unless continuous access evaluation (below) cuts in.
Signals
A policy's assignments and conditions decide which sign-ins it applies to:
- Users and groups: all users, specific groups, directory roles, guests — with exclusions. Groups make policies manageable; roles make sure every new administrator is covered automatically.
- Target resources: all cloud applications, or specific ones such as "Microsoft Admin Portals" or the payroll application.
- Device platform (Windows, iOS, Android, macOS, Linux) and device state (compliant, Entra hybrid joined).
-
Locations: named locations defined by IP address
ranges or countries, for example the office's public range
203.0.113.0/24. - Client apps and authentication flows: browser, modern desktop and mobile apps, or legacy authentication protocols; and flows such as device code.
- Risk: sign-in risk and user risk from Microsoft Entra ID Protection.
Device compliance
Device compliance is the status of a managed device measured against an organization's requirements — supported operating system version, disk encryption, screen lock, no jailbreak or root, security software running. A device management service such as Microsoft Intune checks the device (see Mobile device management) and reports the result to Entra ID, where conditional access can require it. "Require a compliant device" is one of the strongest controls available: a stolen password, even with a stolen MFA approval, is useless from the attacker's own laptop. In the Windows world, Intune compliance policies take over much of the work that Group Policy security baselines did on domain-joined computers (see Group Policy).
Sign-in risk and user risk
Sign-in risk is the estimated probability that a particular authentication request was not made by the account's owner, calculated by the identity provider from signals such as an anonymous IP address, impossible travel between two sign-ins, an unfamiliar location or device, or patterns known from attack tools. User risk is the probability that the account itself is compromised — for example because its credentials appeared in a leak. In Entra ID Protection both are rated low, medium or high, and conditional access can respond: require MFA for a medium-risk sign-in, require a secure password change for a high-risk user, or block.
Why location alone is weak
"Trusted locations" are tempting: skip MFA at the office. But an IP address says where a request came from, not who sent it. An attacker who controls an office computer, or a VPN that egresses through the office range, inherits that trust. Use location to add requirements (block countries you never work from, require more outside the office), not as the only reason to remove them.
Decisions and controls
Each policy ends in one of these decisions:
- Block access.
- Grant access, optionally requiring one or all of: MFA, an authentication strength (for example "phishing-resistant MFA", see MFA and Passwordless), a compliant or Entra hybrid joined device, an approved client app or app protection policy on mobile, a password change, or accepting terms of use.
- Session controls, which shape the session after sign-in: sign-in frequency (how often the user must re-authenticate), whether the browser session persists, and restrictions enforced by the application itself, such as view-only access to email from an unmanaged device.
How policies combine
Firewall rules are usually evaluated top-down, first match wins. Conditional access in Entra ID works differently: there is no order. Every policy whose conditions match applies, and the result is the most restrictive combination. If any matching policy blocks, the sign-in is blocked. Otherwise the user must satisfy the grant controls of every matching policy — all of a policy's controls, or just one of them if that policy is set to "Require one of the selected controls". Policies in report-only mode are evaluated and logged, but not enforced. This sketch evaluates five sign-ins against three policies in that way:
POLICIES = [
{"name": "Block legacy authentication", "state": "on",
"users": "all", "apps": "all", "client": "legacy", "grant": "block"},
{"name": "Require MFA for all users", "state": "on",
"users": "all", "apps": "all", "client": "any", "grant": {"mfa"}},
{"name": "Admins need a compliant device", "state": "report-only",
"users": "admins", "apps": "admin portals", "client": "any",
"grant": {"mfa", "compliant device"}},
]
EXCLUDED = {"breakglass-01"} # emergency accounts: excluded from every policy
ADMIN_PORTALS = {"Azure portal", "Entra admin center"}
def applies(policy, s):
if s["user"] in EXCLUDED:
return False
if policy["users"] == "admins" and not s["admin"]:
return False
if policy["apps"] == "admin portals" and s["app"] not in ADMIN_PORTALS:
return False
if policy["client"] == "legacy" and not s["legacy"]:
return False
return True
def outcome(policies, s):
"""All matching policies apply together: block wins, otherwise every control is required."""
blocking = [p["name"] for p in policies if p["grant"] == "block"]
if blocking:
return f"block ({blocking[0]})"
required = set().union(*(p["grant"] for p in policies))
if "compliant device" in required and not s["compliant"]:
return "block (device not compliant)"
return "grant after " + " + ".join(sorted(required)) if required else "grant, no controls"
SIGN_INS = [
{"user": "lucia.romero", "app": "Outlook on the web", "legacy": False, "admin": False, "compliant": True},
{"user": "lucia.romero", "app": "IMAP mail client", "legacy": True, "admin": False, "compliant": True},
{"user": "admin-identity1", "app": "Azure portal", "legacy": False, "admin": True, "compliant": False},
{"user": "admin-identity2", "app": "Entra admin center", "legacy": False, "admin": True, "compliant": True},
{"user": "breakglass-01", "app": "Azure portal", "legacy": False, "admin": True, "compliant": False},
]
for s in SIGN_INS:
matching = [p for p in POLICIES if applies(p, s)]
enforced = [p for p in matching if p["state"] == "on"]
print(f"{s['user']:<16}{s['app']:<20}{outcome(enforced, s)}")
for p in matching:
if p["state"] == "report-only":
print(f"{'':<16}report-only: '{p['name']}' would {outcome(enforced + [p], s)}")
lucia.romero Outlook on the web grant after mfa
lucia.romero IMAP mail client block (Block legacy authentication)
admin-identity1 Azure portal grant after mfa
report-only: 'Admins need a compliant device' would block (device not compliant)
admin-identity2 Entra admin center grant after mfa
report-only: 'Admins need a compliant device' would grant after compliant device + mfa
breakglass-01 Azure portal grant, no controls
Read the output as an identity engineer would:
- The IMAP sign-in also matches "Require MFA", but the block wins. Legacy protocols cannot perform MFA at all, which is why blocking them is usually the first policy anyone deploys.
-
The report-only policy would block
admin-identity1, whose laptop is not compliant. That is exactly what report-only is for: you discover it in the logs and enroll the device before you switch the policy on. - The break-glass account gets through without any control from these policies. That is deliberate (see below), and it is why such accounts need a strong credential, monitoring and an alert on every use. Microsoft's own mandatory MFA for Azure administration, which tenant policies cannot switch off, still applies to it.
A policy as configuration
Policies can be managed as code through Microsoft Graph, which makes them reviewable and repeatable. The JSON below is an illustrative policy, modeled on the shape Graph uses; check the field names against Microsoft's documentation before automating anything, and replace the placeholder IDs.
{
"displayName": "CA010 - Admins - require phishing-resistant MFA and compliant device",
"state": "enabledForReportingButNotEnforced",
"conditions": {
"users": {
"includeRoles": ["role-id-global-administrator", "role-id-privileged-role-administrator"],
"excludeGroups": ["group-id-break-glass-accounts"]
},
"applications": {
"includeApplications": ["All"]
},
"clientAppTypes": ["all"]
},
"grantControls": {
"operator": "AND",
"builtInControls": ["compliantDevice"],
"authenticationStrength": { "id": "strength-id-phishing-resistant-mfa" }
},
"sessionControls": {
"signInFrequency": { "isEnabled": true, "type": "hours", "value": 4 }
}
}
Note the naming convention in displayName (a number,
the target group, the effect), the state that starts in report-only,
"operator": "AND" (all controls required, rather than
any one of them), and the exclusion of the break-glass group.
Rolling out policies safely
A conditional access mistake can lock an entire organization out of its email — or lock out every administrator who could fix it. Roll out like a change to production:
- Start in report-only mode. Report-only mode is a policy state in which the policy is evaluated for every matching sign-in and its would-be result is written to the sign-in logs, but nothing is enforced. Run it for a week or two and read the results.
- Use the What If tool to test a specific user, application and device against all policies before switching on.
- Enable for a pilot group, then widen to all users. Tell the service desk before each step; they will get the calls.
- Exclude break-glass accounts from every policy: two cloud-only emergency accounts with Global Administrator, phishing-resistant credentials kept in a safe, and an alert on every sign-in (see Privileged Access Management). Exclude them by group, and review the exclusions regularly — an exclusion list that grows is a policy that slowly disappears.
Security defaults for small tenants
Organizations without Entra ID P1 licenses cannot use conditional access. They get security defaults instead: a single switch that requires all users to register for MFA, requires MFA for administrators and for Azure management, and blocks legacy authentication protocols. It is on by default in new tenants and cannot be tuned; once you create your own conditional access policies, you turn security defaults off and replace them with equivalent policies.
Continuous access evaluation
Classic token-based access has a gap: an access token stays valid until it expires, typically about an hour, even if the account is disabled a minute after it was issued. Continuous access evaluation (CAE) closes most of that gap: services that support it — in Microsoft 365, Exchange Online, SharePoint Online and Teams among them — subscribe to critical events from the identity provider and reject an existing token in near real time when, for example, the user is disabled or deleted, their password is changed or reset, an administrator revokes their sessions, or high user risk is detected. Some services also re-check location policies when the client's IP address changes.
CAE is an open idea (the OpenID Foundation's Shared Signals work covers the same ground), but in practice it depends on support in both the identity provider and the application. For services without it, keep access token lifetimes short and revoke sessions as part of the leaver process (see The Identity Lifecycle).
Common mistakes
- Gaps between policies: a policy for "all applications" that excludes one application for a vendor problem, and nobody ever removes the exclusion. Review exclusions on a schedule.
- Forgetting non-browser flows: legacy authentication and the device code flow need their own blocking policies.
- Not reading the logs: the sign-in logs show which policies applied and why each sign-in passed or failed (see Monitoring and Troubleshooting Identity).