You know that you can use Azure AD Conditional Access to secure access to your resource by enforcing MFA, device compliance…
Well, Azure AD Conditional Access has been updated to allow you use Token Protection.
Token Protection attempts to reduce attacks using token theft by ensuring a token is usable only from the intended device.
Token protection creates a cryptographically secure tie between the token and the device (client secret) it’s issued to. Without the client secret, the bound token is useless.
Support for Token Protection with Conditional Access is available for desktop applications running on Windows accessing Exchange and SharePoint Online during the preview.
To be able to use Token Protection with Conditional Access you need to ensure the following requirements are met:
- Windows 1x either AAD Joined, Hybrid Joined or AAD Registered
- OneDrive client running version 22.217 or later
- Teams client running version 1.6.00.1331 or later
- Office click to run; perpetual client are not supported
In addition of these requirements, the following limitations apply (at least during the preview):
- Guest accounts are not supported; when implementing Token Protection, guests must be excluded
- The following applications are not supported and users are blocked when accessing Exchange and/or SharePoint from them:
- Power BI Desktop client
- PowerShell modules accessing Exchange, SharePoint, or Microsoft Graph scopes that are served by Exchange or SharePoint
- PowerQuery extension for Excel
- Extensions to Visual Studio Code which access Exchange or SharePoint
- Visual Studio
Now you are ready to start implementing Token Protection – recommended to first start in Report only mode to be able to review if there is any side effects for your users.
Access your Azure AD portal (https://aad.portal.azure.com/) to access the Active Directory\Security\Conditional Access blade or Entra portal (https://entra.microsoft.com/) to access the Protect and secure\Conditional Access blade
From there you can create the new conditional access rule as per below:
- Users: all users (or any users/groups or roles you wish) except guest or external and break the glass account(s)
- Cloud apps: Office 365 Exchange Online and Office 365 SharePoint Online
- Conditions:
- Device platforms: Windows
- Client apps: enabled with Mobile apps and desktop clients
- Access controls:
- Session: Require token protection for sign-in sessions
And now you can enable in report only mode for reviewing before enforcing the rule
To review the sign-in logs, you can use the Azure Active Directory\Sign-in logs blade (AAD portal) or Monitoring & health\Sign-in logs blade (Entra portal)
or you can also use the below KQL queries to query Log Analytics
- Highlight blocked vs allowed requests by application
//Per Apps query
// Select the log you want to query (SigninLogs or AADNonInteractiveUserSignInLogs )
//SigninLogs
AADNonInteractiveUserSignInLogs
// Adjust the time range below
| where TimeGenerated > ago(7d)
| project Id,ConditionalAccessPolicies, Status,UserPrincipalName, AppDisplayName, ResourceDisplayName
| where ConditionalAccessPolicies != “[]”
| where ResourceDisplayName == “Office 365 Exchange Online” or ResourceDisplayName ==”Office 365 SharePoint Online”
//Add userPrinicpalName if you want to filter
// | where UserPrincipalName ==”<user_principal_Name>”
| mv-expand todynamic(ConditionalAccessPolicies)
| where ConditionalAccessPolicies [“enforcedSessionControls”] contains ‘[“Protection”]’
| where ConditionalAccessPolicies.result !=”reportOnlyNotApplied” and ConditionalAccessPolicies.result !=”notApplied”
| extend SessionNotSatisfyResult = ConditionalAccessPolicies[“sessionControlsNotSatisfied”]
| extend Result = case (SessionNotSatisfyResult contains ‘Protection’, ‘Block’,’Allow’)
| summarize by Id,UserPrincipalName, AppDisplayName, Result
| summarize Requests = count(), Users = dcount(UserPrincipalName), Block = countif(Result == “Block”), Allow = countif(Result == “Allow”), BlockedUsers = dcountif(UserPrincipalName, Result == “Block”) by AppDisplayName
| extend PctAllowed = round(100.0 * Allow/(Allow+Block), 2)
| sort by Requests desc
- Highlight blocked vs allowed requests by user
//Per users query
// Select the log you want to query (SigninLogs or AADNonInteractiveUserSignInLogs )
//SigninLogs
AADNonInteractiveUserSignInLogs
// Adjust the time range below
| where TimeGenerated > ago(7d)
| project Id,ConditionalAccessPolicies, UserPrincipalName, AppDisplayName, ResourceDisplayName
| where ConditionalAccessPolicies != “[]”
| where ResourceDisplayName == “Office 365 Exchange Online” or ResourceDisplayName ==”Office 365 SharePoint Online”
//Add userPrincipalName if you want to filter
// | where UserPrincipalName ==”<user_principal_Name>”
| mv-expand todynamic(ConditionalAccessPolicies)
| where ConditionalAccessPolicies.enforcedSessionControls contains ‘[“Protection”]’
| where ConditionalAccessPolicies.result !=”reportOnlyNotApplied” and ConditionalAccessPolicies.result !=”notApplied”
| extend SessionNotSatisfyResult = ConditionalAccessPolicies.sessionControlsNotSatisfied
| extend Result = case (SessionNotSatisfyResult contains ‘Protection’, ‘Block’,’Allow’)
| summarize by Id, UserPrincipalName, AppDisplayName, ResourceDisplayName,Result
| summarize Requests = count(),Block = countif(Result == “Block”), Allow = countif(Result == “Allow”) by UserPrincipalName, AppDisplayName,ResourceDisplayName
| extend PctAllowed = round(100.0 * Allow/(Allow+Block), 2)
| sort by UserPrincipalName asc
Your KQL queries don’t work
I just tried again and it works
Just ensure the double-quote and quote are correct; sometime the direct copy/paste does not use the correct quote and double-quote like shown below
”Office 365 SharePoint Online” while it should be “Office 365 SharePoint Online“