Articles

OAuth: Device Flows

Share this post:

OAuth: Device Flows

Introduction to Device Flows

As IOT devices become more prevalent, so does the importance of the way these devices interact with user information and the web. These devices often need to call APIs which require authentication, but cannot provide a suitable method of user interaction in order for traditional authentication mechanisms such as username/password. Because these devices intend to consume APIs, OAuth is a clear choice to secure the authorization, however OAuth traditionally still requires browser interaction during authentication.

The OAuth device flow seeks to define a mechanism which solves this problem, providing the well defined security patterns of OAuth, while enabling an alternative method for the user to authenticate to the authorization server. The OAuth device flow is currently in Draft Version 10.

New in ISAM 9.0.5.0 is support for device flows.

User Experience

The device flow starts with a device requesting authorization. This results in two codes, one which is kept by the device(device_code) and the user_code which is displayed with a verification_uri  to the end user.

An example screen a smart device might show to an end user. They may either manually enter the URI and the code, or they may choose to scan the QR code with a mobile device

An example screen that a device might show to an end user. They may either manually enter the URL and the code, or they may choose to scan the QR code with a mobile device

The user is prompted with instructions to visit the URL, and login. The user enters the code and if this is successful they may be prompted to consent to the devices request. After this, a success message is shown

The three user interaction steps: 1, Authentication. 2, Prompting for a code. 3, The success page.

The three user interaction steps: Authentication, prompting for a code , and the success page.

 

While the user is performing these operations, the client is silently polling the /token endpoint of the authorization server. Once it polls the endpoint after the user_code is authorized, it will be issued a bearer token.

 

Device Flow Sequence

The diagram below shows the interactions between the entities; the device, the authorization server,  and the user. Step 3 is highlighted to show that this is a polling operation. Step 4 the flow of information is one way, as the user has viewed the device and used this to know the required input into their mobile device.

diag

Details on each step are:

  1. The device, upon powering on or some other ‘ready’ event, makes a request to the device_authorize endpoint. This request includes a client_id and potentially a scope as defined by the OAuth 2.0 specification.
  2. The response from a successful request is returned to the client, this includes a device_codeuser_code and verification_uri. Additional parameters may be returned. At this point, the client displays the user_code and verification_uri, while keeping device_code a secret.
  3. The client begins polling the token endpoint of the authorization server. It uses the grant_type value of urn:ietf:params:oauth:grant-type:device_code. Using this grant type the client presents its secret device_code. The device may be told to slow_down if it is polling to fast, and the device_code may eventually expire, which would force the client to restart the flow.
  4. While the device is polling, it will display the verification_uri and user_code, to the end user. It may also choose to present this information in another manner, such as a QR code.
  5. At this point the user is involved. The user borwses to the verification_uri where they can enter the user_code. The authorization server prompts the user to authenticate and potentially consent to the device.
  6. Once authentication has completed, the device which is polling with the device_code will succeed and an OAuth 2.0 Bearer Token  is returned.

 

Configuring Device flows with ISAM

To perform device flows on ISAM. An OpenID Connect and API Protection definition needs to be configured for device flows. On the API definition creation page check the “Device Grant” check box under the grant types heading.

grants

Note: This assumes a configured web reverse proxy and other relevent components. For more on this visit the knowledge center for the pages on OpenID Connect and API protection and Reverse Proxy configuration

An example client implementation

Below is a python script which emulates a device.

#!/bin/python3

import requests
import urllib3
import json
import sys
import time

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

######################################################################
#CONSTANTS. Update these with your own values
######################################################################
#Should be the protocol + hostname + port + junction of your definition
BASE_URI="https://www.myidp.ibm.com/mga"

#client_id to use in the flow
CLIENT_ID="myclientid"

#any scopes to include
SCOPE="scope1 scope2"
######################################################################

