Skip to content

Device Code Flow ​

The device authorization grant is designed for devices that have limited input capability — CLI tools, smart TVs, IoT devices — where the user authorizes on a separate device (like their phone or laptop).

When to Use ​

  • CLI tools and terminal applications
  • Devices without a browser (IoT, set-top boxes)
  • Environments where redirect-based flows aren't feasible

Flow Overview ​

Step 1: Request Device Code ​

bash
curl -X POST /oauth/device/authorize \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "scope=openid projects:read"

Response:

json
{
  "device_code": "DEVICE_CODE",
  "user_code": "ABCD-1234",
  "verification_uri": "https://developer.knowify.com/device",
  "verification_uri_complete": "https://developer.knowify.com/device?user_code=ABCD-1234",
  "expires_in": 600,
  "interval": 5
}

Step 2: Display Instructions ​

Show the user:

To authorize this device, visit:
  https://developer.knowify.com/device

And enter code: ABCD-1234

Step 3: Poll for Token ​

While the user authorizes, poll the token endpoint at the specified interval:

bash
curl -X POST /oauth/token \
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  -d "device_code=DEVICE_CODE" \
  -d "client_id=YOUR_CLIENT_ID"

Possible responses while waiting:

ErrorMeaning
authorization_pendingUser hasn't authorized yet — keep polling
slow_downPolling too fast — increase interval by 5 seconds
expired_tokenThe device code expired — start over
access_deniedUser denied the request

Once approved, you receive the token response:

json
{
  "access_token": "eyJhbG...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "ey...",
  "scope": "openid projects:read"
}

Polling Example ​

javascript
async function pollForToken(deviceCode, clientId, interval = 5) {
  while (true) {
    await new Promise(r => setTimeout(r, interval * 1000));

    const res = await fetch('/oauth/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: deviceCode,
        client_id: clientId,
      }),
    });

    const data = await res.json();

    if (data.access_token) return data;
    if (data.error === 'slow_down') interval += 5;
    if (data.error === 'expired_token') throw new Error('Device code expired');
    if (data.error === 'access_denied') throw new Error('User denied access');
  }
}