Detecting OAuth Device Code Phishing Before It Becomes a Breach

OAuth device code phishing bypasses MFA without touching a password. Here's how the attack works and how to detect it in your identity logs.

The device code flow was built for TVs and CLI tools that can’t open a browser. Attackers figured out it also works great for stealing a session without ever seeing a password.

Why this matters now

Over the past two years, phishing crews have shifted away from fake login pages. Login pages get flagged by browser isolation, password managers refuse to autofill on the wrong domain, and MFA prompts stop most credential replays cold. Device code phishing sidesteps all of it.

The trick uses a legitimate OAuth flow — the device authorization grant (RFC 8628) — the same mechanism Microsoft 365, Google Workspace, GitHub CLI, and countless SaaS tools use to let a smart TV or headless server ask a user to authenticate on a second screen. The victim isn’t asked to type a password anywhere suspicious. They’re asked to visit a real, legitimate login page and enter a short code. Because the domain is genuine and MFA completes normally, the attack looks like a routine login from the identity provider’s point of view.

The short version

PointWhy it matters
No fake login pageStandard phishing filters and browser isolation don’t trigger
MFA completes normallyThe victim approves a real prompt, so MFA bypass detections miss it
Token, not password, is stolenThe attacker walks away with a valid access and refresh token, often long-lived
Detection lives in identity logs, not emailYour best signal is the token grant event, not the phishing email itself

How the attack actually runs

  1. The attacker starts a device authorization request against the target’s real identity provider (Microsoft Entra ID, Okta, Google, etc.) and gets back a device_code and a short user_code.
  2. The attacker sends the victim a message — Teams, email, SMS — telling them to go to a real, well-known verification URL and enter the code. Pretext examples seen in the wild: “join this Teams meeting,” “verify your device for the new VPN policy,” “authenticate to view this shared document.”
  3. The victim, on the legitimate identity provider domain, enters the code and completes MFA as usual.
  4. The identity provider issues an access token and refresh token to the attacker’s polling client, not the victim’s browser.
  5. The attacker now holds a valid session — often with a refresh token lifetime measured in weeks — usable against mail, file storage, and any downstream app trusting that identity.

Nothing here is a vulnerability in the OAuth spec. It’s a phishing pretext wrapped around a legitimate flow, which is exactly why it slips past controls built to catch fake domains and credential harvesting pages.

What to look for in your logs

The device code flow leaves a distinct fingerprint in identity provider sign-in logs, even though the sign-in itself looks “successful.” The fields to pull out:

event_type:        sign-in / token-issued
grant_type:        device_code
client_app:        the polling application that requested the code
client_id:         often a well-known first-party client ID (e.g. a CLI or Teams client)
user_agent:        frequently absent or generic (headless polling client)
source_ip / geo:   the *requesting* client's IP, often unrelated to the victim's usual location
session_lifetime:  long-lived refresh token issued

The single highest-signal check: correlate the IP that requested the device code against the IP the user authenticated from. In a legitimate device flow (a smart TV, a CLI tool on a known corporate laptop), those IPs are close together on the network or at least consistent with the user’s normal pattern. In a phishing case, the requesting IP is the attacker’s infrastructure and the authenticating IP is the victim’s — two unrelated locations completing one login.

Pseudo-code for a detection rule

rule: oauth_device_code_anomalous_grant
when:
  event.grant_type == "device_code"
  and event.result == "success"
correlate:
  requesting_ip = event.device_code_request.source_ip
  auth_ip       = event.mfa_completion.source_ip
flag when:
  geo_distance(requesting_ip, auth_ip) > threshold_km
  or requesting_ip in known_bad_asn_list
  or client_app not in allowlisted_device_flow_clients
severity: high
mitre:
  - T1528  # Steal Application Access Token
  - T1566  # Phishing

Run this as a near-real-time rule against your identity provider stream where possible — the useful response window is the token’s lifetime, not the next day’s log review. If your identity provider doesn’t expose device-code-specific fields directly, you can often approximate grant_type from the sign-in log’s authentication method field, which is worth confirming against a real test login before you trust the rule.

Hardening beyond detection

Detection buys you time, but the durable fix is reducing how often this flow is even reachable:

ControlEffect
Disable device code flow for the tenant unless a specific app needs itRemoves the attack surface entirely for most orgs
Allowlist device flow to specific known client IDsBlocks unknown polling clients from ever completing the grant
Shorten refresh token lifetime for device-flow sessionsLimits the blast radius if a grant does succeed
Conditional access requiring compliant/managed device for token useStops the stolen token from being usable on attacker hardware

Most identity providers ship at least the first two controls today. If your organization has no legitimate use case for device code sign-in — no smart TVs, no CLI tools relying on it — turning it off is the single highest-leverage change you can make this week.

Final thought

Device code phishing is a good reminder that “MFA completed successfully” and “the legitimate identity provider domain was used” are necessary conditions for a safe login, not sufficient ones. The grant type and the correlation between requesting and authenticating IPs matter just as much. If you’re not already alerting on device code grants, it’s a cheap rule to add and one of the higher-signal detections you can put in front of an identity-based attack.

If you want help reviewing whether your identity provider logs give you enough visibility to catch this, contact us.