Device Authorization Flow
OAuth 2.0 Device Authorization Grant (RFC 8628) for input-constrained devices like CLI tools, smart displays, and IoT devices.
Device Flow Overview
- Device requests codes via
POST /oauth/device_token— receives adevice_codeanduser_code - Device displays the
user_codeandverification_urito the user - User visits the verification URI in a browser and enters the user code
- Server validates code via
POST /oauth/device_user_codeand shows the client details - User authorizes via
POST /oauth/authorize_device - Device polls via
POST /oauth/device_access_tokenuntil authorization is granted - Server issues tokens — access token (and optionally refresh token)
POST /api/oauth/device_token
Initiate a device authorization request. Returns codes for the user to enter in a browser.
Authentication: Required
Payload:
{
"client_id": "a6bd8f0f72b2c3275ff6",
"scope": "read:dataset write:dataset"
}
| Field | Type | Required | Description |
|---|---|---|---|
client_id | string | Yes | OAuth client ID |
scope | string | No | Space-separated list of requested scopes |
Response:
{
"device_code": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"user_code": "BKFT-DNLZ",
"interval": 5,
"expires_in": 1800,
"verification_uri": "https://informer.example.com/oauth/device",
"verification_uri_complete": "https://informer.example.com/oauth/device?code=BKFT-DNLZ"
}
| Field | Description |
|---|---|
device_code | 16-byte hex token for the device to use when polling |
user_code | Human-readable code (8 uppercase consonants in XXXX-XXXX format) |
interval | Minimum seconds between polling requests |
expires_in | Code lifetime in seconds (1800 = 30 minutes) |
verification_uri | URL the user should visit to enter the code |
verification_uri_complete | URL with the user code pre-filled |
POST /api/oauth/device_user_code
Validate a user code entered in the browser. Returns the associated client details so the user can confirm authorization.
Authentication: Required
Payload:
{
"user_code": "BKFT-DNLZ"
}
| Field | Type | Required | Description |
|---|---|---|---|
user_code | string | Yes | User code in XXXX-XXXX format |
Response:
{
"device_code": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"user_code": "BKFT-DNLZ",
"client_id": "a6bd8f0f72b2c3275ff6",
"scope": "read:dataset write:dataset",
"app": {
"id": "client-uuid-123",
"name": "Informer CLI",
"description": "Command-line interface for Informer",
"url": null,
"client_id": "a6bd8f0f72b2c3275ff6",
"pkce": true,
"enableRefreshTokens": true
}
}
The user code is deleted after validation. If the user enters the code again, it will return an error.
Error Responses:
404 Not Found- User code is invalid or expired
POST /api/oauth/authorize_device
Authorize or deny a device authorization request. Called by the user in their browser after reviewing the client details.
Authentication: Required
Payload:
{
"device_code": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"authorize": true
}
| Field | Type | Required | Description |
|---|---|---|---|
device_code | string | Yes | Device code from the initial request |
authorize | boolean | No | Whether to authorize the device |
Response:
{
"authorized": true
}
POST /api/oauth/device_access_token
Poll for the access token after the user has been prompted to authorize. The device should call this endpoint at the interval specified in the initial response.
Authentication: Required
Payload:
{
"device_code": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
}
| Field | Type | Required | Description |
|---|---|---|---|
device_code | string | Yes | Device code from the initial request |
Success Response (after user authorizes):
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"scope": "read:dataset write:dataset",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 900
}
Pending Response (user hasn't authorized yet):
{
"error": "authorization_pending"
}
Error Responses:
| Error | HTTP Status | Description |
|---|---|---|
authorization_pending | 400 | User hasn't authorized yet — keep polling |
access_denied | 400 | User explicitly denied authorization |
expired_token | 400 | Device code has expired — restart the flow |
- Respect the
intervalvalue (default: 5 seconds) between polls - Stop polling after receiving
expired_tokenoraccess_denied - On
authorization_pending, wait and retry