Table of Contents

Device authorization

A device with no browser, or no keyboard worth typing on — a television, a command-line tool — gets a code pair. It shows the user a short code and where to enter it, and polls the token endpoint with the long one until the user, on a phone or a computer, has signed in and approved. The device is the demo host's device-client, a public client.

Follows: RFC 8628.

Recorded from the demo host (src/Fdyay.IdentityServer.Host) by the DeviceAuthorizationFlow test on every CI run. A value in braces, such as {access_token_1}, is one that differs on every run; the same placeholder is the same value wherever it appears. See how these pages are made.

sequenceDiagram
    participant Device
    participant AuthorizationServer as Authorization Server
    participant User
    Device->>AuthorizationServer: POST /connect/deviceauthorization
    AuthorizationServer-->>Device: 200 OK
    Device->>AuthorizationServer: POST /connect/token
    AuthorizationServer-->>Device: 400 Bad Request, authorization_pending
    Note over User,AuthorizationServer: On another device, the user opens verification_uri,<br/>signs in, enters the user code and allows the<br/>request (the host's /device page#59; not shown).
    Device->>AuthorizationServer: POST /connect/token
    AuthorizationServer-->>Device: 200 OK

1. The device asks for a code pair

device_code is the device's to keep; user_code is for the person, with verification_uri to type it into, or verification_uri_complete to show as a QR code. interval is how often the device may poll.

Device → Authorization Server

POST /connect/deviceauthorization HTTP/1.1
Content-Type: application/x-www-form-urlencoded

client_id=device-client
&scope=openid+profile+api

Authorization Server → Device

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "device_code": "{device_code_1}",
  "user_code": "{user_code_1}",
  "verification_uri": "http://localhost/device",
  "expires_in": 300,
  "interval": 5,
  "verification_uri_complete": "http://localhost/device?userCode={user_code_1}"
}

2. The device polls before the user has answered

It is told to keep waiting.

Device → Authorization Server

POST /connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
&device_code={device_code_1}
&client_id=device-client

Authorization Server → Device

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "error": "authorization_pending"
}

On another device, the user opens verification_uri, signs in, enters the user code and allows the request (the host's /device page; not shown).

3. The device polls again

The user has allowed it, so the tokens are released — once.

Device → Authorization Server

POST /connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
&device_code={device_code_1}
&client_id=device-client

Authorization Server → Device

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "access_token": "{access_token_1}",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile api",
  "id_token": "{id_token_1}"
}

{access_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "at+jwt"
}
{
  "aud": [
    "api-resource"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 3600},
  "jti": "{jti_1}",
  "client_id": "device-client",
  "scope": "api openid profile",
  "sub": "alice",
  "sid": "{session_id_1}"
}

{id_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "JWT"
}
{
  "aud": [
    "device-client"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 300},
  "jti": "{jti_2}",
  "azp": "device-client",
  "sub": "alice",
  "name": "Alice Smith",
  "sid": "{session_id_1}",
  "at_hash": "{at_hash_1}"
}