Device Authorization Grant
Introduction
The device authorization grant is designed for Internet connected devices that either lack a browser to perform a user-agent based authorization or are input constrained to the extent that requiring the user to input text in order to authenticate during the authorization flow is impractical. It enables clients on such devices (like smart TVs, media consoles, digital picture frames, and printers) to obtain user authorisation to access protected resources by using a user agent on a separate device.
Implementation follows OAuth 2.0 Device Authorization Grant standards https://tools.ietf.org/html/rfc8628
Device-Auth User flow
This section describes how device authorization flow can be achieved from a device.
API Spec: User Authentication#/Device%20Authorization
Endpoint specifications:
Endpoint | HTTP Method | Purpose |
---|---|---|
/api/auth/v1/device | POST | Creates unique user code and a verification URI to present it on a external browser to verify the user. |
/api/auth/v1/device/verify | POST | Verifies the user code with the authenticated user. |
/api/auth/v1/access_token grant_type="urn:ietf:params:oauth:grant-type:device_code" | POST | Provides access_token to the device once the user is verified externally. |
User flow:
1. User Turn on device (e.g: STB) first time
2. User is prompted with a random 8 characters code (aka user_code)
3. User is prompted to go to a website to login (authenticate)
4. Once successfully authenticated, user will be asked to enter the user_code from step 2
5. User enter user_code and submit it
6. If user_code is valid => the device (in #1) will be successfully login => user can start streaming services entitled to his/her account.
Requesting a user_code:
When the user wants to log in, the device starts out by making a POST request to begin the process. The POST request contains only one piece of information, its client_id
. This request is made to a /api/auth/v1/device endpoint that is unique to the Device Flow.
curl -X POST "https://testing.booxmedia.xyz/api/auth/v1/device" -H "accept: application/vnd.api+json" -H "Content-Type: application/json" -d "{\"client_id\":\"xyz123\"}"
The server will respond with a JSON document with a few pieces of information.
{ "device_code": "NGU4QWFiNjQ5YmQwNG3YTdmZMEyNzQ3YzQ1YSA", "verification_uri": "", "user_code": "ABCD1234", "expires_in": 1800, "interval": 5 }
Here’s what the properties in the response mean:
device_code
- This is a long string that the device will use to eventually exchange for an access token.verification_uri
- This is the URL the user needs to enter into their browser to start logging in.user_code
- This is the code the user will enter at the URL above, it will be of 8 characters, upper case A-Z and numeric.expires_in
- The number of seconds that this set of values is valid. After this amount of time, thedevice_code
anduser_code
will expire and the process will have to start over.interval
- The number of seconds the device should wait between polling to see if the user has finished authentication.
Now the device needs to display the URL and User Code to the user.
Client is free to add spaces or dashes to the user code (ex. ABCD-1234) in order to make it easier to read and enter into the secondary device, as long as all such characters are stripped from the input while requesting for the verification.
Verification:
User logs in with regular authentication mechanism to the service from mobile phone/browser, then enters the verification_uri provided by the device endpoint, a POST request is made to /api/auth/v1/device/verify endpoint to verify the user. See: User Authentication#/Device%20Authorization/verifyUserCode
verification_uri is a configurable property, so it is up to clients to provide the user with the URI to enter the user code. By default, server provides an empty string. This is to follow the device-flow standards.
curl -X POST "https://testing.booxmedia.xyz/api/auth/v1/device/verify" -H "accept: */*" -H "Content-Type: application/json" -d "{\"user_code\":\"ABCD-1234\",\"user_id\":\"1234\"}"
While the device waits for the user to enter the code and log in, it will make a POST request every 5 seconds as specified by the interval
returned. This POST request will be made to the /api/auth/v1/access_token endpoint, using a grant type of urn:ietf:params:oauth:grant-type:device_code
curl -X POST "https://testing.booxmedia.xyz/api/auth/v1/access_tokens" -H "accept: application/vnd.api+json" -H "Content-Type: application/json" -d "{\"grant_type\":\"urn:ietf:params:oauth:grant-type:device_code \",\"credentials\”:{\”client_id\”:\”1234xyz”, \”device_code\”:\”NGU4QWFiNjQ5YmQwNG3YTdmZMEyNzQ3YzQ1YSA ”},\”login_user_profile\":\"84eb61a9-75d4-42c7-8c15-84c3d7776227\”}”
While the user is busy logging in on their phone, the token endpoint will return the error message below:
{ "errors": [ { "id": "6de7da3e-8877-4f2b-a670-16e18e5d79a0", "status": "202", "code": "0", "title": "Authentication Pending", "detail": " - User is not yet authenticated, try again." } ] }
The Authentication Pending error means the user isn’t finished logging in, but the code hasn’t yet expired either. The device should try this again after the specified number of seconds, if the device polls against interval time provided, Too Many Requests error will be returned
{ "errors": [ { "id": "6de7da3e-8877-4f2b-a670-16e18e5d79a0", "status": "429", "code": "0", "title": "Too Many Requests", "detail": " - Slow Down" } ] }
Once the verification is successful, the api/auth/v1/access_tokens
endpoint will respond with access_token and refresh_token which can be used by the device for further use of services.
{ "data": { "type": "AccessToken", "id": "9bc6871e-ce26-4b8a-96f6-e6688599d938", "attributes": { "login_timestamp": 1538126476, "user_id": "1234", "user_profile_id": "84eb61a9-75d4-42c7-8c15-84c3d7776227", "user_flags": [ "TEST_FLAG1" ], "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJBbWlub01vdmUiLCJpYXQiOjE1Mzc1MjIxNzQsImV4cCI6MTUzODEyNjk3OSwiYXVkIjoid3d3LmJvb3h0di5maSIsInN1YiI6ImV4YW1wbGVfdXNlckBlbWFpbGRvbWFpbi5jb20iLCJwcm9maWxlX2lkIjoiNmM3Zjk0YWYtYjYwMy00ZjM5LTgzODQtZjEyMjI2ZWE5ZDRjNmM3Zjk0YWYtYjYwMy00ZjM5LTgzODQtZjEyMjI2ZWE5ZDRjIiwidHlwZSI6ImFjY2VzcyIsInN0Yl9zZXJpYWxfbm8iOiI2MTU1MDAyNjYxNjIifQ.RW6NdDE_kGgkOVKqodeT1fDdxU7Slaf551rp5ctBcFc", "expires_at_timestamp": 1538126476, "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJBbWlub01vdmUiLCJpYXQiOjE1Mzc1MjIxNzQsImV4cCI6MTU0NTk4OTM3OSwiYXVkIjoid3d3LmJvb3h0di5maSIsInN1YiI6ImV4YW1wbGVfdXNlckBlbWFpbGRvbWFpbi5jb20iLCJ0eXBlIjoicmVmcmVzaCJ9.9Pxzv0oGxjZmAIpZiH-aiue7LwAMcddPPHnaiOkpb-A" } } }
If api/auth/v1/access_tokens returns 404 Not Found, that means either the device_code has not been registered or the device code has been expired. In such cases, a new request has to be made using /api/auth/v1/device.