From 641f6965dd5cc23d1ffec69c035cd7db687ec5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Tue, 16 Dec 2025 12:48:44 +0100 Subject: [PATCH] Spec device management for application services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per MSC4190. Signed-off-by: Kévin Commaille --- content/application-service-api.md | 106 ++++++++++++++---- content/client-server-api/_index.md | 4 + data/api/client-server/cross_signing.yaml | 6 + data/api/client-server/device_management.yaml | 39 ++++++- data/api/client-server/registration.yaml | 12 ++ 5 files changed, 144 insertions(+), 23 deletions(-) diff --git a/content/application-service-api.md b/content/application-service-api.md index cc2c25ca..0ab518de 100644 --- a/content/application-service-api.md +++ b/content/application-service-api.md @@ -428,6 +428,8 @@ imports and similar behaviour). #### Server admin style permissions +{{% changed-in v="1.17" %}} + The homeserver needs to give the application service *full control* over its namespace, both for users and for room aliases. This means that the AS should be able to manage any users and room alias in its namespace. No additional API @@ -444,33 +446,57 @@ achieved by including the `as_token` on a `/register` request, along with a login type of `m.login.application_service` to set the desired user ID without a password. - POST /_matrix/client/v3/register - Authorization: Bearer YourApplicationServiceTokenHere +``` +POST /_matrix/client/v3/register +Authorization: Bearer YourApplicationServiceTokenHere - Content: - { - "type": "m.login.application_service", - "username": "_irc_example" - } +Content: +{ + "type": "m.login.application_service", + "username": "_irc_example" +} +``` -Similarly, logging in as users needs API changes in order to allow the AS to -log in without needing the user's password. This is achieved by including the -`as_token` on a `/login` request, along with a login type of -`m.login.application_service`: +{{% boxes/note %}} +{{% added-in v="1.17" %}} +Servers MUST still allow application services to use the `/register` endpoint +with a login type of `m.login.application_service` even if they don't support +the [Legacy Authentication API](/client-server-api/#legacy-api). + +In that case application services MUST set the `"inhibit_login": true` parameter +as they cannot use it to log in as users. If the `inhibit_login` parameter is +not set to `true`, the server MUST return a 400 HTTP status code with an +`M_APPSERVICE_LOGIN_UNSUPPORTED` error code. +{{% /boxes/note %}} + +Similarly, logging in as users using the [Legacy authentication API](/client-server-api/#legacy-api) +needs API changes in order to allow the AS to log in without needing the user's +password. This is achieved by including the `as_token` on a `/login` request, +along with a login type of `m.login.application_service`: {{% added-in v="1.2" %}} - POST /_matrix/client/v3/login - Authorization: Bearer YourApplicationServiceTokenHere +``` +POST /_matrix/client/v3/login +Authorization: Bearer YourApplicationServiceTokenHere - Content: - { - "type": "m.login.application_service", - "identifier": { - "type": "m.id.user", - "user": "_irc_example" - } - } +Content: +{ + "type": "m.login.application_service", + "identifier": { + "type": "m.id.user", + "user": "_irc_example" + } +} +``` + +{{% boxes/note %}} +{{% added-in v="1.17" %}} +Application services MUST NOT use the `/login` endpoint if the server doesn't +support the Legacy authentication API. If `/login` is called with the +`m.login.application_service` login type the server MUST return a 400 HTTP +status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code. +{{% /boxes/note %}} Application services which attempt to create users or aliases *outside* of their defined namespaces, or log in as users outside of their defined @@ -512,6 +538,44 @@ client-server endpoint. {{% http-api spec="client-server" api="appservice_room_directory" %}} +#### Device management + +{{% added-in v="1.17" %}} + +Application services need to be able to create and delete devices to manage the +encryption for their users without having to rely on `/login`, which also +generates an access token for the user, and which might not be available for +homeservers that only support the [OAuth 2.0 API](client-server-api/#oauth-20-api). + +##### Creating devices + +Application services can use the [`PUT /_matrix/client/v3/devices/{deviceId}`](/client-server-api/#put_matrixclientv3devicesdeviceid) +endpoint to create new devices. + +When a new device was created, the homeserver MUST return a 201 HTTP status code. +It MUST still return a 200 HTTP status code if a device was updated. + +This endpoint is rate-limited for device creation. Servers MAY want to use login +rate limits. + +##### Deleting devices + +The following endpoints used to delete devices MUST NOT require [User-Interactive +Authentication](/client-server-api/#user-interactive-authentication-api) when +used by an application service: + +* [`DELETE /_matrix/client/v3/devices/{deviceId}`](/client-server-api/#delete_matrixclientv3devicesdeviceid) +* [`POST /_matrix/client/v3/delete_devices`](/client-server-api/#post_matrixclientv3delete_devices) + +#### Cross-signing + +{{% added-in v="1.17" %}} + +Appservices need to be able to verify themselves and replace their cross-signing +keys, so the [`POST /_matrix/client/v3/keys/device_signing/upload`](/client-server-api/#post_matrixclientv3keysdevice_signingupload) +endpoint MUST NOT require [User-Interactive Authentication](/client-server-api/#user-interactive-authentication-api) +when used by an application service, even if cross-signing keys already exist. + ### Referencing messages from a third-party network Application services should include an `external_url` in the `content` diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 2460776f..c9b2676a 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1562,6 +1562,10 @@ If the access token does correspond to an appservice, but the user id does not lie within its namespace then the homeserver will respond with an errcode of `M_EXCLUSIVE`. +{{% added-in v="1.17" %}} If this login type is used and the server doesn't +support logging in via the Legacy authentication API, it MUST return a 400 HTTP +status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code. + ##### Login Fallback If a client does not recognize any or all login flows it can use the diff --git a/data/api/client-server/cross_signing.yaml b/data/api/client-server/cross_signing.yaml index ec490b2a..64742250 100644 --- a/data/api/client-server/cross_signing.yaml +++ b/data/api/client-server/cross_signing.yaml @@ -46,6 +46,12 @@ paths: authentication type if the access token was obtained via the [OAuth 2.0 API](/client-server-api/#oauth-20-api). {{% /boxes/note %}} + + {{% boxes/note %}} + {{% added-in v="1.17" %}} + When this endpoint is used by an application service, the server MUST NOT require + User-Interactive Authentication, even if cross-signing keys already exist. + {{% /boxes/note %}} operationId: uploadCrossSigningKeys security: - accessTokenQuery: [] diff --git a/data/api/client-server/device_management.yaml b/data/api/client-server/device_management.yaml index 1b245e78..338dc027 100644 --- a/data/api/client-server/device_management.yaml +++ b/data/api/client-server/device_management.yaml @@ -87,8 +87,21 @@ paths: tags: - Device management put: - summary: Update a device - description: Updates the metadata on the given device. + summary: Create or update a device + description: |- + Updates the metadata on the given device. + + {{% boxes/note %}} + {{% added-in v="1.17" %}} + This endpoint can be used by application services to create a device. + + When a new device was created, the homeserver MUST return a 201 HTTP + status code. It MUST still return a 200 HTTP status code if a device was + updated. + + This endpoint is rate-limited for device creation. Servers MAY want to + use login rate limits. + {{% /boxes/note %}} operationId: updateDevice security: - accessTokenQuery: [] @@ -127,6 +140,16 @@ paths: examples: response: value: {} + "201": + description: |- + The device was successfully created by the application service. + content: + application/json: + schema: + type: object + examples: + response: + value: {} "404": description: The current user has no device with the given ID. tags: @@ -142,6 +165,12 @@ paths: Since this endpoint uses User-Interactive Authentication, it cannot be used when the access token was obtained via the [OAuth 2.0 API](/client-server-api/#oauth-20-api). {{% /boxes/warning %}} + + {{% boxes/note %}} + {{% added-in v="1.17" %}} + When this endpoint is used by an application service, the server MUST NOT + require User-Interactive Authentication. + {{% /boxes/note %}} operationId: deleteDevice security: - accessTokenQuery: [] @@ -199,6 +228,12 @@ paths: Since this endpoint uses User-Interactive Authentication, it cannot be used when the access token was obtained via the [OAuth 2.0 API](/client-server-api/#oauth-20-api). {{% /boxes/warning %}} + + {{% boxes/note %}} + {{% added-in v="1.17" %}} + When this endpoint is used by an application service, the server MUST NOT + require User-Interactive Authentication. + {{% /boxes/note %}} operationId: deleteDevices security: - accessTokenQuery: [] diff --git a/data/api/client-server/registration.yaml b/data/api/client-server/registration.yaml index e7ede561..4872ca64 100644 --- a/data/api/client-server/registration.yaml +++ b/data/api/client-server/registration.yaml @@ -60,6 +60,18 @@ paths: Any user ID returned by this API must conform to the grammar given in the [Matrix specification](/appendices/#user-identifiers). + + {{% boxes/note %}} + {{% added-in v="1.17" %}} + Even if the server doesn't support the Legacy authentication API, it + MUST support this endpoint for application services to be able to + [create users](/application-service-api/#server-admin-style-permissions). + + In that case application services MUST set the `"inhibit_login": true` + parameter as they cannot use it to log in as users. If the + `inhibit_login` parameter is not set to `true`, the server MUST return a + 400 HTTP status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code. + {{% /boxes/note %}} operationId: register parameters: - in: query