Device code phishing works because the victim signs in on a genuine Microsoft page, so there is no fake login to spot and no look-alike domain to block. The practical defence is to switch the flow off for everyone who does not need it. Microsoft's own guidance is to get as close as possible to a unilateral block on device code flow, and a single Conditional Access policy does it.
What the device code flow is for
The flow exists for devices that have no browser or are too awkward to type on. RFC 8628 names smart TVs, media consoles, picture frames and printers. The device shows no sign-in page. It tells the user to visit a URL on another device, such as a phone, and enter a short code.
The client sends its client identifier to the authorization server and gets back a device_code, a user_code, a verification_uri and an expires_in value. While the user signs in on the second device, the client polls the token endpoint with the device_code until it receives an access token, an error, or an instruction to keep waiting.
In the Microsoft identity platform the endpoints are /devicecode and /token. The Microsoft documentation gives the user 15 minutes to sign in by default, and a refresh token is issued if the requested scope included offline_access.
Why it works as phishing
The party that requests the code is the client. The party that signs in is whoever holds the code. The protocol does not require these to be the same person, and RFC 8628 lists "Remote Phishing" as a security consideration (section 5.4).
An attacker can therefore act as the device. They request a code with a client_id (and a scope, which Microsoft's endpoint requires) and send the user_code to the target with a pretext. The target opens the real verification page, signs in and enters the code. The attacker's client, polling all along, receives the tokens.
The password never touches an attacker-controlled page. That is why checks built around domains and URLs, such as our link checker, have nothing to flag here.
The attacker still depends on the victim. The target must sign in and enter the code before expires_in passes (15 minutes by default), or the poll fails with expired_token. Personal-account users are also asked to sign in again and consent to the requested permissions.
There is one useful cue for training. Microsoft does not support verification_uri_complete, the optional field that carries the code inside the URL. "Someone sent me a code to enter on a Microsoft sign-in page" is the pattern to teach.
Detect device code sign-ins
Microsoft recommends a policy that audits existing use of the flow, and report-only mode gives you exactly that. Create the policy described below in Report-only and read the policy impact results to see who and what would have been blocked.
For ongoing hunting, filter the Entra sign-in logs for device code sign-ins and group them by user and application. This is practitioner guidance; the Microsoft pages cited here do not document it. A sign-in from a user who has no device or tool that uses the flow, or from an application nobody can name, deserves a conversation. Ask the user what message they received.
Block the flow with Conditional Access
The click-path is in Microsoft's Conditional Access guidance. Sign in as at least a Conditional Access Administrator, go to Entra ID > Conditional Access > Policies and create a new policy. Include all users and All resources. Under Conditions > Authentication Flows set Configure to Yes and select Device code flow. Under Grant select Block access, and set Enable policy to Report-only. Move it to On after you have reviewed the results.
The same page covers blocking authentication transfer, which lets users move a sign-in from a PC to a mobile device. It uses the same Authentication Flows condition, so decide on it at the same sitting.
Microsoft's advice is to allow the flow only in well documented and secured use cases, like legacy tooling that can't be updated.
For what has to stay, build a dedicated exclusion group with a name that says what it is for. Record an owner and a reason for every member, and audit the list regularly, as Microsoft advises. Keep your emergency access (break-glass) accounts excluded too, to avoid a lockout if the policy is misconfigured.
Separately, Microsoft notes that calls made by service principals aren't blocked by policies scoped to users, and suggests replacing service accounts used in scripts or code with managed identities.
If a code phish has already worked
Assume the attacker holds tokens, not just a password. A refresh token can be used to obtain new access tokens, so revoke the user's sessions first, then reset the password. This is practitioner guidance, not from the sources above.
Next, review the enterprise applications and consent grants tied to the account, and remove any you do not recognise. Check the mailbox for rules you did not create, especially forwarding or deletion rules.
Scope the hunt beyond one user by running the device code filter above across other accounts and looking for the same application. If more than one account may be affected, follow a proper response process; our incident response team can run it with you, and our blog carries incident response and phishing guidance for the training side.
Where to start
- List every device, script and tool that might sign in with a code: printers, TVs, IoT devices, command-line tools. Ask each owner whether it does.
- Create the Conditional Access policy with Device code flow selected, all users in scope and break-glass accounts excluded, in Report-only mode. Decide on the authentication transfer condition at the same time.
- Read the report-only results and the device code sign-ins in the logs. Fix or retire each legitimate use, and put what remains into a named exclusion group with an owner.
- Switch the policy to On, and put the exclusion group on the calendar for audit.
- Write down the response steps (revoke sessions, review enterprise apps and consent grants, check mailbox rules). Tell staff that a code they did not request themselves, from a device they are not holding, is a phishing attempt.
