Someone on your team gets a Teams meeting invite, types the "meeting ID" from the email into microsoft.com/devicelogin, approves the MFA prompt and sees nothing wrong. The person who sent that code now holds a signed-in session for the account, password and MFA included. This post explains what the device code flow is for, how campaigns like Storm-2372 and EvilTokens turn it into a login handed over on a plate, and the Entra Conditional Access policy that shuts it down.
What the Device Code Flow Is For
Some devices need to sign in and have no browser to do it with. A smart TV, a printer, a command-line tool on a locked-down server. The OAuth 2.0 device authorization grant, written up as RFC 8628, solves that by splitting the login across two devices.
Microsoft's implementation runs like this. The app asks the identity service for a code by posting its client ID and the permissions it wants to the devicecode endpoint. It gets back two codes, a short one for the human and a long one for itself, plus the address to send the human to. On Entra that address is microsoft.com/devicelogin and the short code is good for 15 minutes by default, per Microsoft's protocol reference.
The person opens that page on a phone or laptop, types the short code, signs in and clears MFA like any other day. Meanwhile the app polls the token endpoint every few seconds with its long code and gets "authorization pending" until the sign-in completes. Then it gets an access token that lives about an hour and, if it asked for offline access, a refresh token that keeps the session alive long after that.
Notice what never happens. The device that ends up holding the token never sees the password. The sign-in page never sees the device. That separation is the feature, and it's also the hole.
How the Flow Gets Abused
Device code phishing is what you get when the attacker plays the part of the app. They request a code from Microsoft, using the client ID of a real Microsoft application so nothing looks foreign in the logs. They mail the short code to the target wrapped in a reason to type it: a Teams meeting invite with the code as the meeting ID, a shared document, a voicemail notification, an RFP. The target lands on the real Microsoft page, types the code, signs in and approves MFA.
At that moment the attacker's polling loop stops getting "pending" and gets the tokens. No fake login page was built, no proxy sat in the middle, and the target typed nothing into anything the attacker controls. Of the four routes covered in our credential harvesting explainer, this is the one that leaves the least evidence on the user's side.
Microsoft Threat Intelligence described the playbook in its Storm-2372 report on February 13, 2025. The group built rapport first, posing as a known figure over WhatsApp, Signal or Teams, then sent the invite. Once in, it used Microsoft Graph to search the mailbox for words like "password", "admin", "teamviewer", "anydesk" and "credentials", pulled the matches out, and sent fresh device code lures to colleagues from the victim's own account.
A day after the report, Microsoft saw the group switch to the client ID of the Microsoft Authentication Broker. That matters because the broker's refresh token can be traded for a token to the device registration service, which lets the attacker register their own machine in the victim's tenant and collect a Primary Refresh Token. At that point the attacker isn't a stolen session anymore. They're a device.
From Storm-2372 to EvilTokens
The technique went from a state-aligned operation to a product in about a year. Storm-2372 had been running since August 2024 when Microsoft published, and in July 2026 Microsoft folded the group into Midnight Blizzard as an initial-access sub-cluster. By February 2026 a phishing-as-a-service kit called EvilTokens was selling the same flow to anyone with a budget.
Huntress tracked one EvilTokens-driven campaign that reached 344 organizations across the United States, Canada, Australia, New Zealand and Germany, with the token harvesting hosted on Railway, a legitimate platform-as-a-service provider, per its device code phishing write-up from September 2026. Victims ranged from law firms and construction companies to public safety agencies.
Microsoft's April 6, 2026 analysis of the AI-enabled campaign explains why the newer kits land more often. Early attacks embedded a pre-generated code in the email, so a target who opened it 20 minutes later found the code expired. The kits now generate the code when the target clicks the link, so the 15-minute clock starts at the moment of interest, and a script copies the code to the clipboard to save the victim the typing. Lures are written per role, with invoice, e-signature and password-expiry themes, and some actors register a device within minutes of the sign-in.
Why MFA Does Not Stop It
MFA did its job. The real user signed in on the real page and approved the real prompt. The token Microsoft issued carries the MFA claim, and every downstream app trusts it. As u/Sunsparc put it in the r/sysadmin thread above, the sign-in log entry reads "MFA requirement satisfied by claim in the token." A passkey makes the user's half stronger and changes nothing about who receives the result.
The other usual defenses miss for the same reason. A password reset invalidates the password, not the refresh token already in the attacker's hands. The email filter sees a link to microsoft.com and lets it through. The user who was trained to check the URL checks it, and it's correct.
What does stop it is refusing to issue the token in the first place. Microsoft's own documentation says to get as close as possible to a unilateral block on the flow, and that's the control the rest of this post is about. The email security practices that stop business email compromise still matter, because the lure still arrives by mail, but they're the second line here, not the first.
Blocking It With Conditional Access
The policy is short. In the Entra admin center go to Conditional Access, create a new policy, include all users and exclude your break-glass accounts, target all resources, open Conditions, switch on Authentication flows, tick Device code flow, and set Grant to Block access. Microsoft's how-to page says to create it in report-only mode first, read the impact, then turn it on.
Microsoft also ships a managed version. Its Entra blog announced in February 2025 that tenants with no device code sign-ins in the previous 25 days would get a "Block device code flow" policy created for them, in report-only mode to start. Check Conditional Access under Microsoft-managed policies to see whether yours already has one and what state it's in.
The thing that breaks is Teams hardware. Teams Rooms on Android, Teams Phones, Panels and Displays use the device code flow to register and to re-authenticate after a password change. When the managed policy switched to enforced in July 2025, admins found their rooms signed out, as Petri reported on July 3, 2025, and the thread below is one of them finding out the hard way.
The fix is not to turn the policy off. Microsoft's Teams device guidance, updated May 2026, is to keep the block for everyone, put the Teams device resource accounts in a persistent exception group, and exclude the Device Registration Service resource from the policy so registration still works. The exception is per account, not per app, so keep the group to genuine room and phone accounts. One r/sysadmin commenter adds a sensible layer: those room accounts can only sign in from trusted locations. Azure CLI and other developer tools that still use the flow should move to brokered sign-in or managed identities rather than earn an exception.
Hunting and Cleanup
Before you block, inventory. Filter the Entra sign-in logs on Authentication protocol equals Device code flow and you get every account and app that used it, which is your exception list and your audit trail in one. After you block, watch Original transfer method equals Device code flow as well, because a session that began with a device code stays tagged that way on later token refreshes even when the current event doesn't show the protocol.
Microsoft's hunting guidance keys on one pattern: a sign-in error 50199 followed by a success for the same session within the hour. The error is the pause while the user goes to find the code in the email. Pair that with a URL click on microsoft.com/devicelogin from a rare sender and you have the campaign in your own logs.
If an account is already compromised, revoke before you reset. Call revokeSignInSessions through Graph to kill the refresh tokens, and because access tokens already issued keep working for about an hour, Microsoft's April 2026 advice is to disable the account for that window. Then check for new device registrations, new inbox rules and unusual mailbox reads, and only then reset the password. The order matters: a reset on its own leaves the refresh token alive, which is the gap our ATO prevention guide covers in more depth.
The Short Version
The device code flow is a legitimate login for devices with no browser, and attackers use it because the login is legitimate. The user signs in on Microsoft's page, MFA is satisfied for real, and the token lands with whoever requested the code. The only clean fix is a Conditional Access policy that blocks the flow for everyone except a documented, monitored list of Teams devices, rolled out in report-only mode first so the meeting rooms stay signed in.
OpenFrame's agents read endpoint state through osquery, and this attack never touches the endpoint, which is the point: the fix lives in your identity provider, not on the device. If you're mapping the rest of the identity surface, start with SAML vs SSO for where the device code grant sits among the other OAuth flows, then the credential harvesting and ATO prevention posts linked above for the routes that do touch the user's machine.

Aliaska Varieva
Head of Platform
Hi! I’m Aliaska, and I’ve been working as a software engineer (mostly Java + a bit Kotlin) for over 8 years now. I mostly spend my time building backend services, integrating systems, fixing bugs (the fun part 🙃), and making sure things don’t fall apart behind the scenes.
