Device Authorization Flow

This tutorial demonstrates how to call your API from an input-constrained device using the Device Authorization Flow. We recommend that you log in to follow this quickstart with examples configured for your account.

For an interactive experience, you can use the Device Flow Playground.

1

Prerequisites

2

Request device code

When the user starts the device application and wants to authorize it, your application must request a device code from the Auth0 Authentication API to associate with the user session.

To get the device code, your application must call the Authentication API Device Authorization Flow Authorize endpoint:

curl --request post \
--url 'https://{yourDomain}/oauth/device/code' \
--header 'content-type: application/x-www-form-urlencoded'

feedbackSection.helpful

/
var client = new RestClient("https://{yourDomain}/oauth/device/code");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);

feedbackSection.helpful

/
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://{yourDomain}/oauth/device/code"
req, _ := http.NewRequest("post", url, nil)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}

feedbackSection.helpful

/
HttpResponse<String> response = Unirest.post("https://{yourDomain}/oauth/device/code")
.header("content-type", "application/x-www-form-urlencoded")
.asString();

feedbackSection.helpful

/
var axios = require("axios").default;
var options = {
method: 'post',
url: 'https://{yourDomain}/oauth/device/code',
headers: {'content-type': 'application/x-www-form-urlencoded'}
};
axios.request(options).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.error(error);
});

feedbackSection.helpful

/
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/oauth/device/code"]
                                                   cachePolicy:NSURLRequestUseProtocolCachePolicy

                                               timeoutInterval:10.0];

[request setHTTPMethod:@"post"];
[request setAllHTTPHeaderFields:headers];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                                            if (error) {

                                                NSLog(@&quot;%@&quot;, error);

                                            } else {

                                                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;

                                                NSLog(@&quot;%@&quot;, httpResponse);

                                            }

                                        }];

[dataTask resume];

feedbackSection.helpful

/
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{yourDomain}/oauth/device/code",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "post",
CURLOPT_HTTPHEADER => [
&quot;content-type: application/x-www-form-urlencoded&quot;

],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}

feedbackSection.helpful

/
import http.client
conn = http.client.HTTPSConnection("")
headers = { 'content-type': "application/x-www-form-urlencoded" }
conn.request("post", "/{yourDomain}/oauth/device/code", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

feedbackSection.helpful

/
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://{yourDomain}/oauth/device/code")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/x-www-form-urlencoded'
response = http.request(request)
puts response.read_body

feedbackSection.helpful

/
import Foundation
let headers = ["content-type": "application/x-www-form-urlencoded"]
let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/oauth/device/code")! as URL,
                                    cachePolicy: .useProtocolCachePolicy,

                                timeoutInterval: 10.0)

request.httpMethod = "post"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)

} else {
let httpResponse = response as? HTTPURLResponse

print(httpResponse)

}
})
dataTask.resume()

feedbackSection.helpful

/

3

Receive device code

The device application should receive an HTTP 200 response and a payload similar to this:

{
"device_code": "GmRh...k9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://my-tenant.auth0.com/device",
"verification_uri_complete": "https://my-tenant.auth0.com/device?user_code=WDJB-MJHT",
"expires_in": 900,
"interval": 5
}

feedbackSection.helpful

/

4

Request device activation

After your device application receives the device_code and the user_code, it should instruct the user to go to the verification_uri and enter the user_code.

5

Poll the token endpoint

While your device application waits for the user to activate it, it should call the Authentication API POST /oauth/token endpoint intermittently and handle the response appropriately.

curl --request POST \ 
--url 'https://{yourDomain}/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=urn:ietf:params:oauth:grant-type:device_code \
--data device_code=AUTH0_SCOPES \
--data 'client_id={yourClientId}'

feedbackSection.helpful

/

6

User authorization

The user will either scan the QR code, or else will open the activation page and enter the user code:

A confirmation page will be shown to have the user confirm that this is the right device:

The user will complete the transaction by signing in. This step may include one or more of the following processes:

  • Authenticating the user

  • Redirecting the user to an Identity Provider to handle authentication

  • Checking for active SSO sessions

  • Obtaining user consent for the device, unless consent has been previously given

Upon successful authentication and consent, the confirmation prompt will be shown:

At this point, the user has authenticated and the device has been authorized.

7

Receive tokens

After the user authorizes the device application, it receives an HTTP 200 response and the following payload:

