cd /news/developer-tools/oauth-2-0-device-flow-explained-for-… · home topics developer-tools article
[ARTICLE · art-8920] src=stackoverflow.blog ↗ pub= topic=developer-tools verified=true sentiment=· neutral

OAuth 2.0 – Device flow explained for Engineers, especially for Backend Engineers

The article explains the OAuth 2.0 Device Authorization Grant (device flow), a protocol designed for devices like smart TVs, CLIs, and IoT devices where traditional password entry is impractical. It details a step-by-step process where a device requests a code, displays a user code and URL for the user to authenticate on a separate device, and then polls the server for a token while handling five specific response states (authorization pending, slow down, expired token, access denied, and success). The author emphasizes proper implementation of polling intervals and user code formatting to ensure security and usability.

read4 min views13 publishedMay 11, 2026

First time I tried to login to Netflix at a hotel TV I almost gave up. The remote was having only four arrow keys and a number pad. My password was 18 characters with symbols. Whoever designed the login screen had either never used it themselves, or they had decided suEering builds character. After few years, the same TV’s started doing something different. They showed us a short code and an URL. I opened phone, typed the URL, entered the code and we are in. No remote-control circus. No password on a TV. That is OAuth 2.0 device authorization grant. Most of the people just call it that device flow. If we run aws sso login, gh auth login, or signed into Spotify on an Xbox, we have already used it. And if you are build a backend for a CLI, an IOT device, a smart TV app or anything where typing a password is really painful or not safe, we should end up implementing it sooner or later. Here is the step by step Step1: Assume we are building a CLI called mycli. The user runs: $ mycli login Below will happen behind the scenes. Step 1: The CLI asks for a code The CLI sends a POST to authorization server: POST /oauth/device_authorization HTTP/1.1 Host: auth.example.com

Content-Type: application/x-www-form-urlencoded
client_id=mycli-prod&scope=read:repos%20write:repos

The server responds with something like this:

{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://example.com/device",
"verification_uri_complete": "https://example.com/device?user_code=WDJB-

MJHT",

"expires_in": 1800,
"interval": 5
}

Five fields actually matter: • device_code is a long, opaque string. The CLI keeps this private. • user_code is the short, human-friendly one. The CLI shows this on screen. • verification_uri is where the user goes to enter the code. • expires_in is how long the exchange is valid. Usually 15 to 30 minutes. • interval is how often the CLI is allowed to poll for the token. We will get to that. Step 2: the CLI tells the user what to do Your CLI prints something like: Open this URL in your browser: https://example.com/device Enter this code: WDJB-MJHT Waiting for confirmation... The user goes to their phone or laptop, opens the URL enter the code and signs in normally with SSO and approves the request. Step 3: CLI polls the token endpoint When we are using phone, the CLI is in the loop and every five seconds it try to reach the server and get the status POST /oauth/token HTTP/1.1 Host: auth.example.com

Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code

&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS &client_id=mycli-prod The server’s response is one of the five things, this is the part most of us will get wrong on the first implementation. Five response we should handle Authorization_pending – Users not approved yet slow_down – you are polling too fast. The RFC says you must increase your interval by at least 5 seconds expired_token – Exchange ran out of time, stop polling access_denied – User clicked no or closed the page. Stop polling success – the token is in the body, like a normal OAuth response:

{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "v1.MRn7...",
"scope": "read:repos write:repos"
}

That is the whole protocol. If we can handle those five cases, we will have a working device flow client. Few additional things to take care of

  1. Treating user_code like it is secret. It is not. The user types it into their phone. Your job is to make it short enough to read but unguessable. The standard is 8 to 9 alphanumeric characters with the confusing ones removed (no 0, O, I, 1). GitHub uses 8 characters in the format XXXX XXXX. That is a good template.
  2. No rate limiting on the verification page. The endpoint where users enter the user_code is a brute-force target. With a 9-character code from a reduced alphabet, you have plenty of theoretical entropy, but pending codes at any given moment number in the thousands. Lock it down. Per-IP, per-session, exponential backoff after failures.
  3. Ignoring slow_down . I have seen clients that sleep for the same interval forever, no matter what the server says. That works in dev. In production it gets you flagged as abusive.
  4. Not expiring the user_code immediately on use. Once the user submits the code, mark it consumed. Atomically. If your check-then-mark logic is not transactional, you have a real bug waiting to happen.
  5. Confusing this with PKCE. Device flow is for input-constrained devices. PKCE is for public clients (mobile apps, SPAs) that cannot keep a secret. They look similar on the surface (no client secret) but solve different problems. You sometimes need both. Device flow + PKCE is fine and increasingly common. Conclusion The device flow is one of those protocols that looks complicated in the RFC but actually fits on a napkin. Two endpoints, five response cases, Once you have built it once, you will wonder why anyone ever bothered with passwords on TVs. If you are shipping a CLI today and still asking users to paste personal access tokens into a prompt, do them a favor. Spend an afternoon implementing this. Your users will notice.
── more in #developer-tools 4 stories · sorted by recency
── more on @netflix 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/oauth-2-0-device-flo…] indexed:0 read:4min 2026-05-11 ·