Skip to main content

Device Code Flow

The Device Code flow (also known as Device Authorization Grant) is designed for devices that lack a browser or have limited input capabilities, such as smart TVs, IoT devices, or CLI tools. It allows users to authorize devices using a secondary device with a browser.

Overview

This flow is ideal for browserless or input-constrained devices. The device displays a user code and a verification URL, which the user enters on a separate device (like a smartphone or computer) to complete the authorization. The original device polls the authorization server until the user completes the authorization.

Flow Steps

  1. Device Authorization Request: The device requests a device code and user code from the authorization server
  2. Display User Code: The device displays the user code and verification URL to the user
  3. User Authorization: The user navigates to the verification URL on another device and enters the user code
  4. User Authentication: The user authenticates and authorizes the application
  5. Polling: The device polls the authorization server to check if authorization is complete
  6. Token Exchange: Once authorized, the device receives an access token
  7. Access Resources: Use the access token to access protected resources

Implementation

Prerequisites

Before starting, you’ll need:
  • Your application’s client_id and client_secret
  • The device authorization endpoint: https://login.camall.io/oauth/device/code
  • The token endpoint: https://login.camall.io/oauth/token
  • The verification URL: https://login.camall.io/activate

Step 1: Initiate Device Authorization Request

Request a device code and user code from the authorization server:
CLIENT_ID="your_client_id"
SCOPE="openapi offline_access"

curl -X POST https://login.camall.io/oauth/device/code \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=${CLIENT_ID}" \
  -d "scope=${SCOPE}"
Parameters:
  • client_id: Your application’s client ID
  • scope: Requested permissions (space-separated)
Response:
{
  "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
  "user_code": "WDJB-MJHT",
  "verification_uri": "https://login.camall.io/activate",
  "verification_uri_complete": "https://login.camall.io/activate?user_code=WDJB-MJHT",
  "expires_in": 900,
  "interval": 5
}
Response Fields:
  • device_code: The device verification code (keep this secret)
  • user_code: The code the user must enter (display this to the user)
  • verification_uri: The URL where the user should go to authorize
  • verification_uri_complete: Optional URL with the user code pre-filled
  • expires_in: How long the codes are valid (in seconds)
  • interval: Minimum time between polling requests (in seconds)

Step 2: Display User Code to User

Show the user code and instructions to the user:
# Parse the response
DEVICE_CODE=$(echo "$RESPONSE" | jq -r '.device_code')
USER_CODE=$(echo "$RESPONSE" | jq -r '.user_code')
VERIFICATION_URI=$(echo "$RESPONSE" | jq -r '.verification_uri')
VERIFICATION_URI_COMPLETE=$(echo "$RESPONSE" | jq -r '.verification_uri_complete')
EXPIRES_IN=$(echo "$RESPONSE" | jq -r '.expires_in')
INTERVAL=$(echo "$RESPONSE" | jq -r '.interval')

echo "========================================"
echo "Device Authorization"
echo "========================================"
echo ""
echo "Please visit: $VERIFICATION_URI"
echo "And enter code: $USER_CODE"
echo ""
echo "Or scan QR code / visit:"
echo "$VERIFICATION_URI_COMPLETE"
echo ""
echo "Code expires in: $EXPIRES_IN seconds"
echo "========================================"
User Experience Options:
  1. Display the verification_uri and user_code separately
  2. Display the verification_uri_complete as a clickable link or QR code
  3. Show a QR code that encodes the verification_uri_complete

Step 3: Poll for Authorization

While the user is authorizing on another device, poll the token endpoint:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  -d "device_code=${DEVICE_CODE}" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}"
Parameters:
  • grant_type=urn:ietf:params:oauth:grant-type:device_code: Specifies device code flow
  • device_code: The device code received in step 1
  • client_id: Your application’s client ID
  • client_secret: Your application’s client secret
Polling Responses: 1. Authorization Pending (User hasn’t completed authorization yet):
{
  "error": "authorization_pending",
  "error_description": "The authorization request is still pending"
}
Action: Wait for the interval seconds and poll again 2. Slow Down (Polling too quickly):
{
  "error": "slow_down",
  "error_description": "You are polling too quickly"
}
Action: Increase the polling interval by 5 seconds 3. Access Denied (User denied the authorization):
{
  "error": "access_denied",
  "error_description": "The user denied the authorization request"
}
Action: Stop polling and inform the user 4. Expired Token (Device code expired):
{
  "error": "expired_token",
  "error_description": "The device code has expired"
}
Action: Stop polling and restart the flow from step 1 5. Success (Authorization complete):
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_token_here",
  "scope": "openapi offline_access"
}
Action: Stop polling and use the access token

Step 4: Exchange Complete - Use Access Token

Once you receive the access token, make authenticated API requests:
curl -X GET https://api.camall.io/v1/resource \
  -H "Authorization: Bearer ACCESS_TOKEN_HERE" \
  -H "Content-Type: application/json"

Step 5: Refresh the Access Token (Optional)

When the access token expires, use the refresh token to obtain a new one:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=REFRESH_TOKEN_HERE" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}"
Response:
{
  "access_token": "new_access_token_here",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "new_refresh_token_here",
  "scope": "openapi offline_access"
}

Complete Example Script

Here’s a complete bash script demonstrating the entire flow:
#!/bin/bash

# Configuration
CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"
SCOPE="openapi offline_access"

