Your Device Code Flow Alert Fires on Every az login. The Signal Is the Broker Token, Not the Prompt
Device code phishing is the identity attack that never touches a credential. There’s no lookalike login page, no reverse proxy sitting between the victim and Microsoft, no replayed session cookie for Conditional Access to catch on a device-binding claim. The attacker starts a legitimate OAuth device authorization flow (RFC 8628), hands the victim a real Microsoft URL and a short user code, and lets them authenticate against the genuine endpoint. MFA prompt satisfied. FIDO2 key tapped. Passkey approved. All of it, on Microsoft’s own infrastructure. Then the attacker polls the token endpoint and collects the access and refresh tokens the victim just minted. The entire trick is social: convince someone that typing a code at microsoft.com/devicelogin is a normal step in joining a Teams call or an IT support session.
This is why passkeys don’t save you the way the marketing implies, and why token protection only partly does. The victim completes a real, compliant, phishing-resistant authentication against the genuine origin — so a passkey or FIDO2 key does exactly its job and still hands the attacker a usable token. The attack doesn’t cryptographically defeat MFA; it redirects a legitimately MFA-completed authorization, the way the Cloud Security Alliance frames it: it doesn’t beat MFA, it reroutes it. Token Protection is the one control that actually bites here — it binds sign-in session tokens to the device, so a token replayed from the attacker’s box fails — but its coverage is narrow: native apps only, a short list of resources (Exchange, SharePoint, Teams, plus Azure Virtual Desktop and Windows 365 on Windows), and specific platforms. It reduces replay where it applies; it doesn’t replace blocking the flow where you don’t need it. The device code flow was designed for input-constrained devices — smart TVs, conference room displays, headless CLIs — and the attacker is abusing the design intent, not defeating a control. There’s no downgrade to detect. Nothing looks broken.
And it’s having a moment. Microsoft attributed the original Storm-2372 campaign to a Russia-aligned actor in February 2025, targeting government, defense, telecom, and energy across Europe and North America. What changed in 2026 is that the technique went commodity. Per the Cloud Security Alliance’s April research note, device code phishing pages jumped roughly 37.5x by early April 2026, and a single EvilTokens phishing-as-a-service campaign running February through March compromised 340-plus Microsoft 365 organizations across the US, Canada, Australia, New Zealand, and Germany. Financially motivated crews picked up a nation-state playbook, and the phishing kit is now a Telegram subscription.
The detection everyone writes first, and why it floods
Open your SIEM and the obvious query is right there. In Microsoft Sentinel’s SigninLogs, you filter AuthenticationProtocol == "deviceCode". In the Graph interactive sign-in schema — and in Defender advanced hunting — the same flow is tagged originalTransferMethod == "deviceCodeFlow". (Yes, one flow, two field names across two Microsoft schemas. This is exactly the kind of thing that breaks a detection you copy-pasted from a blog post that used the other table.)
Run that filter and you’ll get a wall of hits. Because device code flow is legitimate almost everywhere a cloud engineering team operates. az login --use-device-code on a jump box with no browser. kubectl against an AKS cluster with OIDC. GitHub CLI. Terraform in a CI runner. PowerShell Connect-MgGraph on a locked-down admin workstation. Every conference room Surface Hub and every digital signage panel that enrolled through the Intune device flow. In a shop with a healthy platform team, deviceCode sign-ins are background noise, not an anomaly. Alert on the raw flow and you’ve built a false-positive generator that the SOC will mute by end of week one.
So the flow is not the signal. The redemption is.
What actually separates the phish from the pipeline
The distinguishing behavior lives in what the attacker does with the token, not that a device code flow happened. Storm-2372 evolved to request the device code against a specific client: the Microsoft Authentication Broker, appId 29d9ed98-a469-4536-ade2-f981bc1d605e. That client matters because the refresh token it hands back can be used to register an attacker-controlled device in Entra ID, and a registered device is the doorway to a Primary Refresh Token — durable, broad, and far harder to knock out than a single access token. Watch for that Broker appId followed by a call to the Device Registration Service (appId 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9) and/or the Microsoft Intune Enrollment resource (appId d4ebce55-015a-49b5-a083-c84d1797ae8c) — which one depends on the enrollment path — showing up right after a device code sign-in.
The correlation window is the thing to build around:
- A
deviceCode/ Authentication Broker sign-in, followed within one to two days by a device registration event (Add devicein the audit log, or a new join in the device inventory) for a user who has never registered a device before. - A geographic split between where the device code was authorized and where the resulting tokens get used. The victim taps their phone in Ohio; the attacker polls the token endpoint from a hosting IP somewhere else. Don’t rely solely on one
sessionIdto straddle both ends — later token use often surfaces as separate, non-interactive activity, and the correlation has to reach it. But don’t discount the session either: whenSessionIdorCorrelationIddoes tie the victim’s browser and the polling client into one record, multiple IPs or user agents inside that single session is high-value evidence. Elastic ships a rule built on exactly this — concurrent device-code sign-ins with more than one user agent or address in the same session — and it’s a far more specific signal than any rawdeviceCodequery. Correlate the redemption to later sign-ins by the same user and client as well, so you still catch the case where no single session record spans both geographies. - An elevated
RiskLevelDuringSignInon the redemption itself — Identity Protection flagging the polling client’s IP or properties as risky. Treat this as a corroborator, not a trigger: it depends on Identity Protection licensing and is frequently null for device code flows, so it firms up a case built on the other two indicators rather than standing on its own.
None of these alone is conclusive. A user genuinely does enroll their first device sometimes. A VPN, a corporate SASE or egress proxy (Zscaler, Netskope), or a mobile carrier NAT can smear a session across geographies. The detection has to require two or three of these firing together on the same identity inside a short window, and even then you’re triaging, not blocking.
If you want IOCs to seed a hunt, the March EvilTokens campaign leaned on Railway.com hosting — 162.220.234.41, 162.220.234.66, 162.220.232.57 were named in the CSA writeup. Useful for a retro-hunt, useless for tomorrow. Hosting IPs rotate faster than you can maintain the list, and building a detection around them is how you end up with a threat-intel feed nobody’s refreshed since the analyst who owned it left.
The first round of tuning is an allowlist you don’t want to build
Here’s where it gets annoying, and where most teams stall. The Authentication Broker is not inherently malicious — it’s the legitimate path for real Intune device enrollment. So you can’t just alert on appId 29d9ed98. You have to baseline it. Which known device-code apps are normal in your tenant, from which IP ranges, for which users? Your CI runners have stable egress; carve those out by IP. Your platform engineers use az login --use-device-code from a known bastion; that’s a suppression. Legitimate corporate Intune enrollment happens from managed subnets and correlates with an actual hardware purchase and a helpdesk ticket, not a phishing lure.
What survives after you strip the known-good is a much smaller set: device code redemptions against the Broker, from residential or hosting IPs, for users with no enrollment history, that don’t line up with any provisioning activity. That’s your queue. Expect the first pass to be noisy for a week or two while you learn which of your own tools use the flow — the tuning is discovering your own environment, not the attacker’s.
A note on volume and cost, since this is where lab detections die at scale. SigninLogs in a tenant of any size is a heavy table, and the interactive plus non-interactive streams together can dwarf your other ingest. If you’re paying to keep 90 days hot for this correlation, price it before you commit. Near-real-time correlation only needs a hot window wide enough to hold the day-or-two gap between the sign-in and the device-registration join — but retro-hunts run weeks after the fact, and that’s where a short hot window leaves you pulling the originating sign-in out of a slower, cheaper tier. Push the older data to a cheaper tier and accept that retro-hunts against it will be slower.
The real fix is a policy, and the detection is just buying time until it lands
Detection matters, but device code flow is one of the few identity attacks with a clean structural remedy. Conditional Access now exposes an authentication flows condition. Set Conditions > Authentication Flows > Device code flow to blocked, scope it to everyone you can, and the attack surface collapses for those users. Microsoft’s own guidance is to block device code flow wherever you don’t explicitly need it, and they’re right. Most organizations need it for a handful of shared-device and CLI scenarios and nowhere else. Enumerate those, exclude them into a tightly scoped group, and block the rest.
Two caveats that will bite you if nobody warns you.
The first is a genuine gotcha in the sign-in logs: you may see a successful deviceCodeFlow entry — OriginalTransferMethod = deviceCodeFlow — even with the block in place. Part of that is protocol tracking: once a session is established through device code flow, that state rides along through refreshes, so a later sign-in whose immediate request was not a fresh device-code authorization can still carry the original transfer method. On top of that, Conditional Access is evaluated at token issuance against a resource, so a refresh token minted before you enabled the policy keeps working until it expires, and a sign-in against an app or client combination outside your policy scope won’t get caught. Before you panic that your block failed, open the specific sign-in event, check the Conditional Access tab, and read whether the policy shows Applied, Not applied, or Excluded. Nine times out of ten it’s a protocol-tracked refresh, a pre-existing token, or a scope gap, not a bypass. Which is also why enabling the block without revoking existing sessions leaves a window open.
The second is that this is a place Continuous Access Evaluation quietly lets you down. CAE gives you near-real-time revocation on critical events, but its coverage for B2B guests is limited and inconsistent across resources, and the propagation isn’t instantaneous for IP-based conditions. If your device code phish lands on a B2B guest — and cross-tenant collaboration is exactly where these campaigns like to fish — the fast-revocation story you’re relying on doesn’t fully apply. Plan the manual revocation path.
On a confirmed hit, the sequence is: revoke the refresh tokens (revokeSignInSessions on the user), force credential and MFA re-registration, then go straight to the device inventory and look for a device the user didn’t register. If the Broker/DRS activity or the audit log shows an attacker-controlled device registration, remove it — token revocation without pulling a registered device is a half-remediation, because the PRT path stays open and you’ll be back in the same incident in a week. Not every device-code phish gets that far, though, so let the evidence decide: where there’s no registration, token revocation may be enough — but confirm that from the logs rather than assume it, because assuming it is how the registered-device path gets left open. While you’re in the audit log, check for OAuth consent grants or new credentials added to a service principal in the same window; device registration is this campaign’s signature persistence, but a valid token doesn’t have to take that path to leave a way back in.
Where it maps
| Control | Why it applies |
|---|---|
| IA-2 | MFA is satisfied legitimately, so the control is intact but insufficient; device code phishing is the case that proves MFA alone doesn’t establish session provenance. |
| AC-12 | Session termination — revokeSignInSessions and CAE-driven revocation are the levers, and their gaps (guests, pre-existing tokens) are the exposure. |
| AC-2 | Account monitoring for anomalous device registration tied to an account with no enrollment history. |
| CM-7 | Least functionality — blocking device code flow where it isn’t needed is the structural fix; the attack surface only exists because the flow stays enabled for users who never touch it. |
| SI-4 | The sign-in and audit log correlation itself: the flow, the Broker redemption, the device add. |
| AU-6 | Log review that survives contact with volume — the tuning that separates deviceCode noise from the redemption signal. |
| CA-7 | Continuous monitoring, and the honest accounting of where CAE’s real-time claim breaks. |
Block the flow where you don’t need it. Detect it where you must allow it. And treat a device code sign-in against the Authentication Broker, from an IP you don’t recognize, for a user who’s never owned a registered device, as the start of a device-registration story — not a login you can close as benign.
Sources
- Storm-2372 conducts device code phishing campaign (Microsoft Security Blog)
- OAuth device code phishing: 37x surge in enterprise ATO (Cloud Security Alliance)
- OAuth device code phishing hits 340+ Microsoft 365 organizations (Cloud Security Alliance)
- 3 recent OAuth TTPs and how to detect them with Entra ID logs (Wiz)
- Microsoft Entra ID OAuth phishing and detections (Elastic Security Labs)
- Token protection in Microsoft Entra Conditional Access (Microsoft Learn)
- Block authentication flows with Conditional Access policy (Microsoft Learn)
- Authentication flows as a condition in Conditional Access policy (Microsoft Learn)
- Continuous access evaluation in Microsoft Entra (Microsoft Learn)
- Entra login shows a successful deviceCodeFlow login when globally blocked in Conditional Access (Microsoft Q&A)
- Device code phishing hits 340+ Microsoft 365 orgs across five countries via OAuth abuse (The Hacker News)