PingAM 7.5.1

Device authorization grant

The device authorization grant, formerly known as the device flow, is designed for client devices that have limited user interfaces, such as a set-top box, streaming radio, or a server process running on a headless operating system.

Instead of logging in with the client device, you can authorize the client to access protected resources on your behalf. You log in with a different user agent, such as a phone or Internet browser, and authenticate using a code that the client device displays.

The device grant flow

OAuth 2.0 device grant flow
Figure 1. OAuth 2.0 device grant flow
  1. The client device requests a device code from AM.

  2. AM returns a device code, a user code, a URL for entering the user code, and an interval, in seconds.

  3. The client device provides instructions to the user to enter the user code. The client may choose an appropriate method to convey the instructions, for example, text instructions on screen, or a QR code.

  4. The client device continuously polls AM to check if authorization has been completed.

  5. If the user hasn’t completed the authorization, AM returns an HTTP 403 status code, with an authorization_pending message.

  6. The user follows the instructions from the client device to enter the user code by using a separate device.

  7. If the user code is valid, AM redirects the resource owner for authentication.

  8. Upon authentication, the user is prompted to confirm the user code. The page is pre-populated with the one entered before.

  9. The user can authorize the client device. The AM consent page also displays the requested scopes, and their values.

    If the user had a valid session when they entered the code and the client is configured to skip consent, AM doesn’t display the confirmation or the consent pages.

    This is also true if you perform the call using REST and pass the decision=allow parameter.

  10. Upon authorization, AM responds to the client device’s polling with an HTTP 200 status, and an access token, giving the client device access to the requested resources.

Demonstrate the device grant flow

Follow these steps to demonstrate the OAuth 2.0 device flow:

Prepare the demonstration

This demonstration assumes the following configuration:

  • AM is configured as an OAuth 2.0 authorization server.

    Ensure that the Device Code grant type is configured in the Grant Types field.

    For more information, refer to Authorization server configuration.

  • A public client called myClient is registered in AM with the following configuration:

    • Scopes: write

    • Grant Types: Device Code

    For more information, refer to Client application registration.

Get a user code for the device

Devices can display a user code and instructions for a user, which can be used on a separate client to provide consent, allowing the device to access resources.

User codes consist of a random selection of characters. You can configure the character set.

By default, AM generates the user code from eight of the following characters:

234567ABCDEFGHIJKLMNOPQRSTVWXYZabcdefghijkmnopqrstvwxyz

You can configure the list of possible characters to improve usability. For example, remove similar characters such as '4' and 'A' to reduce ambiguity on low-resolution device screens, or limit input to either alphabetical or numerical characters to suit mobile keyboards.

The length of the user code is also configurable.

For more information, refer to the device flow configuration.

Perform the following steps to request a user code in the OAuth 2.0 device flow:

  1. As the client, call the /oauth2/device/code endpoint specifying the client ID, myClient, as a minimum.

    For information about the parameters supported by the /oauth2/device/code endpoint, refer to /oauth2/device/code. For information about private client authentication methods, refer to OAuth 2.0 client authentication.

    For example:

    $ curl \
    --request POST \
    --data "client_id=myClient" \
    --data "scope=write" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/code"
    {
        "interval": 5,
        "device_code": "7a95a0a4-6f13-42e3-ac3e-d3d159c94c55…​",
        "verification_uri": "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/user",
        "verification_url": "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/user",
        "verification_uri_complete": "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/user?user_code=VAL12e0v",
        "user_code": "VAL12e0v",
        "expires_in": 300
    }

    On success, AM returns a user code, a verification URI, and a verification_uri_complete value comprising the user code appended to the URI, which can be used to create QR codes.

    The output includes the verification_url to support earlier versions of the specification.

    AM also returns an interval, in seconds, that the client device must wait between requests for an access token.

    You can configure the returned values by navigating to Realms > Realm Name > Services > OAuth2 Provider > Device Flow.

  2. The client device should now give instructions to the user to enter the user code and grant access to the OAuth 2.0 device.

    The client can choose an appropriate method to convey the instructions, for example, text instructions on screen, or a QR code. Perform the steps in one of the following procedures:

  3. The client device should also begin polling the authorization server for the access token using the interval and device code information obtained in the previous step.

    For more information, refer to Poll for authorization.

Grant consent using REST

The OAuth 2.0 device authorization grant requires the user to grant consent to let a client device access a resource. The authorization server provides the client with an access token.

