Device Authorization Flow
The Device Authorization Grant (RFC 8628) allows users to log in on devices that have limited or no browser capabilities. Instead of entering credentials directly on the device, the user is shown a short code and a URL. They visit the URL on a phone or laptop, enter the code, and approve the request. Meanwhile, the device polls the token endpoint until the user completes authorization.
Common use cases:
- Smart TV apps that display a code on screen for the user to approve on their phone
- CLI tools that open a browser for the user to authenticate
- IoT devices with no keyboard or screen that print a code to serial output
- Kiosk and point-of-sale terminals
How It Works
The device flow is a two-channel protocol. The device communicates with the token endpoint, while the user interacts with Auris in a browser on a separate device:
- The device sends a token request to
/api/oauth/device/codewith itsclient_idand requestedscope - Auris returns a
device_code(opaque, long), auser_code(short, human-readable, 8 characters), averification_uri, and a pollinginterval - The device displays the
user_codeandverification_urito the user - The user visits the verification URL in a browser, enters the code, and authenticates with Auris
- Meanwhile, the device polls
POST /api/auth/tokenwithgrant_type=urn:ietf:params:oauth:grant-type:device_codeat the specified interval - Once the user approves, the next poll returns an access token and refresh token
- If the user denies or the code expires, the poll returns an error
Console Setup
Enable Device Flow
In the Auris Console, go to Applications and select the application that will use the device flow. Under the Settings tab, toggle Enable Device Flow to on.
Configure Settings
Set the device code lifetime and polling interval:
| Setting | Default | Notes |
|---|---|---|
| Code Lifetime | 600 seconds (10 minutes) | Maximum time the user has to enter the code and approve |
| Polling Interval | 5 seconds | Minimum interval between token polling requests from the device |
| User Code Length | 8 characters | Alphanumeric, uppercase, easy to read and type |
Note the Client ID
Device flow uses a public client (no client secret). Copy the Client ID from the Credentials tab.
Implementation
JavaScript SDK
import { AurisClient } from '@auris/js'
const auris = new AurisClient({
domain: 'auth.yourdomain.com',
clientId: 'your-device-app-client-id',
})
// Step 1: Request a device code
const deviceAuth = await fetch('https://auth.yourdomain.com/api/oauth/device/code', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: 'your-device-app-client-id',
scope: 'openid profile email',
}),
}).then(r => r.json())
// Step 2: Display to the user
console.log(`Go to: ${deviceAuth.verification_uri}`)
console.log(`Enter code: ${deviceAuth.user_code}`)
// Step 3: Poll for the token
const token = await pollForToken(deviceAuth)
async function pollForToken(deviceAuth) {
const interval = deviceAuth.interval * 1000 // convert to ms
const expiresAt = Date.now() + deviceAuth.expires_in * 1000
while (Date.now() < expiresAt) {
await new Promise(resolve => setTimeout(resolve, interval))
const response = await fetch('https://auth.yourdomain.com/api/auth/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: deviceAuth.device_code,
client_id: 'your-device-app-client-id',
}),
})
if (response.ok) {
return await response.json()
}
const error = await response.json()
if (error.error === 'authorization_pending') {
continue // user hasn't approved yet
}
if (error.error === 'slow_down') {
await new Promise(resolve => setTimeout(resolve, 5000)) // back off
continue
}
if (error.error === 'expired_token') {
throw new Error('Device code expired. Please restart the flow.')
}
if (error.error === 'access_denied') {
throw new Error('User denied the authorization request.')
}
throw new Error(`Unexpected error: ${error.error}`)
}
throw new Error('Device code expired.')
}Device Code Response
When you request a device code, Auris returns the following fields:
| Field | Type | Description |
|---|---|---|
device_code | string | Opaque code used by the device to poll for the token. Keep secret. |
user_code | string | Short alphanumeric code (8 characters) displayed to the user. |
verification_uri | string | URL the user visits to enter the code. |
verification_uri_complete | string | URL with the code pre-filled as a query parameter. |
expires_in | number | Seconds until the device code expires (default: 600). |
interval | number | Minimum polling interval in seconds (default: 5). |
Error Handling
During the polling phase, the token endpoint returns specific error codes to indicate the current state:
| Error Code | HTTP Status | Meaning | Client Action |
|---|---|---|---|
authorization_pending | 400 | User has not yet approved the request | Continue polling at the specified interval |
slow_down | 400 | Polling too frequently | Increase polling interval by 5 seconds |
expired_token | 400 | Device code has expired | Restart the flow from step 1 |
access_denied | 400 | User explicitly denied the request | Show error message, do not retry |
User Verification Page
Auris provides a hosted verification page at /hosted/device where users enter their device code. The page:
- Prompts the user to enter the 8-character code
- Authenticates the user (login required if no active session)
- Shows the requesting application name and requested scopes
- Asks the user to approve or deny the request
- Displays a confirmation message on success
If the verification_uri_complete URL is used, the code field is pre-filled, saving the user a step.
Security Considerations
- Short code lifetime: Device codes expire after 600 seconds by default. This limits the window for code interception.
- Human-readable codes: The 8-character user codes use an unambiguous character set (no
0/O,1/I/lconfusion). - Rate-limited polling: The token endpoint enforces the polling interval. Clients that poll too fast receive
slow_downerrors. - One-time use: Each device code can only be approved once. After successful token issuance, the code is invalidated.
API Endpoints
/api/oauth/device/codeRequest a new device code. Requires client_id and optional scope in the request body. Returns device_code, user_code, verification_uri, expires_in, and interval.
/api/auth/tokenToken endpoint. For device flow, set grant_type=urn:ietf:params:oauth:grant-type:device_code, device_code, and client_id. Returns access token on success, or an error code during polling.
/api/oauth/device/verifyHosted verification page. Accepts optional user_code query parameter for pre-filling.
/api/oauth/device/codesRequires: manage:device_codesList active device codes for the tenant. Admin endpoint for monitoring and debugging.
Required Permissions
| Operation | Permission |
|---|---|
| Enable Device Flow on an application | manage:applications |
| List active device codes | manage:device_codes |
| Revoke a device code | manage:device_codes |
| Request/poll for token | No permission required (public endpoint) |
Related Guides
- Hosted Login (PKCE) — Browser-based interactive authentication
- M2M Client Credentials — Server-to-server authentication without a user
- CIBA (Backchannel Auth) — Decoupled authentication on a separate device