Device flow support in the OAuth 2.0 / OpenID Connect SDK

Posted 2019-03-23

The device authorisation grant is now available in the OAuth 2.0 / OpenID Connect SDK, thanks to a contribution by Emond Papegaaij from Topicus KeyHub.

Also known as device flow, this is a special OAuth 2.0 grant intended for cases where the client resides on one device and the user gets authenticated and authorises the request on another.

Here are some typical scenarios:

  • The client is an IoT or smart home device which doesn't have a browser, but is capable of displaying a short code to the user, such as "WDJB-MJHT", or is able to transmit the code (e.g. via Bluetooth) to a smart phone or computer that belongs to the user.

  • The client, such as a smart TV or game console, is capable of launching a browser, but for reasons of convenience or security the client request is to be authorised on the user's smart phone or computer.

  • The client is a console / CLI or remotely accessed application.

The device flow

What are the steps in the device flow?

1. Device authorisation request and response

The client posts a request directly to the Authorisation server, specifying the scope (potentially implicitly) of the access token. The client can be public or confidential (authenticating itself with a credential).

POST /device-authz HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

client_id=123&scope=post_stats

The Authorisation server remembers the client request and responds with a unique device code, a user code and a few additional parameters required by the flow.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
  "device_code"               : "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
  "user_code"                 : "WDJB-MJHT",
  "verification_uri"          : "https://c2id.com/device",
  "verification_uri_complete" : "https://c2id.com/device?user_code=WDJB-MJHT",
  "expires_in"                : 1800,
  "interval"                  : 5
}

2. User interaction

The client displays the user code along with a verification URI at the Authorisation server where the client is to be authorised, by letting the user type in the code.

The verification URI with the code could also be encoded in a QR code.

After the Authorisation server successfully verifies the user code, the user is presented with a consent screen to authorise or deny the client request.

3. Token request

During the user interaction the client continuously polls the token endpoint of the Authorisation server with the device code:

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
&client_id=123

If the user interaction has not completed the Authorisation server will return an authorization_pending error code, to signal the client to repeat the token request after the suggested interval (returned in the device authorisation response):

HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "error" : "authorization_pending"
}

The client stops the polling when the Authorisation server returns one of the following responses:

  • The request was authorised by the user, the Authorisation server provides the requested token(s):

    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    
    {
    "token_type"    : "Bearer",
    "access_token"  : "2YotnFZFEjr1zCsicMWpAA",
    "expires_in"    : 3600,
    "refresh_token" : "tGzv3JOkF0XG5Qx2TlKWIA",
    "scope"         : "post_stats"
    }
    
  • The request was denied by the user:

    HTTP/1.1 400 Bad Request
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    
    {
    "error" : "access_denied"
    }
    
  • The device code has expired:

    HTTP/1.1 400 Bad Request
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    
    {
    "error" : "expired_token"
    }
    

SDK usage

The Java classes for implementing the device authorisation grant on the client and server side are found in the com.nimbusds.oauth2.sdk.device package.

Example device authorisation request:

// Create the device authZ requst and post it to the AS
URI deviceAuthzURI = new URI("https://c2id.com/device-authz");
ClientID clientID = new ClientID("123");
Scope scope = new Scope("post_stats");

HTTPRequest httpRequest = new DeviceAuthorizationRequest.Builder(clientID)
    .scope(scope)
    .endpointURI(deviceAuthzURI)
    .build()
    .toHTTPRequest();

HTTPResponse httpResponse = httpRequest.send();

// Parse the response
DeviceAuthorizationResponse response = DeviceAuthorizationResponse.parse(httpResponse);

if (! response.indicatesSuccess()) {
    System.out.println("Error: " + response.getErrorObject());
    return;
}

DeviceAuthorizationSuccessResponse successResponse = response.toSuccessResponse();

System.out.println("Device code: " + successResponse.getDeviceCode());
System.out.println("User code: " + successResponse.getUserCode());

Release notes

version 6.6 (2019-03-19)

  • Adds new com.nimbusds.oauth2.sdk.device package implementing the OAuth 2.0 Device Authorization Grant (draft-ietf-oauth-device-flow-15).