To grant consent with a user code without using a browser, perform the following steps:

  1. The resource owner logs in to the authorization server.

    This example authenticates the demo user:

    $ curl \
    --request POST \
    --header "Content-Type: application/json" \
    --header "X-OpenAM-Username: demo" \
    --header "X-OpenAM-Password: Ch4ng31t" \
    --header "Accept-API-Version: resource=2.0, protocol=1.0" \
    'https://openam.example.com:8443/openam/json/realms/root/realms/alpha/authenticate'
    {
        "tokenId":"AQIC5wM…​TU3OQ*",
        "successUrl":"/openam/console",
        "realm":"/alpha"
    }
  2. The client sends a POST request to the authorization server’s device user endpoint, specifying the SSO token of the demo user and, at least, the following parameters:

    • user_code=resource-owner-user-code

    • decision=allow

    • csrf=demo-user-SSO-token

    The iPlanetDirectoryPro cookie is required and must contain the SSO token of the user granting access to the client.

    The scope and the client_id parameters aren’t included because the user code already contains that information.

    $ curl \
    --request POST \
    --header "Cookie: iPlanetDirectoryPro=AQIC5wM…​TU3OQ*" \
    --data "user_code=VAL12e0v" \
    --data "decision=allow" \
    --data "csrf=AQIC5wM…​TU3OQ*" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/user"

    For information about the parameters supported by the /oauth2/device/user endpoint, refer to /oauth2/device/user.

    AM returns HTML containing a JavaScript fragment named pageData, with details of the result. The following example shows the default HTML response:

    <!DOCTYPE html>
    <!--
      Copyright © 2015-2018 Ping Identity Corporation.
    
      Use of this code requires a commercial software license with Ping Identity Corporation
      or with one of its affiliates. All use shall be exclusively subject
      to such license between the licensee and Ping Identity Corporation.
    -->
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta name="description" content="OAuth2 Authorization">
      <title>OAuth2 Authorization Server</title>
    </head>
    
    <body style="display:none">
    <div id="wrapper">Loading...</div>
    <footer id="footer" class="footer"></footer>
    <script type="text/javascript">
      pageData = {
          locale: "en_US",
          baseUrl : "https://openam.example.com:8443/openam/XUI/",
          realm : "\/alpha",
          done: true
      };
    </script>
    <script src="https://openam.example.com:8443/openam/XUI/main-device.js"></script>
    </body>
    </html>

    The done: true in the pageData fragment means the flow can continue.

    To return the output in JSON format instead of HTML, include the Accept: application/json header in the request. The following example returns the output in JSON:

    $ curl \
    --request POST \
    --header "Cookie: iPlanetDirectoryPro=ne1QUOjPzY0r…​" \
    --header "Accept: application/json" \
    --data "user_code=Gp4qkX4G" \
    --data "decision=allow" \
    --data "csrf=ne1QUOjPzY0r…​" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/user"
    {
      "baseUrl": "http://am.example.com:8080/am/XUI",
      "errorCode": null,
      "realm": "/alpha",
      "csrf": "1hQSplblZgaErLQ0/1rvcEvw5lj/cJQx3lrC3dV/3ac=",
      "locale": "en_US",
      "userCode": "Gp4qkX4G"
    }

    In this case, the "errorCode": null means the request is successful and the flow can continue.

    If the supplied user code is wrong or has been used, the output includes the following pageData fragment:

    pageData = {
        locale: "en_US",
        errorCode: "not_found",
        realm : "/alpha",
        baseUrl : "https://openam.example.com:8443/openam/XUI/"
        oauth2Data: {
              csrf: "ErFIk8pMraJ1rvKbloTgpp6b7GZ57kyk9HaIiKMVK3g=",
              userCode: "VAL12e0v"
        }
    }

    In accordance with Section 4.1.1 of the OAuth 2.0 authorization framework, the authorization server must legitimately obtain an authorization decision from the resource owner.

    Clients using the endpoints to register consent are responsible for ensuring this requirement. AM can’t assert that consent was given in these cases.

Grant consent using a browser

OAuth 2.0 device flow requires that the user grants consent to allow the client device to access the resources. The authorization server would then provide the client with an access token.

To grant consent with a user code using a browser, perform the following steps:

  1. The resource owner navigates to the verification URL acquired with the user code, for example, https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/device/user.

  2. The resource owner logs in to the authorization server using, for example, the demo user credentials.

  3. The resource owner enters their user code:

    Visit the verification URL to enter the user code.
    Figure 2. OAuth 2.0 User Code
  4. The resource owner authorizes the device flow client by allowing the requested scopes:

    AM lists the scopes requested by the client device, and their values.
    Figure 3. OAuth 2.0 Consent Page

    If the client uses implied consent, AM does not display this screen.

  5. AM adds the OAuth 2.0 client to the user’s profile page in the Authorized Apps section and displays that the user is done with the flow:

    The user code part of the device flow is done.
    Figure 4. OAuth 2.0 Done Page

    The device now can request an access token from AM.

Poll for authorization

The client device must poll the authorization server for an access token, since it cannot know whether the resource owner has already given consent or not.

Perform the following steps to poll for an access token:

  1. On the client device, create a POST request to poll the /oauth2/access_token endpoint to request an access token specifying, at least, the following parameters:

    • client_id=your-client-id

    • grant_type=urn:ietf:params:oauth:grant-type:device_code

    • device_code=your-device-code

    For information about the parameters supported by the /oauth2/access_token endpoint, refer to /oauth2/access_token.

    The client device must wait for the number of seconds previously provided as the value of interval between polling AM for an access token. For example:

    $ curl \
    --request POST \
    --data "client_id=myClient" \
    --data "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
    --data "device_code=7a95a0a4-6f13-42e3-ac3e-d3d159c94c55…​" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"

    If the user has authorized the client device, AM returns an HTTP 200 status code with an access token that can be used to request resources:

    {
        "expires_in": 3599,
        "token_type": "Bearer",
        "access_token": "c1e9c8a4-6a6c-45b2-919c-335f2cec5a40"
    }

    If the user hasn’t authorized the client device, AM returns an HTTP 403 status code with the following error message:

    {
        "error": "authorization_pending",
        "error_description": "The user has not yet completed authorization"
    }

    If the client device is polling faster than the specified interval, AM returns an HTTP 400 status code with the following error message:

    {
        "error": "slow_down",
        "error_description": "The polling interval has not elapsed since the last request"
    }

    The authorization server can also issue refresh tokens at the same time the access tokens are issued. For more information, refer to Refresh tokens.