def main():
    rest_hdr= {"accept":"application/json"}

    error=None

    while(error == None or error == "expired_token"):

        token_uri = "{0}/sps/oauth/oauth20/token".format(BASE_URI)
        device_authorize= "{0}/sps/oauth/oauth20/device_authorize".format(BASE_URI)

        device_authorize_request_data={"client_id":CLIENT_ID, "scope":SCOPE } 
        print(chr(27) + "[2J")
        print("\n")
        print("\n")
        print("\tInitiating device flow.\n")

        #For details on this request: https://tools.ietf.org/html/draft-ietf-oauth-device-flow-09#section-3.1
        req = requests.post(url=device_authorize, params=device_authorize_request_data, headers=rest_hdr, verify=False)

        if(req.status_code != 200):
            print(req.text)
            print("\tError getting device_authorize endpoint. Exiting\n\n")
            sys.exit(1)
        #For details on this response: https://tools.ietf.org/html/draft-ietf-oauth-device-flow-09#section-3.2
        device_code = req.json()['device_code']
        user_code = req.json()['user_code']
        verification_uri = req.json()['verification_uri']

        print(
"""
\tReceived: \n\t\tDevice Code(A secret, not usually shown): {0}\n\t\tUser \
Code: {1}\n\t\tVerification_uri: {2}\n\n\t Visit the verification uri\
 and input the user code\n\n\n
"""
                .format(device_code,user_code,verification_uri))

        print('\n\tPolling for the token to be validated.', end='')
        df = {"client_id": CLIENT_ID,"grant_type": "urn:ietf:params:oauth:grant-type:device_code", 
                "device_code":device_code}

        bearer = None
        slower=1
        while(bearer == None and error != "expired_token"):
            #For details on this response: https://tools.ietf.org/html/draft-ietf-oauth-device-flow-09#section-3.4
            token = requests.post(url=token_uri,  data=df, verify=False)

            error = token.json().get("error")

            if (error == None):
                bearer = token.json()

            if(error == "slow_down"):
                slower +=1

            print(".", end="")
            sys.stdout.flush()
            time.sleep(slower)


        if bearer != None: 
            print("\n\tFlow successful")
            print("\n\t\tAccess Token: {0}".format(bearer["access_token"]))
            print("\n\t\tRefresh Token: {0}".format(bearer["refresh_token"]))
            print("\n\t\tScope: {0}".format(bearer["scope"]))
            break


if __name__ == "__main__":
    main()

Note: When running the above, if the verification_uri returned to the user is incorrect, update the pre-token mapping rule to return the correct value

Running the device script

Example usage and expected output from the device script is provided below:

$ python3 device.py
   	Initiating device flow.


	Received:
		Device Code(A secret, not usually shown): jWSBZPbSAUKqNryGpnG41rls6TqSdL
		User Code: uuvw-z2pd
		Verification_uri: https://www.myidp.ibm.com/mga/sps/oauth/oauth20/user_authorize

	 Visit the verification uri and input the user code





	Polling for the token to be validated...............
	Flow successful

		Access Token: baafbggebagabaggagb

		Refresh Token: eagaefaeegefcbfdefeeegdageacdbceceaadeea

		Scope: scope1 scope2



Demonstration

 

 

 Known Issues:

 

Issue Description APAR Work Around
Tokens issued do not have a lifetime http://www-01.ibm.com/support/docview.wss?crawler=1&uid=swg1IJ07345 Add a snippet to the post-token mapping rule which updates the lifetime of the token. Eg:

if(request_type == "access_token" && grant_type == "urn:ietf:params:oauth:grant-type:device_code") {
  var tokens = OAuthMappingExtUtils.getTokens(state_id);
  for(var i = 0 ; i < tokens.length; i++) {
    var t = tokens[i];
    var type = "" + t.getType();
    var lifetime = 604800 
    if(type == 'access_token') {
      lifetime = 3600;
    }
    var updated = OAuthMappingExtUtils.updateToken(t.getId(), lifetime,null,true);
  }
}

 

Thank you to Keiran for your help and input on this article.

Click here to rate this article

Rate this article :

Software Engineer - IBM Security Access Manager

More Articles stories
By Carsten Hagemann on June 20, 2022

Getting started with the IBM Verify SDK

The IBM Verify SDK is a library available for Android and iOS and provide classes to create rich native client mobile applications that interact with IBM Security Verify and IBM Security Verify Access, so that enterprises can easily integrate flexible and intelligent multi-factor authentication into their applications. Multi-factor authentiation (MFA) verifies an indiviual’s identity by […]

Continue reading

By Martin Schmidt on July 11, 2019

Modernizing your B2C Portal Security – LDAP Proxy Deep Dive

In this part of our series we are taking a deeper look on how the LDAP reverse proxy works and what is needed to be done to make it work. Enable CI In this part we look at what needs to be done on the CI side and what information needs to be collected. We […]

Continue reading

By Martin Schmidt on May 4, 2019

Modernizing your B2C Portal Security – Desired End State

Proposition: As we have seen in part one of this series, managing customer identities for a portal can be a challenge and distraction for the business.  In this part of the series we will outline how a modernized solution for a portal security can simplify operations and free your team up to focus on the […]

Continue reading