Skip to content

Device code flow

Imagine signing in on a smart TV with the on-screen keyboard. Or on a CLI tool with no browser. RFC 8628 (the device authorization grant) solves these cases by splitting the sign-in across two devices: the input-poor device shows a short code, and the user types it into a browser on their phone or laptop.

This is the right flow whenever the user-facing device cannot host a usable browser.

Two devices, one sign-in. The user-facing device polls while the user finishes sign-in on their phone.

The poll is the unusual part. The device repeatedly asks "is the user done yet?" until the platform answers yes (with a token) or no (with an error).

Step 1 — the device requests a code:

POST https://<your-tenant-url>/oauth2/device_authorization
Content-Type: application/x-www-form-urlencoded
client_id=<your-client-id>
&scope=openid+profile+email+offline_access

Response:

{
"device_code": "abc123long-opaque-string",
"user_code": "WDJB-MJHT",
"verification_uri": "https://banking-cymmetri.intelliauth.local/activate",
"verification_uri_complete": "https://banking-cymmetri.intelliauth.local/activate?user_code=WDJB-MJHT",
"expires_in": 1800,
"interval": 5
}

Show the user user_code and verification_uri. If the device can show a QR code, encode verification_uri_complete — the user scans the QR and lands on the activation page with the code pre-filled.

Step 2 — the device polls:

POST https://<your-tenant-url>/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=abc123long-opaque-string
&client_id=<your-client-id>

Possible responses:

  • authorization_pending — keep polling.
  • slow_down — you polled too fast; increase the interval by 5 seconds before next poll.
  • expired_token — the device_code expired; restart from step 1.
  • access_denied — the user denied the request.
  • A normal token response — the user signed in; you're done.

Respect the interval field. Polling faster than that gets you throttled.

  • Smart TVs and streaming devices — the canonical case.
  • CLIsgh auth login, aws sso login, and similar tools use device code. The CLI prints the URL + code; the user finishes sign-in in a browser; the CLI receives the token.
  • IoT devices with a screen but no keyboard — same shape.
  • Devices in shared environments — kiosks, gym treadmills, anything where typing a password on the public device feels wrong.

If the device can host a browser (a phone, a tablet, a desktop), prefer authorization code with PKCE instead — fewer round trips, no polling overhead.

The user_code is intentionally short and humans-friendly. The format is "two alphabetic groups separated by a dash" — short enough to type quickly, distinct enough to be unambiguous when read aloud. Show it in a large font, in mixed case. Show the URL too, for users whose phones cannot scan QR codes.

The activation page on the IntelliAuth side accepts the code, asks the user to sign in (if not already), then shows a consent screen describing which device is requesting access. This is the user's chance to back out — important for shared TVs.

Same rules as native apps: use the platform's secure storage if it has one. If the device has no secure storage (a raw embedded system), assume the token can be extracted and design accordingly — keep scopes narrow, rotate refresh tokens aggressively, support revocation.