Device Flow with PKCE

The Device Flow is designed for client devices that have limited user interfaces, such as a set-top box. Since the devices are usually public clients and the device code can be intercepted by malicious users, you can combine the Device Flow with the PKCE standard (RFC 7636) to mitigate against interception attacks if the devices allow it.

This flow is beyond the PKCE and the Device Flow specs, since the Device Flow spec is still a draft and it is not officially recognized by the PKCE standard. AM implements this flow by adding the following parameters on top of those used for the Device Flow grant:

  • code_verifier (form parameter). Contains a random string that correlates the authorization request to the token request.

  • code_challenge (query parameter). Contains a string derived from the code verifier that is sent in the authorization request and that needs to be verified later with the code verifier.

  • code_challenge_method (query parameter). Contains the method used to derive the code challenge.

OAuth 2.0 Device Flow with PKCE
Diagram of the OAuth 2.0 Device Flow with PKCE in AM

The steps in the diagram are described below:

  1. When using the PKCE standard, the device must be able to generate a code verifier and a code challenge. For details, see the PKCE standard (RFC 7636).

    For a JavaScript example that generates a code challenge and a code verifier, see "To Generate a Code Verifier and a Code Challenge".

  2. The client device requests a device code from AM, appending the code challenge previously generated to the request.

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

  4. 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.

  5. The client device begins to continuously poll AM to see if authorization has been completed, appending the code verifier previously generated.

  6. If the user has not yet completed the authorization, AM returns an HTTP 403 status code, with an authorization_pending message.

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

  8. The authorization server verifies the code challenge stored in memory using the validation code. It also verifies the user code. If both codes are valid, AM redirects the resource owner for authentication.

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

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

    Note

    AM does not display the confirmation nor the consent pages if the user has a valid session when they entered the code, and the client is allowed to skip consent.

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

  11. 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.

The following procedures show how to use the OAuth 2.0 device flow endpoints:

To Obtain a User Code For the Device in the Device Flow with PKCE

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 the resources.

As user codes may be displayed on lower resolution devices, the list of possible characters used has been optimized to reduce ambiguity. User codes consist of a random selection of eight of the following characters:

234567ABCDEFGHIJKLMNOPQRSTVWXYZabcdefghijkmnopqrstvwxyz

This procedure assumes the following configuration:

  • AM is configured as an OAuth 2.0 authorization server. Ensure that:

    • The token and the device_code plugins are configured in the Response Type Plugins field.

    • The Device Code grant type is configured in the Grant Types field.

    The Code Verifier Parameter Required drop-down (Realms > Realm Name > Services > OAuth2 Provider > Advanced) specifies whether AM require clients to include a code verifier in their calls.

    However, if a client makes a call to AM with the code_challenge parameter, AM will honor the code exchange regardless of the configuration of the Code Verifier Parameter Required drop-down.

    For more information, see Authorization Server Configuration.

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

    • Scopes: write

    • Response Types: device_code token

    • Grant Types: Device Code

For more information, see Client Registration.

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

  1. The client creates a POST request to the /oauth2/device/code endpoint specifying, at least, the following parameters:

    • response_type=device_code

    • client_id=your_client_ID

    • code_challenge=your_code_challenge

    • code_challenge_method=S256

      Creating the challenge using a SHA-256 algorithm is mandatory if the device supports it, as per the RFC 7636 standard.

    For information about the parameters supported by the /oauth2/device/code endpoint, see "/oauth2/device/code". For information about private client authentication methods, see OAuth 2.0 Client Authentication.

    For example:

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

    On success, AM returns a verification URI (the verification_url output is included to support earlier versions of the draft), and a user code to enter at that URL. AM also returns an interval, in seconds, that the client device must wait for in between requests for an access token.

    Tip

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

  2. The client device should now provide instructions to the user to enter the user code and grant access to the OAuth 2.0 device. The client may 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 and the PKCE code verifier. For more information, see "To Poll for Authorization in the OAuth 2.0 Device Flow".

To Grant Consent with a User Code Without Using a Browser in the Device Flow with PKCE

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 without using a browser, perform the following steps:

  1. The resource owner logs in to the authorization server, for example, using the credentials of the demo user. For example:

    $ 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/authenticate'
    {
        "tokenId":"AQIC5wM...TU3OQ*",
        "successUrl":"/openam/console",
        "realm":"/"
    }
  2. The client makes a POST call to the authorization server's authorization device user endpoint specifying in a cookie SSO token of the demo and, at least, the following parameters:

    • user_code=resource_owner_user_code

    • decision=allow

    • csrf=demo_user_SSO_token

    For information about the parameters supported by the /oauth2/device/user endpoint, see "/oauth2/device/user".

    The iPlanetDirectoryPro cookie is required and should contain the SSO token of the user granting access to the client. For example:

    $ 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/device/user"

    The scope and the client_id parameters have not been included because the user code already contains that information.

    AM returns HTML containing a JavaScript fragment named pageData, with details of the result.

    Successfully allowing or denying access returns:

    pageData = {
        locale: "en_US",
        baseUrl : "https://openam.example.com:8443/openam/XUI/",
        realm : "/\",
        done: true
    }

    done: true means that the flow can now continue.

    If the supplied user code has already been used, or is incorrect, AM returns the following:

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

    Important

    As per Section 4.1.1 of the OAuth 2.0 authorization framework, it is required that the authorization server legitimately obtains an authorization decision from the resource owner.

    Any client using the endpoints to register consent is responsible for ensuring this requirement, AM cannot assert that consent was given in these cases.

To Grant Consent with a User Code Using a Browser in the Device Flow with PKCE

OAuth 2.0 Device Flow requires that the user grants consent to allow the client device to access 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/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:

    OAuth 2.0 User Code
    OAuth 2.0 user code.

    Note

    If the user is not logged in to AM when they provide the code, AM redirects them to the login page.

    After authenticating successfully, the user is prompted to enter the code again. The user code is pre-populated with the code they entered before.

  4. The resource owner authorizes the device flow client by granting the requested scopes:

    OAuth 2.0 Consent Page
    OAuth 2.0 consent page.

    Note

    If the client is allowed to skip consent, the user will not see 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:

    OAuth 2.0 Done Page
    OAuth 2.0 Done Page

    The device now can request an access token from AM.

To Poll for Authorization in the OAuth 2.0 Device Flow with PKCE

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:

  • 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=http://oauth.net/grant_type/device/1.0

    • code=your_device_code

    • code_verifier=your_code_verifier

    For information about the parameters supported by the /oauth2/access_token endpoint, see "/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=http://oauth.net/grant_type/device/1.0" \
    --data "code=7a95a0a4-6f13-42e3-ac3e-d3d159c94c55..." \
    --data "code_verifier=ZpJiIM_G0SE9WlxzS69Cq0mQh8uyFaeEbILlW8tHs62SmEE6n7Nke0XJGx_F4OduTI4"
    "https://openam.example.com:8443/openam/oauth2/realms/root/access_token"

    If the user has authorized the client device, an HTTP 200 status code is returned, 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 has not yet authorized the client device, an HTTP 403 status code is returned, 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, an HTTP 400 status code is returned, with the following error message:

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

    Tip

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

Read a different version of :