Enabling “device flow”

Enabling “device flow”

Preface

“Device flow,” defined in RFC 8628 (OAuth 2.0 Device Authorization Grant) , is an authorization flow to issue access tokens for API clients running on devices with no Web browsers and/or that have limited capability for inputting text, with end user consent.

This article describes component architecture of an authorization server using Authlete to support the device flow and instructions to configure Authlete.

This feature is available in Authlete version 2.1 and later.

How an authorization server supports the device flow using Authlete API

An authorization server must have the following endpoints and “verification URI” to support the device flow. Authlete provides functions as APIs for implementing them.

enabling-device-flow_1

Configuration settings

This section explains settings for enabling the device flow. You have to configure both Authlete service and its client that uses the flow.

Authlete service

Configure the following settings in Service Owner Console.

Tab Item Value
Authorization Supported Grant Types Enable “DEVICE_CODE
Device Flow Device Authorization Endpoint The URL of the device authorization endpoint
e.g. https://as.example.com/device_authorization
Device Flow Verification URI The value of verification_uri to be presented to end users
e.g. https://as.example.com/device
Device Flow Verification URI with Placeholder The value of verification_uri_complete to be presented to end users (typically using QR code etc.)
e.g. https://as.example.com/device?user_code=USER_CODE
Device Flow Verification Code Duration The duration of device_code and user_code in seconds
e.g. 600
Device Flow Polling Interval The minimum interval between polling requests to the token endpoint in seconds
e.g. 5
Device Flow User Code Character Set The character set for user_code
e.g. BASE20
Device Flow User Code Character Set The length of user_code
e.g. 8
enabling-device-flow_2
enabling-device-flow_3

Client settings

Configure the following settings in Client Developer Console.

Tab Key Value
Basic Client Type PUBLIC
Authorization Grant Types Enable “DEVICE_CODE
Authorization [Token  Endpoint]Client Authentication Method Choose “NONE

In this article, the device is assumed to be a non-confidential client . Choose appropriate values for Client Type and Client Authentication Method if devices in your environment are confidential clients.

enabling-device-flow_4
enabling-device-flow_5
enabling-device-flow_6

Example

This example shows how an authorization server using Authlete responds to a device authorization request from a client, verifies a user_code from an end user, and responds token requests from the client.

device-flow-2

Device authorization request

