What Is the Device Authorization Flow?
The OAuth 2.0 Device Authorization Grant (RFC 8628) enables input-constrained devices — smart TVs, game consoles, CLI tools, IoT devices — to obtain tokens without requiring the user to type credentials on the device itself. The device displays a short human-readable code and a URL; the user navigates to that URL on a secondary device (phone or browser), authenticates, and enters the code. The device polls the authorization server until the user approves or the code expires.
Requirements
Functional Requirements
- Issue a device_code (opaque, machine-facing) and a user_code (short, human-typable) on flow initiation.
- Present the user with a verification URL and user_code on the constrained device.
- Allow the user to authorize on a secondary device by visiting the URL and authenticating.
- The device polls the token endpoint; receive a token when approved, appropriate error codes when pending or expired.
- Bind the issued tokens to the device_code to prevent token use on a different device.
- Expire device codes after a configurable TTL (default 15 minutes) and enforce polling interval backoff.
Non-Functional Requirements
- User code must be collision-resistant within the TTL window and easy to type (uppercase alpha, no ambiguous characters).
- Polling must not create undue load; enforce minimum interval and exponential backoff on violations.
- The authorization state must be durable across restarts during the polling window.
Data Model
Device Authorization Record
- device_code — cryptographically random 32-byte value, hex-encoded; never shown to the user.
- user_code — 8 uppercase characters (BCDFGHJKLMNPQRSTVWXZ, excluding vowels and visually ambiguous chars); mapped to device_code.
- client_id, scope, audience.
- status — ENUM:
PENDING,APPROVED,DENIED,EXPIRED. - user_id — populated when a user approves; null until then.
- device_binding_hash — hash of device fingerprint (user agent, IP, client_id); used at token pickup to verify same device is polling.
- issued_at, expires_at, last_polled_at, interval_seconds.
Core Algorithm
Step 1 — Flow Initiation (Device Request)
The device sends a POST to /device/code with client_id and scope. The server generates a cryptographically random device_code and a user_code. The user_code is generated by selecting 8 characters from the allowed set using a CSPRNG, then checking for collisions against PENDING codes in the store (retry on collision, expected to be rare). Store the authorization record with status PENDING and a TTL. Return device_code, user_code, verification_uri, verification_uri_complete (URI with user_code pre-filled), expires_in, and interval to the device.
Step 2 — User Authorization (Secondary Device)
The user navigates to the verification URL, authenticates, and enters the user_code. The server looks up the record by user_code, verifies it is PENDING and not expired, displays the requested scope, and prompts the user to approve or deny. On approval: atomically set status to APPROVED and populate user_id. On denial: set status to DENIED.
Step 3 — Device Polling
The device polls POST /token with grant_type=urn:ietf:params:oauth:grant-type:device_code and device_code. The server checks:
- Status PENDING: return error
authorization_pending; update last_polled_at. - Status APPROVED: issue access token and refresh token (bound to device_binding_hash); mark record consumed.
- Status DENIED: return error
access_denied. - Status EXPIRED or not found: return error
expired_token. - Polling faster than interval: return error
slow_downand increment interval by 5 seconds.
Step 4 — Device-Bound Token Issuance
At token issuance, include the device_binding_hash as a private claim in the access token and as a field in the refresh token record. Resource servers that require device binding verify this claim against the current request fingerprint. For less sensitive resources, the binding hash is informational only.
API Design
- POST /device/code — initiate flow; returns device_code, user_code, verification_uri, expires_in, interval.
- GET /device — renders the user-facing authorization page (enter user_code + authenticate).
- POST /device/authorize — submit user approval or denial for a user_code.
- POST /token with
grant_type=urn:ietf:params:oauth:grant-type:device_code— device polling endpoint.
Scalability Considerations
Store authorization records in Redis with native TTL; the polling pattern (many reads per record) benefits from Redis throughput. Use a secondary index (sorted set) on user_code to support O(1) lookup during user authorization without scanning. Rate-limit polling per device_code using a token bucket in Redis. For high-volume IoT deployments, shard the device authorization store by a hash of client_id. Long-poll variants reduce polling frequency: the device holds a connection open for up to the interval duration; the server responds immediately when status changes. This trades connection overhead for reduced polling load.
Summary
The device authorization flow solves the input-constrained device problem through short human-typable codes, secondary-device authentication, and polling-based token pickup. Collision-resistant user code generation, strict polling interval enforcement, and device binding at token issuance make the flow both usable and secure at scale.
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering
See also: Atlassian Interview Guide