|
| 1 | +--- |
| 2 | +id: device-authorization |
| 3 | +title: Device Authorization |
| 4 | +sidebar_label: Device authorization flow |
| 5 | +--- |
| 6 | + |
| 7 | +The OAuth 2.0 Device Authorization Grant (RFC 8628) brings OAuth to devices with internet connectivity but limited input |
| 8 | +capabilities. This flow is designed for smart TVs, streaming devices, IoT hardware, printers, remote terminal sessions, AI agents, |
| 9 | +and other connected devices where typing credentials or opening a browser isn't practical or possible. Here's how it works: the |
| 10 | +device to be authenticated displays a URL and a short code, prompting you to open that URL on your phone or computer to authorize |
| 11 | +access. After successful authorization, the device will get an access and (optionally) a refresh token. The two devices don't need |
| 12 | +to communicate directly; the authorization happens through the OAuth provider. |
| 13 | + |
| 14 | +This document provides an overview of the Ory's device authorization grant flow, with a step-by-step example of its |
| 15 | +implementation, configuration options, and guidance on creating custom user interfaces for the verification screen. |
| 16 | + |
| 17 | +## Overview of the flow |
| 18 | + |
| 19 | +Here is the high-level overview for the device authorization grant flow: |
| 20 | + |
| 21 | +1. The user attempts to log in to the device. This initiates the device to request authorization from the authorization server. |
| 22 | +1. When the authorization server responds, the user is instructed to visit a URL and enter the provided user code, which they do |
| 23 | + on a different device. |
| 24 | +1. On the different device the user visits the URL, enters the user code, logs in, and grants access to the device. |
| 25 | +1. In the meantime, the device polls the authorization server. Once the user authenticates and grants access, the authentication |
| 26 | + server sends an access token to the device, which is used to access the protected resource. |
| 27 | + |
| 28 | +```mdx-code-block |
| 29 | +import Mermaid from "@site/src/theme/Mermaid"; |
| 30 | +
|
| 31 | +<Mermaid |
| 32 | + chart={`sequenceDiagram |
| 33 | + participant D as Device |
| 34 | + participant U as User |
| 35 | + participant AS as OAuth2 Server |
| 36 | +
|
| 37 | + activate D |
| 38 | + D->>+AS: Start device code grant |
| 39 | + AS-->>-D: Verification URI, Device-code, User-code |
| 40 | + loop background poll before user authorized |
| 41 | + D->>+AS: Request Token by device code |
| 42 | + AS-->>-D: Error |
| 43 | + end |
| 44 | + D->>+U: Ask visit verification URI<br/>Reveal User-code |
| 45 | + U->>+AS: Request verification URI |
| 46 | + AS-->>-U: Prompt for User-code |
| 47 | + U->>+AS: Sumbit User-code |
| 48 | + AS-->>-U: Prompt for login and consent |
| 49 | + U->>+AS: Submit credentials and consent |
| 50 | + AS-->>-U: Show success |
| 51 | + deactivate U |
| 52 | + loop background poll after user authorized |
| 53 | + D->>+AS: Request Token by device code |
| 54 | + end |
| 55 | + AS-->>-D: Token |
| 56 | + D-->>D: Process and store token |
| 57 | + deactivate D |
| 58 | +`} /> |
| 59 | +``` |
| 60 | + |
| 61 | +### Step 1: Device requests authorization |
| 62 | + |
| 63 | +The user attempts to log in through the limited input device. The device sends a POST request to the authorization server to |
| 64 | +initiate the flow with the following parameters: |
| 65 | + |
| 66 | +- `client_id`: The ID of the client (device) that's making the request |
| 67 | +- `scope` (optional): The scope of the access request, which specifies which resources the requesting device can access |
| 68 | + |
| 69 | +The authorization server responds with the following information: |
| 70 | + |
| 71 | +- `device_code`: A unique code to identify the authorization request |
| 72 | +- `user_code`: A code the user enters at the verification URL |
| 73 | +- `verification_uri`: The URL where the user authorizes the device |
| 74 | +- `verification_uri_complete`: The URL where the user authorizes the device, with the user_code already filled in |
| 75 | +- `expires_in`: The lifespan of the device code (in seconds) |
| 76 | +- `interval`: The polling interval (in seconds) for the client to check if the user has authorized the device yet |
| 77 | + |
| 78 | +### Step 2: Display user code and verification URI |
| 79 | + |
| 80 | +The device shows the user the `user_code` and `verification_uri` it received from the authorization server. Depending on the |
| 81 | +device, this can be in the form of a URL, QR code, acoustically, or any other form that the device can communicate with the user. |
| 82 | + |
| 83 | +### Step 3: User grants permission |
| 84 | + |
| 85 | +The user visits the provided URI on a separate device, such as a phone, and enters the code. Once the user enters the code, the |
| 86 | +user is prompted to log in, if not already authenticated, and grants or denies permission to the client (device). After granting |
| 87 | +permission, the user is redirected to a page confirming they are successfully logged in. |
| 88 | + |
| 89 | +### Step 4: Device polls for the access token |
| 90 | + |
| 91 | +While the user is authorizing the device, the device polls the `token` endpoint of the authorization server to check whether the |
| 92 | +user has completed the authorization process by making a POST request with the following parameters: |
| 93 | + |
| 94 | +- `client_id`: The ID of the client that's making the request |
| 95 | +- `device_code`: The device code returned from the authorization request |
| 96 | +- `grant_type`: This must always be `urn:ietf:params:oauth:grant-type:device_code` |
| 97 | + |
| 98 | +After the user grants their consent, the authentication server sends an access token to the device, which is used to access the |
| 99 | +protected resource. |
| 100 | + |
| 101 | +## Configuration options |
| 102 | + |
| 103 | +### Configure the user interface |
| 104 | + |
| 105 | +To enable and configure the device authorization grant in Ory Hydra, adjust the following settings in your configuration file: |
| 106 | + |
| 107 | +```yaml |
| 108 | +urls: |
| 109 | + device: |
| 110 | + # The verification UI is where the user inputs the user-code |
| 111 | + verification: http://path/to/device/verification/ui |
| 112 | + # The success UI is where the user is sent to after successful authorization |
| 113 | + success: http://path/to/device/success |
| 114 | +``` |
| 115 | +
|
| 116 | +### Configure user code entropy |
| 117 | +
|
| 118 | +Depending on your security needs and your traffic load, you should choose the appropriate `user_code` entropy. The |
| 119 | +`oauth2.device_authorization.user_code.entropy_preset` configuration supports 3 values: |
| 120 | + |
| 121 | +- `high`: `user_code` is 8 characters long and consists of alphanumeric characters, excluding some ambiguous symbols |
| 122 | +- `medium`: `user_code` is 8 characters long and consists of only upper case alphabetic characters |
| 123 | +- `low`: `user_code` is 9 characters long and consists of only numeric characters |
| 124 | + |
| 125 | +It is also possible to configure the length and character set directly: |
| 126 | + |
| 127 | +```yaml |
| 128 | +oauth2: |
| 129 | + device_authorization: |
| 130 | + user_code: |
| 131 | + length: 8 |
| 132 | + character_set: abcdefghijklmnopqrstuvwxyz0123456789 |
| 133 | +``` |
| 134 | + |
| 135 | +It is important to strike the right balance between security and user experience here. Higher entropy enhances security and |
| 136 | +protects against an attacker randomly guessing valid user-codes. This is especially important when more concurrent device flows |
| 137 | +are being performed. As users will need to manually enter the user code, the higher the entropy, the more difficult it will be for |
| 138 | +the user to enter the user code. For a better user experience, ambiguous characters should be avoided, for example `O` and `0` on |
| 139 | +any display, or `1` and `7` on a 7-segment display. This isn't of any concern when the user doesn't need to input the user-code |
| 140 | +manually, for example when scanning a QR code. |
| 141 | + |
| 142 | +## Device verification UI implementation |
| 143 | + |
| 144 | +Here is a sample UI implementation for device verification: |
| 145 | + |
| 146 | +```js |
| 147 | +import { Configuration, OAuth2Api } from "@ory/client" |
| 148 | +import { Request, Response } from "express" |
| 149 | +
|
| 150 | +const ory = new OAuth2Api( |
| 151 | + new Configuration({ |
| 152 | + basePath: `https://${process.env.ORY_PROJECT_SLUG}.projects.oryapis.com`, |
| 153 | + accessToken: process.env.ORY_API_KEY, |
| 154 | + }), |
| 155 | +) |
| 156 | + |
| 157 | +// Please note that this is an example implementation. |
| 158 | +// In a production app, please add proper error handling. |
| 159 | +export async function handleLogin(request: Request, response: Response) { |
| 160 | + const challenge = request.query.device_challenge.toString() |
| 161 | + const userCode = request.query.user_code.toString() |
| 162 | + |
| 163 | + // Show the login form if the form was not submitted. |
| 164 | + if (request.method === "GET") { |
| 165 | + response.render("device", { |
| 166 | + challenge, |
| 167 | + userCode, |
| 168 | + }) |
| 169 | + return |
| 170 | + } |
| 171 | + |
| 172 | + // User was authenticated successfully, |
| 173 | + return await ory |
| 174 | + .acceptUserCodeRequest({ |
| 175 | + deviceChallenge: challenge, |
| 176 | + acceptDeviceUserCodeRequest: { |
| 177 | + user_code: userCode, |
| 178 | + }, |
| 179 | + }) |
| 180 | + .then(({ redirect_to }) => { |
| 181 | + response.redirect(String(redirect_to)) |
| 182 | + }) |
| 183 | +} |
| 184 | +``` |
0 commit comments