Device Code Flow
The Device Code Flow (officially: OAuth 2.0 Device Authorization Grant) is an OAuth 2.0 grant type designed for devices that don't have a browser or have limited input capabilities, such as smart TVs, gaming consoles, CLI tools, or IoT devices. It enables users to authorize these devices to access their resources by authenticating on a separate device (e.g., smartphone or computer) with a full browser.
This flow is ideal when the device cannot securely input credentials or display a full login page, but the user has access to another device with a browser.
How it Works

Before we step into how it works, let's understand some useful parameters.
| Parameter Name | Description |
|---|---|
client_id | Client which represents the business-App in cidaas |
device_code | Code used to poll the token endpoint. After authorization, this code becomes invalid |
user_code | Short code that the user enters on the verification page (typically 6-8 characters) |
verification_uri | URL where the user can enter the user_code and credentials. The user visits this URL on a separate device with a browser |
verification_uri_complete | Optional: Complete URL that includes the user_code as a query parameter: <verification_uri>?user_code=<user_code>. This allows direct access without manual code entry |
interval | Time period (in seconds) that the device should wait between polling requests to the token endpoint |
expires_in | Expiration time (in seconds) for the user_code. After this time, the user code expires and a new device authorization request is needed |
grant_type | Must be urn:ietf:params:oauth:grant-type:device_code when exchanging the device code for a token |
Step 1: The device application makes a POST request to the device authorization endpoint with client_id (and optionally scope) to initiate the device authorization flow.
Step 2: cidaas responds with device_code, user_code, verification_uri, verification_uri_complete (optional), interval, and expires_in.
Step 3: The device displays the user_code and verification_uri (or verification_uri_complete) on its screen. If possible, display a QR code containing the verification_uri_complete URL. The device_code is kept by the device application and used for polling (not shown to the user).
The device instructs the user to:
- Visit the verification URL on a separate device (smartphone, computer, etc.) with a browser
- Enter the
user_codewhen prompted - Complete the authentication on that device
Step 4: While the user is authenticating, the device application starts polling the token endpoint at the specified interval using the device_code. The polling continues until:
- The user successfully authenticates (polling returns tokens)
- The
user_codeexpires (polling returns an error) - A maximum number of polling attempts is reached
Step 5: The user visits the verification URL on their separate device (smartphone, computer, etc.) and enters the user_code. If a QR code was provided, the user can scan it to automatically open the verification page with the code pre-filled.
Step 6: cidaas presents the user with a login page where they enter their credentials. This can include password authentication or passwordless authentication methods such as Magic Link, Email/SMS OTP, or FIDO2.
Step 7: After successful authentication, the user's authorization is linked to the device_code. The device application's polling request (started in Step 4) now receives a successful response containing access_token, token_type (typically "Bearer"), expires_in, and scope. The device stops polling.
Step 8: The device application uses the access_token in the Authorization header to access protected resources on the resource server.
Step 9: The resource server validates the access_token using one of the following methods:
- A: Token introspection: Make an introspection call to cidaas to validate the token's authenticity and obtain information about the token (scopes, expiration, user, etc.).
- B: Offline validation: Validate the token's signature and check that the expiration time is in the future (for JWT tokens).
Step 10: If the token is valid, the resource server responds with the requested resource.
Security Note
Important: The Device Code Flow is designed for public clients (devices without secure storage). No
client_secretis required, making it suitable for devices that cannot securely store credentials. Thedevice_codeshould be kept secure by the device application and never exposed to users.
When to Use Device Code Flow
Use this flow when:
- Your device has limited or no input capabilities (no keyboard, limited display)
- Your device cannot securely display a full login page
- Users have access to a separate device with a browser (smartphone, computer)
- You're building for Smart TVs, gaming consoles, IoT devices, or CLI tools
- You cannot securely store a
client_secreton the deviceIf your device has full browser capabilities, consider using the PKCE Flow instead.
Video Tutorial
Let's look at a simple tutorial to get an idea of the Device Code Flow.
Technical Integration
| API | Description | Link to API |
|---|---|---|
| Step 1: Initiate Device Authorization | This call is used to initialize the device authorization request and obtain the device_code, user_code, and verification_uri | View API |
| Step 7: Exchange Device Code (Polling) | This API must be called repeatedly at the specified interval using the device_code until the user completes authentication or the code expires | View API |
| Step 9: Token Introspection | Validate and get information about an access token (optional) | View API |
Example Implementation
Here's a complete example of implementing the Device Code Flow:
Step 1: Initiate Device Authorization
POST https://{{base_url}}/authz-srv/device/authz
Content-Type: application/x-www-form-urlencoded
client_id={{client_id}}
&scope={{desired_scopes}}
Response:
{
"device_code": "abc123...",
"user_code": "ABC-DEF",
"verification_uri": "https://your-domain.cidaas.de/device",
"verification_uri_complete": "https://your-domain.cidaas.de/device?user_code=ABC-DEF",
"interval": 5,
"expires_in": 1800
}
Step 3-4: Display User Code and Start Polling
// Display user_code and verification_uri to the user
console.log(`Visit: ${response.verification_uri}`);
console.log(`Enter code: ${response.user_code}`);
// Start polling at the specified interval
const pollForToken = async () => {
while (true) {
await sleep(response.interval * 1000);
const tokenResponse = await fetch('https://{{base_url}}/token-srv/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: response.device_code,
client_id: '{{client_id}}'
})
});
const data = await tokenResponse.json();
if (tokenResponse.ok) {
// User has authenticated, we have tokens!
return data.access_token;
} else if (data.error === 'authorization_pending') {
// User hasn't authenticated yet, continue polling
continue;
} else {
// Error occurred (expired, denied, etc.)
throw new Error(data.error);
}
}
};
Step 7: Use Access Token
GET https://{{resource_server}}/api/protected-resource
Authorization: Bearer {{access_token}}
Note: The device should poll at the exact
intervalspecified in the response. If the user hasn't authenticated yet, the server will returnauthorization_pending. Once the user completes authentication, the polling request will return the access token.
Need Support?
Please contact us directly on our support page
