Device Code Flow Phishing: How Attackers Steal M365 Tokens While Bypassing MFA — and How to Stop Them
Device code flow phishing lets attackers take over M365 accounts even when MFA is fully enforced — the victim authenticates on a real Microsoft page, the attacker gets the tokens. Storm-2372 has been running this campaign since August 2024. Learn how to detect existing compromise in your Entra ID logs, block the attack via Conditional Access, and avoid breaking Teams Rooms in the process.

Device Code Flow Phishing: How Attackers Steal M365 Tokens While Bypassing MFA — and How to Stop Them
TL;DR: Device code flow phishing lets attackers take over M365 accounts even when MFA is fully enforced. The victim authenticates on a real Microsoft page — the attacker gets the tokens. Storm-2372 (Russia-linked) has been running this campaign since August 2024, with scale exploding through PhaaS kits in 2026. Blocking via Conditional Access takes five minutes, but the edge cases around Teams Rooms and admin tooling will bite you if you skip the prep work.
The Problem
A user files a ticket: suspicious sign-ins from Southeast Asia at 3 AM. You pull the Entra ID sign-in logs — successful authentication, MFA completed, no risk flag. Impossible travel detection did not fire. How?
This is device code flow phishing. The user received a message — via Teams, WhatsApp, or email — asking them to enter a short code at microsoft.com/devicelogin to confirm access to a document or meeting invite. They went to the real Microsoft page, typed the code, signed in with their real password, and approved the MFA prompt. A completely legitimate authentication flow. Except the code was generated by the attacker, and the access tokens went straight to the attacker's polling server.
Since August 2024, Microsoft has been tracking Storm-2372 — a Russia-linked threat actor — actively exploiting this technique against governments, NGOs, technology firms, defense contractors, and energy companies across Europe, North America, and the Middle East. In February 2025 the campaign evolved: attackers began abusing the Microsoft Authentication Broker client ID (29d9ed98-a469-4536-ade2-f981bc1d605e) to register attacker-controlled devices and steal Primary Refresh Tokens (PRTs), which provide SSO-level access across all M365 resources.
By 2026, Phishing-as-a-Service kits like EvilTokens have automated device code phishing at industrial scale. Lure templates impersonate Adobe Acrobat Sign, DocuSign, OneDrive shared files, SharePoint access requests, email quarantine notices, and calendar invites. Hundreds of organizations are compromised daily.
Why This Works
Device code flow (RFC 8628) was designed for a legitimate and specific purpose: enabling authentication on devices without keyboards or browsers — printers, Smart TVs, Xbox consoles, IoT devices, Microsoft Teams Rooms conference systems.
The legitimate flow:
- The device (e.g., a smart TV) sends a request to Entra ID and receives a
device_code(8-10 characters) and auser_code - The TV displays: "Go to microsoft.com/devicelogin and enter code XXXX-XXXX"
- The user authenticates in their browser — with password and MFA prompt
- The TV polls the token endpoint every few seconds and receives
access_token+refresh_tokenafter successful user auth
The attack vector: Step 1 can be executed by anyone, not just a legitimate device. An attacker generates the device code, sends the user_code to the victim via phishing, and waits for the tokens to arrive. The victim sees login.microsoftonline.com — the real site, with Microsoft's TLS certificate. They authenticate normally. Tokens go to the attacker.
Critical technical details that make detection hard:
- Resulting tokens carry
amr: ["mfa"]in their claims — Entra ID sign-in logs show a normal, MFA-protected sign-in with no anomaly indicator. - The device code expires in 15 minutes, but attackers generate fresh codes and send additional lures until one lands.
- Once the attacker holds a refresh token, it is valid for 90 days by default and silently renews access tokens without any further user interaction.
- The new February 2025 variant: after receiving tokens, Storm-2372 authenticates through Microsoft Authentication Broker to register an attacker-controlled device and obtain a PRT — which gives SSO access that persists even if you revoke the original refresh token.
Remediation: Step by Step
Step 1: Check if Microsoft Already Deployed the Managed Policy
Since 2025, Microsoft has been rolling out a managed Conditional Access policy called "Block device code flow" to tenants as part of the Secure Future Initiative. Do not assume it is protecting you — check its actual state:
- Entra Admin Center → Protection → Conditional Access → Policies
- Look for "Block device code flow" — it may be labeled "Microsoft-managed"
- Check the state:
Report-onlyorOn
If it is in Report-only — you are not protected. The policy is logging what it would block, not actually blocking anything. Switch it to On after reading the edge cases section below.
Step 2: Hunt for Existing Compromise in Sign-in Logs
Before implementing the block, determine whether device code phishing has already hit your tenant. Run this KQL in Microsoft Sentinel or Log Analytics:
SigninLogs
| where TimeGenerated > ago(90d)
| where AuthenticationProtocol == "deviceCode"
| project TimeGenerated, UserPrincipalName, AppDisplayName,
IPAddress, Location, RiskLevelDuringSignIn
| order by TimeGenerated desc
Expected (benign) results: sign-ins from Azure CLI, Az PowerShell, Connect-MgGraph — from admin IP addresses during business hours. Investigate immediately: sign-ins from unfamiliar countries, unknown application names, or outside working hours.
Step 3: Build an Exclusion Group Before Blocking
Identify every account that legitimately uses device code flow in your environment:
- Teams Rooms resource accounts on Android-based devices
- Teams Phone, Teams Panel, Teams Display device accounts
- Printers and IoT devices authenticating against M365
- Service accounts used by Azure CLI or PowerShell automation (temporary — migrate these off device code)
Create a dedicated group in Entra ID: SG-DeviceCodeFlow-Allowed. Add only those accounts plus your break-glass accounts. Do not skip this step — enabling the policy without it will generate incident tickets from meeting room users within the hour.
Step 4: Create the Blocking Policy via Portal
- Entra ID → Conditional Access → New policy
- Name: Block Device Code Flow — All Users
- Users: All users
- Exclude:
SG-DeviceCodeFlow-Allowed+ break-glass accounts - Conditions → Authentication flows → Configure: Yes → Device code flow: Yes
- Grant → Block access
- State: Report-only for 7-14 days
During the report-only period, open the Conditional Access Insights and Reporting workbook and check which sign-ins would have been blocked. Add any unexpected legitimate accounts to the exclusion group before going live.
Step 5: Deploy via PowerShell for Repeatability
# Requires: Microsoft.Graph.Identity.SignIns module
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess","Policy.Read.All"
$excludeGroupId = (Get-MgGroup -Filter "displayName eq 'SG-DeviceCodeFlow-Allowed'").Id
$params = @{
displayName = "Block Device Code Flow - All Users"
state = "enabledForReportingButNotEnforced"
conditions = @{
users = @{
includeUsers = @("All")
excludeGroups = @($excludeGroupId)
}
authenticationFlows = @{
transferMethods = "deviceCodeFlow"
}
}
grantControls = @{
operator = "OR"
builtInControls = @("block")
}
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $params
After 14 days in report-only mode, promote to enforced:
$policy = Get-MgIdentityConditionalAccessPolicy |
Where-Object { $_.DisplayName -eq "Block Device Code Flow - All Users" }
Update-MgIdentityConditionalAccessPolicy `
-ConditionalAccessPolicyId $policy.Id `
-BodyParameter @{ state = "enabled" }
Step 6: Revoke Compromised Sessions
If Step 2 surfaced suspicious sign-ins, take immediate action:
# Revokes all refresh tokens — attacker access dies on next token refresh
Revoke-MgUserSignInSession -UserId "user@contoso.com"
# Audit devices registered by this user in the last 30 days
Get-MgUserRegisteredDevice -UserId "user@contoso.com" |
Select-Object DisplayName, OperatingSystem, ApproximateLastSignInDateTime
Critical timing note: Revoke-MgUserSignInSession invalidates refresh tokens immediately but does nothing to existing access tokens. Those remain valid for up to 1 hour. During that window an attacker holding a valid access token can still call Microsoft Graph, Exchange Online, or SharePoint APIs.
Common Traps and Edge Cases
Trap 1: Teams Rooms and Teams Phones Go Offline
This is the most common post-deployment incident, and it happened at scale when Microsoft rolled out the managed policy in 2025. Teams Android devices — Teams Rooms on Android, Teams Phones, Teams Panels, Teams Displays — all use device code flow to authenticate their resource accounts. These devices do not support interactive browser authentication.
If devices have gone offline after your policy activated:
- Identify affected resource accounts:
Get-CsOnlineUser | Where-Object { $_.AccountType -eq "ResourceAccount" } - Add them to
SG-DeviceCodeFlow-Allowed - Wait for policy propagation (up to 10 minutes), then reboot devices 1-3 times
- If rebooting does not restore sign-in, a factory reset is required — the device stores invalid authentication state that a simple reboot cannot clear
Trap 2: You Lock Out Azure CLI, Az PowerShell, and Graph PowerShell
az login, Connect-AzAccount -UseDeviceCode, Connect-MgGraph -UseDeviceCode, Connect-ExchangeOnline -UseDeviceCode — all use device code flow. The admin who deploys the block policy on Monday may find their automation scripts failing on Tuesday. The error: AADSTS53003: Access has been blocked by Conditional Access policies.
Long-term fix: migrate automation to Managed Identity (for Azure-hosted workloads) or Service Principal + certificate — neither uses device code flow. For interactive sessions, switch to browser-based auth: Connect-MgGraph without -UseDeviceCode opens a browser window for interactive sign-in. During migration, temporarily add automation accounts to the exclusion group.
Trap 3: The Microsoft-Managed Policy Is Not Enforced by Default
Three facts every admin needs to know:
- Its default state upon first deployment to your tenant is
Report-only— Microsoft does not switch it to enforced on your behalf - Once you manually change its state, Microsoft stops auto-managing it and the policy stays exactly where you left it
- Rollout is gradual and staggered — your tenant may not have the policy while a neighboring tenant does
Do not rely on it as your sole protection. Create an independent policy under your own control and treat the Microsoft-managed one as a secondary signal.
Trap 4: Blocking Device Code Flow Does Not Undo Existing PRT Compromises
Since February 2025, Storm-2372 has evolved to use the Microsoft Authentication Broker during device code phishing to register an attacker-controlled device and obtain a Primary Refresh Token. A PRT provides SSO access to all M365 resources. If an attacker obtained a PRT before your policy was activated, the block does nothing retroactively.
Audit device registrations from the past 30 days:
Get-MgDevice -All |
Where-Object { $_.ApproximateLastSignInDateTime -gt (Get-Date).AddDays(-30) } |
Select-Object DisplayName, OperatingSystem, ApproximateLastSignInDateTime,
@{Name="Owner"; Expression={
(Get-MgDeviceRegisteredOwner -DeviceId $_.Id).AdditionalProperties.userPrincipalName
}} |
Sort-Object ApproximateLastSignInDateTime -Descending
Any device registered in the last month that you do not recognize — especially with operating systems outside your standard fleet — is a candidate for immediate deletion and further investigation. After deleting a suspicious device, revoke the associated user's sign-in sessions.
Summary
- Device code flow phishing bypasses MFA — the victim performs real, legitimate MFA; tokens go to the attacker; Entra ID logs show a clean sign-in with no anomaly indicators
- Storm-2372 (Russia-linked) has been running this campaign since August 2024; the attack surface exploded through PhaaS kits in 2026
- Blocking via Conditional Access requires Entra ID P1 minimum — included in M365 Business Premium, E3, and E5 plans
- Before turning on the policy: run the KQL hunt across 90 days of sign-in logs and build an exclusion group for Teams Rooms resource accounts
- The Microsoft-managed policy defaults to
Report-only— verify its state is actuallyOn, do not assume - After any suspected incident: revoke sign-in sessions, then audit device registrations from the past 30 days — the PRT-theft variant requires this extra step