Skip to main content

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

device-grant-flow

Before we step into how it works, let's understand some useful parameters.

Parameter NameDescription
client_idClient which represents the business-App in cidaas
device_codeCode used to poll the token endpoint. After authorization, this code becomes invalid
user_codeShort code that the user enters on the verification page (typically 6-8 characters)
verification_uriURL where the user can enter the user_code and credentials. The user visits this URL on a separate device with a browser
verification_uri_completeOptional: 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
intervalTime period (in seconds) that the device should wait between polling requests to the token endpoint
expires_inExpiration time (in seconds) for the user_code. After this time, the user code expires and a new device authorization request is needed
grant_typeMust 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_code when 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_code expires (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_secret is required, making it suitable for devices that cannot securely store credentials. The device_code should 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_secret on the device

If 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.

device-code-flow

Technical Integration

APIDescriptionLink to API
Step 1: Initiate Device AuthorizationThis call is used to initialize the device authorization request and obtain the device_code, user_code, and verification_uriView 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 expiresView API
Step 9: Token IntrospectionValidate 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 interval specified in the response. If the user hasn't authenticated yet, the server will return authorization_pending. Once the user completes authentication, the polling request will return the access token.

Need Support?

Please contact us directly on our support page