Table of Contents
“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.
An authorization server must have the following endpoints and “verification URI” to support the device flow. Authlete provides functions as APIs for implementing them.
Device authorization endpoint
Token endpoint
“Verification URI”
This section explains settings for enabling the device flow. You have to configure both Authlete service and its client that uses the flow.
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 |
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.
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.
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).
curl -s -X POST $apiUrl/device/authorization \
-u $apiKey:$apiSecret \
-H 'Content-type: application/json' \
-d '{"parameters": "client\_id=...&scope=openid+profile+read
"}'
{
"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).
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).
curl -s -X POST $apiUrl/device/verification \ -u $apiKey:$apiSecret \ -H 'Content-type: application/json' \ -d '{"userCode":"TXBBPHDZ"}'
{
"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.
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).
curl -s -X POST $apiUrl/device/complete -u $apiKey:$apiSecret -H 'Content-type: application/json' -d '{"userCode":"TXBBPHDZ", "result":"AUTHORIZED", "subject":"testuser01"}'
{
"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).
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.
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"}'
{
"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\"}",
...
}
{ "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).