DEV Community

Cover image for OAuth 2.0 Device Flow: How Authentication Works When the Device Can't Handle the Login
Sina Rezaei
Sina Rezaei

Posted on AI-assisted

OAuth 2.0 Device Flow: How Authentication Works When the Device Can't Handle the Login

Authentication is straightforward when a user has a browser, keyboard, and screen.

A laptop can open a login page. A phone can redirect the user to an identity provider. But what happens when the device is a Smart TV, game console, CLI tool, or another device that is not well suited for entering credentials?

This is where the OAuth 2.0 Device Authorization Grant, commonly called Device Flow, becomes useful.

Instead of asking the device to handle the login itself, Device Flow lets the user authenticate on another device while the original device waits for the result.

The basic idea

The device starts the authentication process and receives a set of temporary values from the authorization server.

The user then opens a verification URL on a phone or computer, enters a short code, and completes authentication there.

The original device does not need to display or process the full login interface.

The flow looks roughly like this:

Device
   |
   |  Device Authorization Request
   v
Authorization Server
   |
   |  device_code + user_code
   v
Device
   |
   |----------------------> User's Browser / Phone
   |                         |
   |                         | Login + consent
   |                         v
   |                    Authorization Server
   |
   |  Poll Token Endpoint
   v
Authorization Server
   |
   |  Access Token
   v
Device
Enter fullscreen mode Exit fullscreen mode

The important part is that the device and the user's browser do not have to be the same device.

What the device receives

The device first sends a request to the authorization server's device authorization endpoint.

The response typically contains values such as:

{
  "device_code": "4fJ8...",
  "user_code": "WDJB-MJHT",
  "verification_uri": "https://example.com/device",
  "expires_in": 600,
  "interval": 5
}
Enter fullscreen mode Exit fullscreen mode

The important fields are:

  • device_code: identifies the authentication request and is used by the device when requesting tokens.
  • user_code: a short code the user enters on another device.
  • verification_uri: where the user goes to authenticate.
  • expires_in: how long the authorization request remains valid.
  • interval: how often the device should poll the token endpoint.

The device_code and user_code serve different purposes.

The device keeps the device_code. The user normally only sees the user_code.

The user finishes authentication somewhere else

The device can now display something like:

Open:
https://example.com/device

Enter code:
WDJB-MJHT
Enter fullscreen mode Exit fullscreen mode

The user opens the URL on a phone or computer, enters the code, signs in, and approves the requested access.

Nothing special has to happen on the original device while this is taking place.

It simply waits.

The device polls for the token

After receiving the device authorization response, the device starts polling the token endpoint.

For example:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=4fJ8...
&client_id=my-device
Enter fullscreen mode Exit fullscreen mode

If the user has not finished authentication yet, the authorization server can return:

{
  "error": "authorization_pending"
}
Enter fullscreen mode Exit fullscreen mode

The device waits and tries again.

It should not continuously hammer the server. The server can also return:

{
  "error": "slow_down"
}
Enter fullscreen mode Exit fullscreen mode

which tells the client to increase the polling interval.

Once the user has successfully authenticated and granted access, the response contains the token:

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600
}
Enter fullscreen mode Exit fullscreen mode

The device can then use that access token to call the protected API.

What can happen while the device waits?

The device needs to handle more than just success and authorization_pending.

Common responses include:

Response Meaning
authorization_pending The user has not finished yet
slow_down Poll less frequently
expired_token The device authorization request expired
access_denied The user rejected the request

This is important because Device Flow is inherently asynchronous.

The device cannot assume that authentication will finish immediately, or even finish successfully.

Why use a separate user code?

You might wonder why the user does not simply enter the long device_code.

The answer is usability.

A device_code is intended to be handled by the device and can be long or difficult to type. The user_code is designed for humans and is usually much shorter.

That separation also lets the authorization server associate the human-friendly code with the underlying authorization request without exposing the device's internal identifier as the thing the user has to type.

Security still matters

Device Flow solves an authentication-interface problem, but it does not remove the security requirements around authentication.

The verification endpoint should protect against things such as:

  • brute-force attempts against short user codes
  • excessive requests
  • reuse of expired or already-consumed codes
  • unauthorized access to the authorization request
  • overly long-lived device codes

User codes should have limited lifetimes and a sufficiently large search space. Once a code has been successfully consumed, it should not remain usable.

The server also needs to handle concurrent requests safely. Two requests attempting to consume the same authorization state should not both be able to obtain authorization.

Device Flow vs. PKCE

Device Flow and Authorization Code Flow with PKCE solve related but different problems.

PKCE is useful when the client can redirect the user through a browser but needs protection against authorization-code interception.

Device Flow is useful when the device itself is not a practical place to perform the browser-based login.

A CLI application is a good example:

CLI
 |
 | "Go to this URL and enter this code"
 v
Browser
 |
 | Login + consent
 v
Authorization Server
 |
 v
CLI receives token
Enter fullscreen mode Exit fullscreen mode

Trying to force a full browser login experience into a terminal would make the client unnecessarily complicated.

Device Flow moves that responsibility to a device that is actually good at it.

Where Device Flow makes sense

You will commonly see this pattern with:

  • Smart TVs
  • game consoles
  • command-line applications
  • media players
  • IoT devices
  • devices with limited input capabilities
  • applications where the authentication device and usage device are different

The common characteristic is not necessarily weak hardware.

It is that the device is a poor place to perform interactive authentication.

A practical implementation checklist

When implementing Device Flow, make sure the client:

  • stores the device_code securely
  • displays the verification URI and user code clearly
  • respects the polling interval
  • handles authorization_pending and slow_down
  • stops when the request expires
  • handles user denial
  • securely stores the resulting access token

The authorization server should, at minimum:

  • expire device authorization requests
  • rate-limit verification and token endpoints
  • prevent user-code guessing and abuse
  • ensure authorization state cannot be reused incorrectly
  • issue tokens only after the required user authorization has completed

The bigger engineering idea

Device Flow is a useful example of a broader engineering principle.

A component does not have to perform every part of a process itself.

A Smart TV is good at displaying content, but terrible at typing a password with a remote control. A CLI is excellent for commands, but not necessarily for interactive web authentication.

Instead of forcing the device to do something badly, Device Flow gives each part of the system a job it is better suited to perform:

Device       β†’ starts the request and waits
Browser      β†’ handles interactive authentication
Server       β†’ manages authorization and tokens
Device       β†’ uses the resulting access token
Enter fullscreen mode Exit fullscreen mode

That separation is what makes the flow practical.

OAuth 2.0 Device Flow is therefore less about adding another authentication screen and more about moving authentication to the place where it can be performed properly.

Top comments (0)