A stolen session token is often worth more to an attacker than a password, because it has already passed MFA. Token protection is the Conditional Access session control that makes Microsoft Entra ID accept only device-bound sign-in session tokens from supported apps, so a token lifted from one machine can't simply be replayed from another. In this post I'll cover what it protects, where it works and how to roll it out without blocking your users.
Errors this guide fixes
1002 No Microsoft Entra device stateMicrosoft Entra sign-in log (token protection)1003 Device state doesn't meet requirementsMicrosoft Entra sign-in log (token protection)1004 Apple: registration isn't hardware-backedMicrosoft Entra sign-in log (token protection)1006 OS version unsupportedMicrosoft Entra sign-in log (token protection)1008 Client isn't using the platform broker (WAM or the SSO plug-in)Microsoft Entra sign-in log (token protection)
Status (October 2026): Microsoft's platform table lists token protection for native applications as generally available on Windows, macOS and iOS/iPadOS, while support for browser-based apps (selected web apps that access Azure Resource Manager) is in preview on Windows and macOS. Parts of the documentation still label Apple support as preview, so re-check the Learn pages before planning an Apple rollout.
How it works and what you need#
When a device registers with Microsoft Entra ID it receives a Primary Refresh Token (PRT) that is cryptographically bound to that device — typically through the TPM on Windows and the Secure Enclave on Apple devices. With Require token protection for sign-in sessions enforced, Entra ID checks that supported apps present these bound sign-in session tokens when they request access to protected resources. Treat it as one layer of a defence-in-depth plan, not a replacement for phishing-resistant MFA and compliant devices.
| Requirement | Details |
|---|---|
| Licence | Microsoft Entra ID P1 |
| Protected resources | Exchange Online, SharePoint Online, Microsoft Teams; on Windows also Azure Virtual Desktop and Windows 365 |
| Windows devices | Windows 10 or later that are Microsoft Entra joined, hybrid joined or registered; Windows Server 2019 or later that is hybrid joined |
| Apple devices | macOS 14 or later and iOS/iPadOS 16 or later, MDM-managed, with the Microsoft Enterprise SSO plug-in |
| Windows apps (examples) | Outlook, Teams, OneDrive, OneNote, Word, Excel, PowerPoint, Windows App, Power BI Desktop, Visual Studio Code, Exchange PowerShell module |
Step-by-step: report-only first#
1. Create the policy#
- As a Conditional Access Administrator, open Entra ID › Conditional Access › Policies and select New policy.
- Users: include a pilot group; exclude your emergency access accounts.
- Target resources: select Office 365 Exchange Online, Office 365 SharePoint Online and Microsoft Teams Services (plus Azure Virtual Desktop, Windows 365 and Windows Cloud Login if you use Windows App). Don't pick the Office 365 app group here — Microsoft calls this out as an exception to the usual advice.
- Conditions › Device platforms: Windows (build a separate policy for iOS and macOS).
- Conditions › Client apps: only Mobile apps and desktop clients. Leaving Browser selected can block MSAL.js apps such as Teams on the web.
- Session: Require token protection for sign-in sessions, then set the policy to Report-only.
2. Exclude unsupported device types#
Several registration types are unsupported on Windows: Entra joined Azure Virtual Desktop session hosts and Windows 365 Cloud PCs, bulk-enrolled devices, Autopilot self-deploying devices, Power Automate hosted machine groups and Azure VMs joined through the VM extension. Surface Hub and Windows-based Teams Rooms aren't supported either. Exclude them with a Filter for devices set to Exclude filtered devices from policy, for example:
device.systemLabels -contains "CloudPC" -and device.trustType -eq "AzureAD"Swap the label for AzureVirtualDesktop (session hosts) or MicrosoftPowerAutomate (hosted machine groups), use device.profileType -eq "SecureVM" with the same trust type for Azure VMs, and device.enrollmentProfileName with your profile name for self-deploying Autopilot devices.
3. Close the platform gap#
The control only covers Windows and Apple platforms, so an attacker could claim to be another OS. Pair it with policies that block unknown platforms and require compliant devices on known ones — see a Conditional Access baseline.
4. Analyse, then enforce in waves#
Let report-only run long enough to cover normal usage, across both interactive and non-interactive sign-ins. Then move known-good users into an enforced copy of the policy and expand group by group. Testing with What If and report-only covers the general approach.
Verify in the sign-in logs#
In Entra ID › Monitoring & health › Sign-in logs, open a sign-in, select the Report-only (or Conditional Access) tab and check whether your policy's session control was satisfied. On Basic info, the Token Protection - Sign In Session field reads Bound or Unbound. A sign-in can contain several requests and all of them must be bound, so filter by user or correlation ID.
| Unbound code | Meaning | Action |
|---|---|---|
| 1002 | No Microsoft Entra device state | Register the device |
| 1003 | Device state doesn't meet requirements (unsupported registration type, or not registered with fresh credentials) | Exclude the device type, or re-register / upgrade the registration |
| 1004 | Apple: registration isn't hardware-backed | One-time registration upgrade at next prompt |
| 1006 | OS version unsupported | Update the OS |
| 1008 | Client isn't using the platform broker (WAM or the SSO plug-in) | Update or replace the app |
To see the whole picture, query the non-interactive log in Log Analytics:
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(7d)
| where isnotempty(TokenProtectionStatusDetails)
| extend tp = parse_json(TokenProtectionStatusDetails)
| extend Status = tostring(tp.signInSessionStatus), Code = tostring(tp.signInSessionStatusCode)
| where Status =~ "unbound"
| summarize Requests = count(), Users = dcount(UserPrincipalName) by AppDisplayName, Code
| sort by Requests descGood looks like: the apps your users rely on are overwhelmingly bound, every remaining unbound row has an explanation (an excluded device type, an app you're upgrading), and once enforced, users on supported devices notice nothing.
Tips & gotchas#
- Old clients and add-ons. Office perpetual clients aren't supported. PowerShell modules that access SharePoint, Visual Studio Code extensions that access Exchange or SharePoint, and the Power Query extension for Excel outside Current Channel get blocked. Inventory them before enforcing.
- Apple native Mail and Calendar don't support token protection and are blocked once enforced — warn users first.
- Older registrations. Users whose devices registered before the policy may get a one-time prompt to upgrade; codes 1003 and 1004 on Apple devices are self-remediable.
- Guests. External users are supported only if their home-tenant device registration meets the requirements; otherwise they see an unclear error.
- Old queries. The session control value in sign-in data changed from
BindingtoSignInTokenProtection; check both in saved KQL. For more on the logs, see reading sign-in logs like an engineer.