Assume the client makes the following “device authorization request” to the authorization server  (step #2). (all examples below are folded for readability)

POST /device_authorization HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
...
client_id=...&scope=openid+profile+read

The authorization server forwards the request content to Authlete’s /device/authorization API (step #3, #4).

  • Request (step #3 using curl command)
curl -s -X POST $apiUrl/device/authorization \
   -u $apiKey:$apiSecret \
   -H 'Content-type: application/json' \
   -d '{"parameters": "client\_id=...&scope=openid+profile+read
"}'
  • Response (step #4)
{
  "type": "deviceAuthorizationResponse",
  "resultCode": "A220001",
  "resultMessage":
    "[A220001] The device authorization request was
     processed successfully.",
  "action": "OK",
  "deviceCode":
    "-jxwQ_7MEdR3SqS86bEg1ONUYdwGmSYjqH8eIBZ1c3U",
  "responseContent":
    "{\"user_code\":\"TXBBPHDZ\",
      \"device_code\":
        \"-jxwQ_7MEdR3SqS86bEg1ONUYdwGmSYjqH8eIBZ1c3U\",
      \"interval\":5,
      \"verification_uri_complete\":
        \"https://as.example.com/device?user_code=TXBBPHDZ\",
      \"verification_uri\":
        \"https://as.example.com/device\",
      \"expires_in\":600}",
  "userCode": "TXBBPHDZ",
  "verificationUri":
    "https://as.example.com/device",
  "verificationUriComplete":
    "https://as.example.com/device?user_code=TXBBPHDZ",
...
}

The authorization server extracts the value of “responseContent” and sends it back to the client as a device authorization response (step #5. Details omitted).

“Verification URI”

Verifying the user_code

The client will send “device access token requests” to the server. The requests include the value of “device_code” extracted from the server’s device authorization response (described later in this section).

Concurrently with the token requests, the client will ask the end user to present the value of “user_code” (extracted from the device authorization response) at ”Verification URI” of the authorization server  (step #6).

How the code is shown to the user is up to the client. The following example is to present the code along with the value of “verification_uri” to the end user (based on an example in RFC 8628 ).

+-----------------------------------------------+
|                                               |
|  Using a browser on another device, visit:    |
|  https://as.example.com/device                |
|                                               |
|  And enter the code:                          |
|  TXBBPHDZ                                     |
|                                               |
+-----------------------------------------------+

Another example below shows that the client encodes the value of “verification_uri_complete”, which is included in the device authorization response as well, into QR code and asks the user to scan it  (based on an example in RFC 8628 ).

+-------------------------------------------------+
|                                                 |
|  Scan the QR code or, using     +------------+  |
|  a browser on another device,   |[_]..  . [_]|  |
|  visit:                         | .  ..   . .|  |
|  https://as.example.com/device  | . .  . ....|  |
|                                 |.   . . .   |  |
|  And enter the code:            |[_]. ... .  |  |
|  TXBBPHDZ                       +------------+  |
|                                                 |
+-------------------------------------------------+

Once receiving the user_code from the end user in some way (step #7), the “verification URI” of the authorization server forwards the user_code to Authlete’s /device/verification API (step #8, #9).

  • Request  (step #8 using curl command)
curl -s -X POST $apiUrl/device/verification \
    -u $apiKey:$apiSecret \
    -H 'Content-type: application/json' \
    -d '{"userCode":"TXBBPHDZ"}'
  • Response (step #9)
{
    "type": "deviceVerificationResponse",
    "resultCode": "A224001",
    "resultMessage": "[A224001] The user code is valid.",
    "action": "VALID",
    "claimNames": [
        ...
    ],
    "clientId": ...,
    "clientName": "Demo Client",
    "scopes": [
        {
            "defaultEntry": false,
            "name": "openid"
        },
        {
            "defaultEntry": false,
            "name": "profile"
        },
        {
            "defaultEntry": false,
            "name": "read"
        }
    ],
    ...
}

This response states that the verification in terms of the user_code was successful. It also includes other details about the client, requested scopes and/or claims etc.

The authorization server will employ these information to ask consent to the end user.

Completing the verification

The authorization server may authenticate the end user, and present the information of the client and details of the requested access based on the result described above (step #10, #11).

Then the server determines the end user’s identifier and properties (e.g. scopes, claims) of the token to be issued, and makes a request to /device/complete API (step #12, #13).

  • Request  (step #12 using curl command)
curl -s -X POST $apiUrl/device/complete
  -u $apiKey:$apiSecret
  -H 'Content-type: application/json'
  -d '{"userCode":"TXBBPHDZ",
       "result":"AUTHORIZED",
       "subject":"testuser01"}'
  • Response (step #13)
{
    "type": "deviceCompleteResponse",
    "resultCode": "A241001",
    "resultMessage": "[A241001] The API call was processed successfully.",
    "action": "SUCCESS"
}

On receiving the response above, The authorization server notifies the end user that the verification process is completed (step #14).

Token request and response

As described before, the client makes “device access token requests” using the value of “device_code” to the authorization server (step #a). It usually sends the requests multiple times (i.e. polling) until the authorization server completes the verification of the user_code and provides an access token.

  • Request (step #b using curl command)
curl -s -X POST $apiUrl/auth/token
  -u $apiKey:$apiSecret
  -H 'Content-type: application/json'
  -d '{"parameters":
         "client_id=...
          &grant_type=urn:ietf:params:oauth:grant-type:device_code
          &device_code=-jxwQ_7MEdR3SqS86bEg1ONUYdwGmSYjqH8eIBZ1c3U"}'
  • Response (step #c, before the verification of the user_code is completed)
{
  "type": "tokenResponse",
  "resultCode": "A242307",
  "resultMessage":
    "[A242307] The device authorization request has not been authorized yet.",
  "action": "BAD_REQUEST",
  "grantType": "DEVICE_CODE",
  "responseContent":
    "{\"error_description\":
        \"[A242307] The device authorization request has not been authorized yet.\",
      \"error\":\"authorization_pending\",
      \"error_uri\":\"https://docs.authlete.com/#A242307\"}",
...
}
  • Response (step #c, after the verification of the user_code is completed)
{
  "type": "tokenResponse",
  "resultCode": "A242002",
  "resultMessage":
    "[A242002] The token request
     (grant_type=urn:ietf:params:oauth:grant-type:device_code) was processed
     successfully.",
  "accessToken": "ZJHO26vXTC1LIQXm9aYUFnMZd4R599aFA4hLBmH-OlM",
  "action": "OK",
  "clientId": ...,
  "grantType": "DEVICE_CODE",
  "idToken":
    "eyJhbGciOiJIUzI1NiJ9.
     eyJhdF9oYXNoIjoiZkpNOHhuODlTaVNQVnNsMGFLYnBTQSIsInN1YiI6InRlc3R1
     c2VyMDEiLCJhdWQiOiIxNzIwMTA4MzE2NjE2MSIsImlzcyI6Imh0dHBzOi8vYXV0
     aGxldGUuY29tIiwiZXhwIjoxNTk2NjE5OTk2LCJpYXQiOjE1OTY1MzM1OTZ9.
     OYuGqNbombW_DrSHsm9A07LZWa4UWyV_hSiSAQy-CYI",
  "refreshToken": "sliwK3Oa6Pag1c2aGenZALcGZXAP9cIiIu_zjGIdBCI",
  "responseContent":
    "{\"access_token\":\"ZJHO26vXTC1LIQXm9aYUFnMZd4R599aFA4hLBmH-OlM\",
      \"refresh_token\":\"sliwK3Oa6Pag1c2aGenZALcGZXAP9cIiIu_zjGIdBCI\",
      \"scope\":\"openid profile read\",
      \"id_token\":
        \"eyJhbGciOiJIUzI1NiJ9.
          eyJhdF9oYXNoIjoiZkpNOHhuODlTaVNQVnNsMGFLYnBTQSIsInN1YiI6InRlc3R1
          c2VyMDEiLCJhdWQiOiIxNzIwMTA4MzE2NjE2MSIsImlzcyI6Imh0dHBzOi8vYXV0
          aGxldGUuY29tIiwiZXhwIjoxNTk2NjE5OTk2LCJpYXQiOjE1OTY1MzM1OTZ9.
          OYuGqNbombW_DrSHsm9A07LZWa4UWyV_hSiSAQy-CYI\",
     \"token_type\":\"Bearer\",
     \"expires_in\":3600}",
  "scopes": [
    "openid",
    "profile",
    "read"
  ],
  "subject": "testuser01",
...
}

The authorization server extracts the value of “responseContent” and sends it back to the client  (step #d).

See also