{
"access_token": "eyJz93a...k4laUWw",
"refresh_token": "GEbRxBN...edjnXbL",
"id_token": "eyJ0XAi...4faeEoQ",
"token_type": "Bearer",
"expires_in": 86400
}

feedbackSection.helpful

/

Access tokens are used to call the Authentication API Get User Info endpoint (if your device application requested the openid scope) or the API that was specified by the audience parameter. If you are calling your own API, your device application must verify the access token before using it.

ID tokens contain user information that must be decoded and extracted. The Authentication API only returns an id_token if your device application requested the openid scope.

Refresh tokens are used to obtain a new access token or ID token after the previous one has expired. The Authentication API only returns a refresh_token if the Allow Offline Access setting is enabled for the API specified by the audience parameter, and your device application requested the offline_access scope.

8

Call your API

To call your API, your device application must pass the access token as a Bearer token in the Authorization header of your HTTP request.

curl --request GET \
--url https://myapi.com/api \
--header 'authorization: Bearer AUTH0_API_ACCESS_TOKEN' \
--header 'content-type: application/json'

feedbackSection.helpful

/

9

Refresh tokens

To get a new access token for a user, your device application can call the Authentication API POST /oauth/token endpoint with the refresh_token parameter.

curl --request POST \
--url 'https://{yourDomain}/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=refresh_token \
--data 'client_id={yourClientId}' \
--data 'client_secret={yourClientSecret}' \
--data refresh_token=AUTH0_REFRESH_TOKEN

feedbackSection.helpful

/

If the request was successful, your device application receives an HTTP 200 response with the following payload:

{
"access_token": "eyJ...MoQ",
"expires_in": 86400,
"scope": "openid offline_access",
"id_token": "eyJ...0NE",
"token_type": "Bearer"
}

feedbackSection.helpful

/

To learn more about refresh tokens, read Refresh Tokens.

10

Troubleshooting

Tenant logs are created for any interaction that takes place and can be used to troubleshoot issues.

**Code** **Name** **Description**
fdeaz Failed device authorization request
fdeac Failed device activation
fdecc User canceled the device confirmation
fede Failed Exchange Device Code for Access Token
sede Success Exchange Device Code for Access Token

Token responses

While you wait for the user to authorize the device, you may receive a few different HTTP 4xx responses.

Authorization pending

You will see this error while waiting for the user to take action. Continue polling using the suggested interval retrieved in the previous step of this tutorial.

`HTTP 403`
{
"error": "authorization_pending",
"error_description": "..."
}

feedbackSection.helpful

/

Slow down

You are polling too fast. Slow down and use the suggested interval retrieved in the previous step of this tutorial. To avoid receiving this error due to network latency, you should start counting each interval after receipt of the last polling request's response.

`HTTP 429`
{
"error": "slow_down",
"error_description": "..."
}

feedbackSection.helpful

/

Expired token

The user has not authorized the device quickly enough, so the device_code has expired. Your application should notify the user that the flow has expired and prompt them to reinitiate the flow.

`HTTP 403`
{
"error": "expired_token",
"error_description": "..."
}

feedbackSection.helpful

/

Access Denied

If access is denied, you receive:

`HTTP 403`
{
"error": "access_denied",
"error_description": "..."
}

feedbackSection.helpful

/

This can occur for a variety of reasons, including:

  • The user refused to authorize the device.

  • The authorization server denied the transaction.

  • A configured Action denied access.

11

Sample implementations

Refer to the samples below to learn how to implement this flow in real-world applications.

  • Device Authorization Playground

  • AppleTV (Swift): Simple application that shows how Auth0 can be used with the Device Authorization Flow from an AppleTV.

  • CLI (Node.js): Sample implementation of a CLI that uses the Device Authorization Flow instead of the Authorization Code Flow. The major difference is that your CLI does not need to host a webserver and listen on a port.

12

Limitations

To use the Device Authorization Flow, a device application must:

In addition, the Device Authorization Flow does not allow:

Next Steps

Excellent work! If you made it this far, you should now have login, logout, and user profile information running in your application.

This concludes our quickstart tutorial, but there is so much more to explore. To learn more about what you can do with Auth0, check out:

  • Auth0 Dashboard - Learn how to configure and manage your Auth0 tenant and applications
  • Auth0 Marketplace - Discover integrations you can enable to extend Auth0’s functionality

Did it work?

Any suggestion or typo?

Edit on GitHub
Sign Up

Sign up for an or to your existing account to integrate directly with your own tenant.