Device Code Flow
The Device Code flow enables authentication in environments without direct browser access.
Overview
This flow is designed for:
- Remote servers without a GUI
- Docker containers
- SSH sessions
- IoT devices
The user authenticates on a separate device (like a phone or laptop) while the application polls for completion.
sequenceDiagram
participant App as Application
participant IdP as Identity Provider
participant User
participant Browser as User's Browser
App->>IdP: Request device code
IdP->>App: Device code + User code + URL
App->>User: Display code and URL
User->>Browser: Navigate to URL
User->>Browser: Enter user code
User->>Browser: Authenticate
loop Polling
App->>IdP: Check authorization status
IdP->>App: Pending / Success / Error
end
IdP->>App: Access + Refresh tokens
Step 1: Request Device Code
Request a device code from the identity provider:
curl --request POST \
--url "https://{auth_base_url}/oauth/device/code" \
--header "content-type: application/x-www-form-urlencoded" \
--data "client_id={client_id}" \
--data "scope=openid email profile offline_access" \
--data "audience={audience}"
Response:
{
"device_code": "Ag_EE...ko1p",
"user_code": "QTZL-MCBW",
"verification_uri": "https://quera-identity.us.auth0.com/activate",
"verification_uri_complete": "https://quera-identity.us.auth0.com/activate?user_code=QTZL-MCBW",
"expires_in": 900,
"interval": 5
}
| Field | Description |
|---|---|
device_code |
Code used for polling (keep secret) |
user_code |
Code displayed to user |
verification_uri |
URL where user enters the code |
verification_uri_complete |
URL with code pre-filled |
expires_in |
Seconds until codes expire |
interval |
Minimum seconds between poll requests |
Step 2: Display Instructions to User
Show the user:
To authenticate, please:
1. Open: https://quera-identity.us.auth0.com/activate
2. Enter code: QTZL-MCBW
3. Follow the login prompts
Waiting for authentication...
Or provide the complete URL for easier access.
Step 3: Poll for Completion
Poll the token endpoint at the specified interval:
curl --request POST \
--url "https://{auth_base_url}/oauth/token" \
--header "content-type: application/x-www-form-urlencoded" \
--data grant_type=urn:ietf:params:oauth:grant-type:device_code \
--data "device_code={device_code}" \
--data "client_id={client_id}"
Polling Responses
Still waiting:
{
"error": "authorization_pending",
"error_description": "User has yet to authorize device code."
}
Continue polling.
Too fast:
{
"error": "slow_down",
"error_description": "You are polling faster than the specified interval."
}
Increase polling interval and continue.
Success:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86400,
"refresh_token": "v1.MSjO...",
"scope": "openid profile email offline_access"
}
Expired:
{
"error": "expired_token",
"error_description": "The device code has expired."
}
Start over from Step 1.
Denied:
{
"error": "access_denied",
"error_description": "User denied access."
}
User explicitly denied authorization.
Next Steps
- Token Refresh - Refresh tokens before they expire
- M2M Authentication - For fully automated systems