From 43d7518c3a313da2e08d29fd8cf733d8073d0fbb Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 23 Feb 2026 17:23:08 +0000 Subject: [PATCH 1/4] MSC4341 --- .../client_server/newsfragments/2320.feature | 1 + content/client-server-api/_index.md | 180 +++++++++++++++++- .../client-server/oauth_server_metadata.yaml | 19 +- 3 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 changelogs/client_server/newsfragments/2320.feature diff --git a/changelogs/client_server/newsfragments/2320.feature b/changelogs/client_server/newsfragments/2320.feature new file mode 100644 index 00000000..29b14c47 --- /dev/null +++ b/changelogs/client_server/newsfragments/2320.feature @@ -0,0 +1 @@ +Add the OAuth 2.0 Device Authorization Grant (RFC 8628) as a supported grant type, as per [MSC4341](https://github.com/matrix-org/matrix-spec-proposals/pull/4341). diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 5f74ad40..a5d0cba0 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1720,14 +1720,25 @@ authentication type. 1. [Discover the OAuth 2.0 server metadata](#server-metadata-discovery). 2. [Register the client with the homeserver](#client-registration). -3. [Obtain an access token](#login-flow) by authorizing a [scope](#scope) for the client with the [authorization code grant](#authorization-code-grant). +3. Obtain an access token by authorizing a [scope](#scope) for the client with + one of the supported grant types: + - Use the [authorization code grant](#authorization-code-grant) via the + [login flow](#login-flow) for clients with access to a web browser. + - {{% added-in v="1.18" %}} Use the [device authorization grant](#device-authorization-grant) via the + [device authorization flow](#device-authorization-flow) for clients on + devices with limited input capabilities (e.g. CLI applications or TVs) + where the user completes authorization on a separate device. 4. [Refresh the access token](#token-refresh-flow) with the [refresh token grant](#refresh-token-grant) when it expires. 5. [Revoke the tokens](#token-revocation) when the users wants to log out of the client. #### Login flow Logging in with the OAuth 2.0 API should be done with the [authorization code -grant](#authorization-code-grant). In the context of the Matrix specification, +grant](#authorization-code-grant) for clients that have access to a web browser. +For clients on devices with limited input capabilities, the +[device authorization flow](#device-authorization-flow) can be used instead. + +In the context of the Matrix specification, this means requesting a [scope](#scope) including full client-server API read/write access and allocating a device ID. @@ -1873,6 +1884,141 @@ Sample response: Finally, the client can call the [`/whoami`](#get_matrixclientv3accountwhoami) endpoint to get the user ID that owns the access token. +#### Device authorization flow + +{{% added-in v="1.18" %}} + +The device authorization flow allows clients on devices with limited input +capabilities (such as CLI applications or TVs) to obtain an +access token by having the user complete authorization on a separate device +with a web browser. This flow uses the [device authorization +grant](#device-authorization-grant). + +In the context of the Matrix specification, this means requesting a +[scope](#scope) including full client-server API read/write access and +allocating a device ID, as with the [login flow](#login-flow). + +Once the client has retrieved the [server metadata](#server-metadata-discovery), +it needs to generate the following value: + +- `device_id`: a unique identifier for this device; see the + [`urn:matrix:client:device:`](#device-id-allocation) scope token. + +**Device authorization request** + +The client sends a `application/x-www-form-urlencoded` encoded `POST` request to +the `device_authorization_endpoint` as defined in +[RFC 8628 section 3.1](https://datatracker.ietf.org/doc/html/rfc8628#section-3.1): + +| Parameter | Value | +|-------------|----------------------------------------------------------------| +| `client_id` | The client ID returned from client registration. | +| `scope` | `urn:matrix:client:api:* urn:matrix:client:device:` with the `device_id` generated previously. | + +Sample device authorization request: + +```http +POST /oauth2/device HTTP/1.1 +Host: account.example.com +Content-Type: application/x-www-form-urlencoded + +client_id=s6BhdRkqt3&scope=urn%3Amatrix%3Aclient%3Aapi%3A%2A%20urn%3Amatrix%3Aclient%3Adevice%3AAABBBCCCDDD +``` + +**Device authorization response** + +The server responds with a JSON object as defined in +[RFC 8628 section 3.2](https://datatracker.ietf.org/doc/html/rfc8628#section-3.2), +containing: + +| Parameter | | +|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| `device_code` | The device verification code. | +| `user_code` | An end-user verification code. | +| `verification_uri` | The end-user verification URI on the authorization server. | +| `verification_uri_complete` | Optionally, the URI including the `user_code`, so the user does not need to type it in manually. | +| `expires_in` | The lifetime in seconds of the `device_code` and `user_code`. | +| `interval` | The minimum number of seconds the client should wait between polling requests to the token endpoint. If omitted, clients should default to 5. | + +It is RECOMMENDED that the server provides a `verification_uri_complete` such +that the user does not need to type in the `user_code`. + +Sample response: + +```json +{ + "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", + "user_code": "WDJB-MJHT", + "verification_uri": "https://account.example.com/link", + "verification_uri_complete": "https://account.example.com/link?user_code=WDJB-MJHT", + "expires_in": 1800, + "interval": 5 +} +``` + +**User interaction** + +The client conveys the `verification_uri_complete` (and/or `verification_uri` +and `user_code`) to the user. How the client does this depends on the +specific device characteristics and use case. For example: + +- A CLI application could display the `verification_uri` and `user_code` as text + for the user to type into their browser on another device. +- A TV application could encode the `verification_uri_complete` (with fallback + to `verification_uri`) as a QR code for the user to scan with their phone. + +The user opens the verification URI in a web browser on another device and +completes authentication and authorization. + +**Token polling** + +While the user is completing authorization, the client polls the +`token_endpoint` for the outcome, at intervals no shorter than the `interval` +value from the device authorization response. + +The poll request is a `POST` to the `token_endpoint` with the following +parameters, encoded as `application/x-www-form-urlencoded` in the body: + +| Parameter | Value | +|---------------|----------------------------------------------------------------| +| `grant_type` | `urn:ietf:params:oauth:grant-type:device_code` | +| `device_code` | The `device_code` from the device authorization response. | +| `client_id` | The client ID returned from client registration. | + +Sample token polling request: + +```http +POST /oauth2/token HTTP/1.1 +Host: account.example.com +Content-Type: application/x-www-form-urlencoded + +grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS&client_id=s6BhdRkqt3 +``` + +The server responds as defined in [RFC 8628 section +3.5](https://datatracker.ietf.org/doc/html/rfc8628#section-3.5): + +- While authorization is pending, the server returns an `authorization_pending` + error (or `slow_down` if the client is polling too frequently). +- If authorization is denied, the server returns an `access_denied` error. +- If the device code expires, the server returns an `expired_token` error. +- On successful authorization, the server returns a JSON object containing the + access token, token type, expiration time, refresh token, and scope: + +```json +{ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "Bearer", + "expires_in": 299, + "refresh_token": "tGz3JOkF0XG5Qx2TlKWIA", + "scope": "urn:matrix:client:api:* urn:matrix:client:device:AAABBBCCCDDD" +} +``` + +Finally, the client can call the [`/whoami`](#get_matrixclientv3accountwhoami) +endpoint to get the user ID that owns the access token. Token refreshing and +revocation proceed as with the [login flow](#login-flow). + #### Token refresh flow Refreshing a token with the OAuth 2.0 API should be done with the [refresh token @@ -2203,6 +2349,7 @@ The client must also have obtained a `client_id` by [registering with the server This specification supports the following grant types: - [Authorization code grant](#authorization-code-grant) +- {{% added-in v="1.18" %}} [Device authorization grant](#device-authorization-grant) - [Refresh token grant](#refresh-token-grant) ##### Authorization code grant @@ -2240,6 +2387,35 @@ Whether the homeserver supports this parameter is advertised by the Servers that support this parameter SHOULD show the account registration UI in the browser. +##### Device authorization grant + +{{% added-in v="1.18" %}} + +As per [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628), the device +authorization grant lets clients on devices with limited input capabilities +obtain an access token by having the user complete authorization on a separate +device with a web browser. + +This grant requires the client to know the following [authorization server +metadata](#server-metadata-discovery): + +- `device_authorization_endpoint` + +To use this grant, homeservers and clients MUST: + +- Support the device authorization grant as per + [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628). +- Support the [refresh token grant](#refresh-token-grant). + +As with the [authorization code grant](#authorization-code-grant), when +authorization is granted to a client, the homeserver MUST issue a refresh token +to the client in addition to the access token. The access token and refresh +token have the same lifetime constraints as described in the [refresh token +grant](#refresh-token-grant) section. + +The full flow for using this grant is described in the +[device authorization flow](#device-authorization-flow). + ##### Refresh token grant As per [RFC 6749 section 6](https://datatracker.ietf.org/doc/html/rfc6749#section-6), diff --git a/data/api/client-server/oauth_server_metadata.yaml b/data/api/client-server/oauth_server_metadata.yaml index 719a1e16..798beb6f 100644 --- a/data/api/client-server/oauth_server_metadata.yaml +++ b/data/api/client-server/oauth_server_metadata.yaml @@ -67,8 +67,8 @@ paths: type: string format: uri description: |- - URL of the token endpoint, necessary to use the authorization code grant and - the refresh token grant. + URL of the token endpoint, necessary to use the authorization code grant, + device authorization grant and the refresh token grant. revocation_endpoint: type: string format: uri @@ -101,6 +101,10 @@ paths: This array MUST contain at least the `authorization_code` and `refresh_token` values, for clients to be able to use the authorization code grant and refresh token grant, respectively. + + {{% added-in v="1.18" %}} It MAY also contain + `urn:ietf:params:oauth:grant-type:device_code` to indicate support for the + [device authorization grant](/client-server-api/#device-authorization-grant). items: type: string description: A grant type that the server supports. @@ -165,6 +169,14 @@ paths: - "org.matrix.account_deactivate" - "org.matrix.cross_signing_reset" description: An action that the account management URL supports. + device_authorization_endpoint: + x-addedInMatrixVersion: "1.18" + type: string + format: uri + description: |- + URL of the device authorization endpoint, as defined in + [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628), necessary to use + the [device authorization grant](/client-server-api/#device-authorization-grant). required: - issuer - authorization_endpoint @@ -180,9 +192,10 @@ paths: "authorization_endpoint": "https://account.example.com/oauth2/auth", "token_endpoint": "https://account.example.com/oauth2/token", "registration_endpoint": "https://account.example.com/oauth2/clients/register", + "device_authorization_endpoint": "https://account.example.com/oauth2/device", "revocation_endpoint": "https://account.example.com/oauth2/revoke", "response_types_supported": ["code"], - "grant_types_supported": ["authorization_code", "refresh_token"], + "grant_types_supported": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"], "response_modes_supported": ["query", "fragment"], "code_challenge_methods_supported": ["S256"], "account_management_uri": "https://account.example.com/manage", From 9c94ec9f4a2b28b980e7549430c6768f9d62c0e3 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 24 Feb 2026 09:15:07 +0000 Subject: [PATCH 2/4] Don't use TV as an example --- content/client-server-api/_index.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index a5d0cba0..e00c8a43 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1726,7 +1726,7 @@ authentication type. [login flow](#login-flow) for clients with access to a web browser. - {{% added-in v="1.18" %}} Use the [device authorization grant](#device-authorization-grant) via the [device authorization flow](#device-authorization-flow) for clients on - devices with limited input capabilities (e.g. CLI applications or TVs) + devices with limited input capabilities (e.g. CLI applications or embedded devices) where the user completes authorization on a separate device. 4. [Refresh the access token](#token-refresh-flow) with the [refresh token grant](#refresh-token-grant) when it expires. 5. [Revoke the tokens](#token-revocation) when the users wants to log out of the client. @@ -1889,7 +1889,7 @@ endpoint to get the user ID that owns the access token. {{% added-in v="1.18" %}} The device authorization flow allows clients on devices with limited input -capabilities (such as CLI applications or TVs) to obtain an +capabilities (such as CLI applications or embedded devices) to obtain an access token by having the user complete authorization on a separate device with a web browser. This flow uses the [device authorization grant](#device-authorization-grant). @@ -1964,8 +1964,9 @@ specific device characteristics and use case. For example: - A CLI application could display the `verification_uri` and `user_code` as text for the user to type into their browser on another device. -- A TV application could encode the `verification_uri_complete` (with fallback - to `verification_uri`) as a QR code for the user to scan with their phone. +- An embedded device with a screen could encode the `verification_uri_complete` + (with fallback to `verification_uri`) as a QR code for the user to scan with + their phone. The user opens the verification URI in a web browser on another device and completes authentication and authorization. From 501a970202f5c0f79baad99cd1676b194640f142 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Fri, 27 Feb 2026 10:24:21 +0000 Subject: [PATCH 3/4] verification_uri_complete clarification Co-authored-by: Quentin Gliech --- content/client-server-api/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index e00c8a43..a4bfb750 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1936,7 +1936,7 @@ containing: | `device_code` | The device verification code. | | `user_code` | An end-user verification code. | | `verification_uri` | The end-user verification URI on the authorization server. | -| `verification_uri_complete` | Optionally, the URI including the `user_code`, so the user does not need to type it in manually. | +| `verification_uri_complete` | Optionally, the URI which doesn't require the user to manually type the `user_code`, designed for non-textual transmission. | | `expires_in` | The lifetime in seconds of the `device_code` and `user_code`. | | `interval` | The minimum number of seconds the client should wait between polling requests to the token endpoint. If omitted, clients should default to 5. | From 68732e76cf52201be16edbee8d8576443f92153b Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Fri, 27 Feb 2026 10:46:41 +0000 Subject: [PATCH 4/4] Add context about use on desktop applications --- content/client-server-api/_index.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index e00c8a43..e98734fc 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1888,11 +1888,14 @@ endpoint to get the user ID that owns the access token. {{% added-in v="1.18" %}} -The device authorization flow allows clients on devices with limited input -capabilities (such as CLI applications or embedded devices) to obtain an -access token by having the user complete authorization on a separate device -with a web browser. This flow uses the [device authorization -grant](#device-authorization-grant). +The device authorization flow allows clients to obtain an access token without +needing to directly interact with a web browser. Instead, the user completes +authorization on a web browser that can be on a separate device. This is useful +for devices with limited input capabilities (such as CLI applications or +embedded devices) or where the redirect handling may be unreliable (such as a +desktop applications). + +This flow uses the [device authorization grant](#device-authorization-grant). In the context of the Matrix specification, this means requesting a [scope](#scope) including full client-server API read/write access and @@ -1967,9 +1970,12 @@ specific device characteristics and use case. For example: - An embedded device with a screen could encode the `verification_uri_complete` (with fallback to `verification_uri`) as a QR code for the user to scan with their phone. +- A desktop application running on a platform that does not support callbacks + could launch the `verification_uri_complete` (with fallback to + `verification_uri`) in the system browser. -The user opens the verification URI in a web browser on another device and -completes authentication and authorization. +The user opens the verification URI in a web browser, which may be on another +device, and completes authentication and authorization. **Token polling**