โ† All flows

Device Authorization Flow

For TVs, consoles and other devices without a keyboard

The Device Authorization flow (also called the device code flow) lets a device with no browser or easy typing ask for access. It works in three moves:

  1. The device shows a short user_code and a web address.
  2. The user opens that address on their phone or computer and approves the request.
  3. Meanwhile the device polls (checks in repeatedly) until approval arrives.

See it step by step

๐Ÿง‘
You
๐Ÿ“บ
Device
๐Ÿ”
Login server
๐Ÿ“ฆ
API
Request codes

Step 1 of 6

The device asks for a code

The device tells the login server it wants to sign someone in. It gets back a long secret device_code for itself and a short user_code for you.

When to use this flow

  • Smart TVs and streaming boxes โ€” Typing passwords with a remote is painful
  • Games consoles and IoT devices โ€” No browser, or a very limited one
  • Command-line tools โ€” Sign in without a redirect URL
  • Web and mobile apps โ€” Use Authorization Code with PKCE instead

Good practices

  • Poll at the given interval โ€” Wait at least interval seconds, and slow down if told to
  • Show the code and address clearly โ€” Make it easy to type and to recognise
  • Stop when the code expires โ€” Start over with a new device_code
  • Handle denial โ€” If the user says no, the server returns access_denied

Common mistakes

  • Polling too fast โ€” You will be told to slow_down or be blocked
  • Showing the device_code to the user โ€” Only the user_code is meant to be seen
  • Using it when a browser is available โ€” Authorization Code with PKCE is simpler there
  • Skipping expiry handling โ€” Codes usually expire after about 15 minutes
Code example
Device Authorization example
javascript
// Device Authorization flow, device side
const start = await fetch('https://auth.provider.com/oauth/device/code', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({ client_id: CLIENT_ID, scope: 'profile' }),
}).then((r) => r.json());

console.log(`Go to ${start.verification_uri} and enter ${start.user_code}`);

let interval = start.interval ?? 5;
while (true) {
  await new Promise((r) => setTimeout(r, interval * 1000));
  const res = await fetch('https://auth.provider.com/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
      device_code: start.device_code,
      client_id: CLIENT_ID,
    }),
  });
  const body = await res.json();
  if (body.error === 'authorization_pending') continue;
  if (body.error === 'slow_down') { interval += 5; continue; }
  if (body.error) throw new Error(body.error); // access_denied or expired_token
  return body; // { access_token, ... }
}