Device flow support in the OAuth 2.0 / OpenID Connect SDK
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).