echo "========================================"
echo "Device Code Flow - Starting"
echo "========================================"
echo ""

# Step 1: Request device code
echo "Step 1: Requesting device and user codes..."
RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/device/code \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=${CLIENT_ID}" \
  -d "scope=${SCOPE}")

# Parse response
DEVICE_CODE=$(echo "$RESPONSE" | jq -r '.device_code')
USER_CODE=$(echo "$RESPONSE" | jq -r '.user_code')
VERIFICATION_URI=$(echo "$RESPONSE" | jq -r '.verification_uri')
VERIFICATION_URI_COMPLETE=$(echo "$RESPONSE" | jq -r '.verification_uri_complete')
EXPIRES_IN=$(echo "$RESPONSE" | jq -r '.expires_in')
INTERVAL=$(echo "$RESPONSE" | jq -r '.interval')

echo ""
echo "========================================"
echo "Step 2: USER ACTION REQUIRED"
echo "========================================"
echo ""
echo "Please visit: $VERIFICATION_URI"
echo "And enter code: $USER_CODE"
echo ""
echo "Or visit: $VERIFICATION_URI_COMPLETE"
echo ""
echo "Code expires in: $EXPIRES_IN seconds"
echo "========================================"
echo ""

# Step 3: Poll for authorization
echo "Step 3: Polling for authorization..."
echo ""

MAX_ATTEMPTS=$((EXPIRES_IN / INTERVAL))
ATTEMPT=0
POLL_INTERVAL=$INTERVAL

while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
  ATTEMPT=$((ATTEMPT + 1))
  
  # Wait before polling
  sleep $POLL_INTERVAL
  
  # Poll the token endpoint
  TOKEN_RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
    -d "device_code=${DEVICE_CODE}" \
    -d "client_id=${CLIENT_ID}" \
    -d "client_secret=${CLIENT_SECRET}")
  
  # Check for errors
  ERROR=$(echo "$TOKEN_RESPONSE" | jq -r '.error // empty')
  
  if [ -z "$ERROR" ]; then
    # Success! We have the token
    echo "✓ Authorization successful!"
    echo ""
    echo "Token Response:"
    echo "$TOKEN_RESPONSE" | jq '.'
    
    # Extract access token
    ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
    REFRESH_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token')
    
    echo ""
    echo "Step 4: Access token obtained"
    echo "Access Token: $ACCESS_TOKEN"
    echo ""
    echo "You can now use this token to make API requests:"
    echo "curl -H \"Authorization: Bearer $ACCESS_TOKEN\" https://api.camall.io/v1/resource"
    
    exit 0
  elif [ "$ERROR" = "authorization_pending" ]; then
    # Still waiting for user authorization
    echo "[$ATTEMPT/$MAX_ATTEMPTS] Waiting for user authorization..."
  elif [ "$ERROR" = "slow_down" ]; then
    # Polling too fast, increase interval
    POLL_INTERVAL=$((POLL_INTERVAL + 5))
    echo "Polling too fast. Increasing interval to $POLL_INTERVAL seconds..."
  elif [ "$ERROR" = "access_denied" ]; then
    echo "✗ User denied the authorization request"
    exit 1
  elif [ "$ERROR" = "expired_token" ]; then
    echo "✗ Device code has expired"
    exit 1
  else
    echo "✗ Unknown error: $ERROR"
    echo "$TOKEN_RESPONSE" | jq '.'
    exit 1
  fi
done

echo "✗ Authorization timed out"
exit 1

Security Best Practices

  1. Protect device_code: The device code should be kept secret and not displayed to users
  2. Display user_code clearly: Make the user code easy to read and enter
  3. Respect polling interval: Always wait at least interval seconds between polls
  4. Handle slow_down: Increase polling interval when receiving slow_down errors
  5. Set appropriate expiration: Device codes should have limited lifetime (typically 10-15 minutes)
  6. Use HTTPS: All endpoints must use HTTPS in production
  7. Secure client secret: Protect your client secret on the device (consider using public clients for highly insecure devices)
  8. Implement timeout: Stop polling after the expires_in period
  9. User feedback: Provide clear feedback about authorization status

HTTP Request Examples

Raw HTTP Request - Device Authorization

POST /oauth/device/code HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded

client_id=YOUR_CLIENT_ID&scope=openapi%20offline_access

Raw HTTP Request - Token Polling

POST /oauth/token HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=DEVICE_CODE&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET

Raw HTTP Request - API Call with Bearer Token

GET /v1/resource HTTP/1.1
Host: api.camall.io
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Use Cases

The Device Code flow is ideal for:
  • Smart TVs and streaming devices: Apps on TVs where typing is difficult
  • CLI tools and scripts: Command-line applications that need user authorization
  • IoT devices: Devices without screens or keyboards
  • Gaming consoles: Applications on gaming platforms
  • Printer/scanner applications: Devices with limited input capabilities
  • CI/CD pipelines: Automated systems requiring user authorization

Comparison with Other Flows

FeatureDevice CodeAuthorization CodeClient Credentials
User interactionOn separate deviceDirect browser redirectNone (machine-to-machine)
Requires browser on deviceNoYesNo
User contextYesYesNo
Best forInput-constrained devicesWeb/mobile appsServer-to-server
SecurityHigh (with polling)Very high (with PKCE)High (client credentials)