Skip to content
Last updated

Device Code Flow

The Device Code Flow enables authentication for applications where secure credential storage is challenging, such as desktop software or offline client-server applications.

Use Cases

  • Desktop applications: Medical practice management software
  • Offline applications: Client-server systems with limited internet connectivity
  • Limited input devices: Applications where entering credentials is difficult

Flow Overview

┌─────────────────┐
│ Your Application│
└────────┬────────┘
         │ 1. Request device code
         ▼
    ┌──────────┐
    │  Askara  │
    └────┬─────┘
         │ 2. Return device code & user code
         ▼
┌─────────────────┐
│ Your Application│
└────────┬────────┘
         │ 3. Display user code & URL
         ▼
    ┌──────────┐
    │   User   │
    └────┬─────┘
         │ 4. Visit URL & enter code
         ▼
    ┌──────────┐
    │  Askara  │
    └────┬─────┘
         │ 5. Show login/consent page
         ▼
    ┌──────────┐
    │   User   │
    └────┬─────┘
         │ 6. Authorize application
         ▼
    ┌──────────┐
    │  Askara  │
    └────┬─────┘
         │
         │ ┌─────────────────────────┐
         │ │ 7. Polling loop         │
         │ │                         │
┌─────────────────┐                  │
│ Your Application│◄─────────────────┘
└────────┬────────┘ authorization_pending
         │
         │ (continues polling...)
         │
    ┌────▼─────┐
    │  Askara  │
    └────┬─────┘
         │ 8. Return access & refresh tokens
         ▼
┌─────────────────┐
│ Your Application│
└─────────────────┘

Unlike the Authorization Code Flow, the user authenticates on a separate device (phone, computer) by entering a short code.

Implementation

Step 1: Request Device Code

Your application requests a device code from Askara. Make sure to replace CLIENT_ID with your client identifier and to use the desired scopes.

POST https://api.askara.ai/oauth2/device-authorization
Content-Type: application/x-www-form-urlencoded

client_id=CLIENT_ID&scope=profile%20organization

The scopes are separated by a space, which must be encoded in a form-encoded body (%20 or +).

Response:

{
  "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
  "user_code": "1234-5678",
  "verification_uri": "https://app.askara.ai/device-verify",
  "verification_uri_complete": "https://app.askara.ai/device-verify?user_code=1234-5678",
  "expires_in": 600,
  "interval": 5
}

Response fields:

  • device_code: Used for polling (keep secure)
  • user_code: Short code displayed to user — 8 digits grouped 4-4 (e.g., "1234-5678")
  • verification_uri: URL where user enters the code
  • verification_uri_complete: Same URL with the user code already filled in, to use in a QR code or a direct link
  • expires_in: Code validity period in seconds (10 minutes)
  • interval: Minimum seconds between polling requests

Step 2: Display Instructions to User

Show the user how to authorize the application. Provide multiple options:

Option 1: Manual Code Entry

1. Visit: https://app.askara.ai/device-verify
2. Enter code: 1234-5678
3. Authorize the application

Option 2: QR Code Generate a QR code from verification_uri_complete for quick mobile access: the user does not have to type the code.

Option 3: Direct Link Provide a clickable link to verification_uri_complete.

Step 3: Poll for Authorization

Poll the token endpoint while waiting for user authorization:

POST https://api.askara.ai/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS&client_id=CLIENT_ID

Important: Respect the interval value to avoid rate limiting. Poll every 5 seconds (or as specified).

Step 4: Handle Response

Error responses have the standard OAuth2 shape: an error code and a human-readable error_description. Rely on the error value only, the description is meant for debugging and its wording may change.

Authorization Pending

HTTP 400:

{
  "error": "authorization_pending"
}

Action: Continue polling.

Slow Down

HTTP 400:

{
  "error": "slow_down"
}

Action: Increase polling interval by 5 seconds.

Success

{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "def502003f8a8c7..."
}

Action: Store tokens and stop polling.

Expired

HTTP 400:

{
  "error": "expired_token"
}

Action: Start over from Step 1.

Denied

HTTP 401, when the user refuses the authorization:

{
  "error": "access_denied"
}

Action: Stop polling. Start over from Step 1 only if the user asks to retry.

Code Example

// Step 1: Request device code
$response = $httpClient->post('https://api.askara.ai/oauth2/device-authorization', [
    'form_params' => [
        'client_id' => getenv('ASKARA_CLIENT_ID'),
        'scope' => 'profile organization'
    ]
]);

$deviceData = json_decode($response->getBody(), true);

// Step 2: Display to user
echo "Visit: {$deviceData['verification_uri']}\n";
echo "Enter code: {$deviceData['user_code']}\n";
echo "Or scan this QR code:\n";
displayQRCode($deviceData['verification_uri_complete']);

// Step 3: Poll for authorization
$interval = $deviceData['interval'];
$expiresAt = time() + $deviceData['expires_in'];

while (time() < $expiresAt) {
    sleep($interval);

    try {
        $tokenResponse = $httpClient->post('https://api.askara.ai/oauth2/token', [
            'form_params' => [
                'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code',
                'device_code' => $deviceData['device_code'],
                'client_id' => getenv('ASKARA_CLIENT_ID')
            ]
        ]);

        $tokens = json_decode($tokenResponse->getBody(), true);
        // Success! Store tokens
        storeTokens($tokens);
        echo "Authorization successful!\n";
        break;

    } catch (HttpException $e) {
        $error = json_decode($e->getResponse()->getBody(), true);

        if ($error['error'] === 'authorization_pending') {
            // Keep waiting
            continue;
        }

        if ($error['error'] === 'slow_down') {
            // Increase interval
            $interval += 5;
            continue;
        }

        // Other error (expired, denied, etc.)
        echo "Authorization failed: {$error['error_description']}\n";
        break;
    }
}

User Experience Best Practices

Clear Instructions

  • Display the verification URL prominently
  • The user code is 8 digits grouped 4-4; display it with the hyphen for readability (e.g., "1234-5678" not "12345678")
  • Show expiration time to create urgency

Multiple Entry Methods

  • Provide QR code for mobile users
  • Offer clickable link for desktop users
  • Include manual entry for maximum compatibility

Progress Indication

⏳ Waiting for authorization...
   Visit: https://app.askara.ai/device-verify
   Code: 1234-5678
   Expires in: 8:32

Timeout Handling

❌ Authorization expired
   The code has expired. Click here to generate a new code.

Security Considerations

Device Code Protection

  • Device codes are bearer tokens - keep them secure
  • Don't log or expose device codes in error messages
  • Codes expire after 10 minutes

Rate Limiting

  • Respect the interval parameter
  • Handle slow_down errors appropriately
  • Implement exponential backoff for repeated failures

Client Secret

For desktop applications, client secrets cannot be kept confidential. Consider:

  • Using public client configuration (no secret required)
  • Contact us to discuss your specific use case

Comparison with Authorization Code Flow

FeatureDevice CodeAuthorization Code
Redirect requiredNoYes
User enters codeYesNo
Client secretOptionalRequired for confidential clients (public clients use PKCE)
Best forDesktop appsWeb apps
User deviceAny deviceSame device

Next Steps