From 1f8158715423b25ce3c8fb856e910c7afdac9aac Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 10 May 2022 15:09:27 -0400 Subject: [PATCH 01/17] add spec for refresh tokens --- content/client-server-api/_index.md | 99 ++++++++++++++++----- data/api/client-server/login.yaml | 25 ++++++ data/api/client-server/refresh.yaml | 107 +++++++++++++++++++++++ data/api/client-server/registration.yaml | 27 ++++++ 4 files changed, 235 insertions(+), 23 deletions(-) create mode 100644 data/api/client-server/refresh.yaml diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 3edb5b16..52398c9c 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -71,7 +71,7 @@ These error codes can be returned by any API endpoint: Forbidden access, e.g. joining a room without permission, failed login. `M_UNKNOWN_TOKEN` -The access token specified was not recognised. +The access or refresh token specified was not recognised. An additional response parameter, `soft_logout`, might be present on the response for 401 HTTP status codes. See [the soft logout @@ -314,7 +314,8 @@ Most API endpoints require the user to identify themselves by presenting previously obtained credentials in the form of an `access_token` query parameter or through an Authorization Header of `Bearer $access_token`. An access token is typically obtained via the [Login](#login) or -[Registration](#account-registration-and-management) processes. +[Registration](#account-registration-and-management) processes. Access tokens +can expire; a new access token can be generated by using a refresh token. {{% boxes/note %}} This specification does not mandate a particular format for the access @@ -336,42 +337,92 @@ to prevent the access token being leaked in access/HTTP logs. The query string should only be used in cases where the `Authorization` header is inaccessible for the client. -When credentials are required but missing or invalid, the HTTP call will -return with a status of 401 and the error code, `M_MISSING_TOKEN` or -`M_UNKNOWN_TOKEN` respectively. +{{% changed-in v="1.3" %}} When credentials are required but missing or +invalid, the HTTP call will return with a status of 401 and the error code, +`M_MISSING_TOKEN` or `M_UNKNOWN_TOKEN` respectively. Note that an error code +of `M_UNKNOWN_TOKEN` could mean one of four things: + +1. the access token was never valid. +2. the access token has been logged out. +3. the access token has been [soft logged out](#soft-logout). +4. the access token [needs to be refreshed](#refreshing-access-tokens). + +When a client receives an error code of `M_UNKNOWN_TOKEN`, it should: + +- attempt to [refresh the token](#refreshing-access-tokens), if it has a refresh + token; +- if [`soft_logout`](#soft-logout) is set to `true`, it can offer to + re-log in the user, retaining any of the client's persisted + information; +- otherwise, consider the user as having been logged out. ### Relationship between access tokens and devices Client [devices](../index.html#devices) are closely related to access -tokens. Matrix servers should record which device each access token is -assigned to, so that subsequent requests can be handled correctly. +tokens and refresh tokens. Matrix servers should record which device +each access token and refresh token are assigned to, so that +subsequent requests can be handled correctly. When a refresh token is +used to generate a new access token and refresh token, the new access +and refresh tokens are now bound to the device associated with the +initial refresh token. By default, the [Login](#login) and [Registration](#account-registration-and-management) processes auto-generate a new `device_id`. A client is also free to generate its own `device_id` or, provided the user remains the same, reuse a device: in either case the client should pass the `device_id` in the request body. If the client sets the `device_id`, the server will -invalidate any access token previously assigned to that device. There is -therefore at most one active access token assigned to each device at any -one time. +invalidate any access and refresh tokens previously assigned to that device. + +### Refreshing access tokens + +{{% added-in v="1.3" %}} + +Access tokens can expire after a certain amount of time. Any HTTP +calls that use an expired access token will return with an error code +`M_UNKNOWN_TOKEN`, preferably with `soft_logout: true`. When a client +receives this error and it has a refresh token, it should attempt to +refresh the access token by calling `/refresh`. Clients can also +refresh their access token at any time, even if it has not yet +expired. If the token refresh succeeds, it should use the new token +for future requests, and can re-try previously-failed requests with +the new token. When an access token is refreshed, a new refresh token +may be returned; if a new refresh token is given, the old refresh +token will be invalidated, and the new refresh token should be used +when the access token needs to be refreshed. + +If the token refresh fails, then the client can treat it as a [soft +logout](#soft-logout), if the error response included a `soft_logout: +true` property, and attempt to obtain a new access token by re-logging +in. If the error response does not include a `soft_logout: true` +property, the client should consider the user as being logged out. + +Handling of clients that do not support refresh tokens is up to the +homeserver; clients indicate their support for refresh tokens by +including a `refresh_token: true` property in the request body of the +`/login` and `/register` endpoints. For example, homeservers may allow +the use of non-expiring access tokens, or may expire access tokens +anyways and rely on soft logout behaviour on clients that don't +support refreshing. ### Soft logout -When a request fails due to a 401 status code per above, the server can -include an extra response parameter, `soft_logout`, to indicate if the -client's persisted information can be retained. This defaults to -`false`, indicating that the server has destroyed the session. Any -persisted state held by the client, such as encryption keys and device -information, must not be reused and must be discarded. +A client can be in a "soft logout" state if the server requires +re-authentication before continuing, but does not want to invalidate +the client's session. That is, any persisted state held by the client, +such as encryption keys and device information, must not be reused and +must be discarded, and can be re-used if the client successfully +re-authenticates. -When `soft_logout` is true, the client can acquire a new access token by -specifying the device ID it is already using to the login API. In most -cases a `soft_logout: true` response indicates that the user's session -has expired on the server-side and the user simply needs to provide -their credentials again. +The server indicates that the client is in a soft logout state by +including a `soft_logout: true` parameter in an `M_UNKNOWN_TOKEN` +error response; the `soft_logout` parameter defaults to `false`. -In either case, the client's previously known access token will no -longer function. +A client that receives such a response can try to [refresh its access +token](#refreshing-access-tokens), if it has a refresh token +available. If it does not have a refresh token available, or +refreshing fails with `soft_logout: true`, the client can acquire a +new access token by specifying the device ID it is already using to +the login API. ### User-Interactive Authentication API @@ -1105,6 +1156,8 @@ errcode of `M_EXCLUSIVE`. {{% http-api spec="client-server" api="login" %}} +{{% http-api spec="client-server" api="refresh" %}} + {{% http-api spec="client-server" api="logout" %}} #### Login Fallback diff --git a/data/api/client-server/login.yaml b/data/api/client-server/login.yaml index 8865665f..3eb1e0d2 100644 --- a/data/api/client-server/login.yaml +++ b/data/api/client-server/login.yaml @@ -1,5 +1,6 @@ # Copyright 2016 OpenMarket Ltd # Copyright 2018 New Vector Ltd +# Copyright 2022 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -133,6 +134,11 @@ paths: description: |- A display name to assign to the newly-created device. Ignored if `device_id` corresponds to a known device. + refresh_token: + type: boolean + description: |- + If true, the client supports refresh tokens. + x-addedInMatrixVersion: "1.3" required: ["type"] responses: @@ -142,6 +148,8 @@ paths: application/json: { "user_id": "@cheeky_monkey:matrix.org", "access_token": "abc123", + "refresh_token": "def456", + "expires_in_ms": 60000, "device_id": "GHTYAJCE", "well_known": { "m.homeserver": { @@ -163,6 +171,23 @@ paths: description: |- An access token for the account. This access token can then be used to authorize other requests. + refresh_token: + type: string + description: |- + A refresh token for the account. This token can be used to + obtain a new access token when it expires by calling the + `/refresh` endpoint. + x-addedInMatrixVersion: "1.3" + expires_in_ms: + type: integer + description: |- + The lifetime of the access token, in milliseconds. Once + the access token has expired a new access token can be + obtained by using the provided refresh token. If no + refresh token is provided, the client will need re-log in + to obtain a new access token. If not given, the client can + assume that the access token will not expire. + x-addedInMatrixVersion: "1.3" home_server: type: string description: |- diff --git a/data/api/client-server/refresh.yaml b/data/api/client-server/refresh.yaml new file mode 100644 index 00000000..abe8c8fb --- /dev/null +++ b/data/api/client-server/refresh.yaml @@ -0,0 +1,107 @@ +# Copyright 2022 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Client-Server Registration and Login API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/v3 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/refresh": + post: + x-addedInMatrixVersion: "1.3" + summary: Refresh an access token + description: |- + Refresh an access token. Clients should use the returned access token + when making subsequent API calls, and store the returned refresh token + (if given) in order to refresh the new access token when necessary. + + After an access token has been refreshed, a server can choose to + invalidate the old access token immediately, or can choose not to, for + example if the access token would expire soon anyways. Clients should + not make any assumptions about the old access token still being valid, + and should use the newly provided access token instead. + + Note that this endpoint does not require authentication, since + authentication is provided via the refresh token. + + Application Service identity assertion is disabled for this endpoint. + operationId: refresh + parameters: + - in: body + name: body + required: true + schema: + type: object + example: { + "refresh_token": "some_token" + } + properties: + refresh_token: + type: string + description: The refresh token + responses: + 200: + description: A new access token and refresh token were generated. + examples: + application/json: { + "access_token": "a_new_token", + "expires_in_ms": 60000, + "refresh_token": "another_new_token" + } + schema: + type: object + properties: + access_token: + type: string + description: |- + The new access token to use. + refresh_token: + type: string + description: |- + The new refresh token to use when the access token needs to + be refreshed again. If not given, the old refresh token can + be re-used. + expires_in_ms: + type: integer + description: |- + The lifetime of the access token, in milliseconds. If not + given, the client can assume that the access token will not + expire. + required: + - access_token + 401: + description: |- + The provided token was unknown, or has already been used. + examples: + application/json: { + "errcode": "M_UNKNOWN_TOKEN", + "error": "Soft logged out", + "soft_logout": true + } + schema: + "$ref": "definitions/errors/error.yaml" + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" diff --git a/data/api/client-server/registration.yaml b/data/api/client-server/registration.yaml index 810d8bc9..7e6a34cd 100644 --- a/data/api/client-server/registration.yaml +++ b/data/api/client-server/registration.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2022 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -127,6 +128,11 @@ paths: returned from this call, therefore preventing an automatic login. Defaults to false. example: false + refresh_token: + type: boolean + description: |- + If true, the client supports refresh tokens. + x-addedInMatrixVersion: "1.3" responses: 200: description: The account has been registered. @@ -152,6 +158,27 @@ paths: An access token for the account. This access token can then be used to authorize other requests. Required if the `inhibit_login` option is false. + refresh_token: + type: string + description: |- + A refresh token for the account. This token can be used to + obtain a new access token when it expires by calling the + `/refresh` endpoint. + + Omitted if the `inhibit_login` option is false. + x-addedInMatrixVersion: "1.3" + expires_in_ms: + type: integer + description: |- + The lifetime of the access token, in milliseconds. Once + the access token has expired a new access token can be + obtained by using the provided refresh token. If no + refresh token is provided, the client will need re-log in + to obtain a new access token. If not given, the client can + assume that the access token will not expire. + + Omitted if the `inhibit_login` option is false. + x-addedInMatrixVersion: "1.3" home_server: type: string description: |- From d692062e3a7f1ddf2acf04480b85d39ca0890639 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 10 May 2022 15:17:09 -0400 Subject: [PATCH 02/17] add changelog --- changelogs/client_server/newsfragments/1056.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/1056.feature diff --git a/changelogs/client_server/newsfragments/1056.feature b/changelogs/client_server/newsfragments/1056.feature new file mode 100644 index 00000000..2f8febb7 --- /dev/null +++ b/changelogs/client_server/newsfragments/1056.feature @@ -0,0 +1 @@ +Add refresh tokens, per [MSC2918](https://github.com/matrix-org/matrix-spec-proposals/pull/2918). From 515269b2e337b14957a7e0d0a456566a0a08bde8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 31 May 2022 09:24:06 -0600 Subject: [PATCH 03/17] Unify & standardize the v1.3 changelog (#1102) * Reference MSCs where MSCs were not being referenced. * Alter language to appear consistent and from a single voice. * Bundle and group various changes together (will affect the final changelog - the rendered one still doesn't bundle appropriately). * Move entries to the spec area they are intended to be in. --- changelogs/client_server/newsfragments/1003.clarification | 2 +- changelogs/client_server/newsfragments/1059.clarification | 2 +- changelogs/client_server/newsfragments/3681.clarification | 2 +- changelogs/room_versions/newsfragments/1037.clarification | 3 +-- changelogs/room_versions/newsfragments/1042.clarification | 1 + changelogs/room_versions/newsfragments/1043.clarification | 2 +- changelogs/room_versions/newsfragments/1050.clarification | 2 +- changelogs/room_versions/newsfragments/1093.clarification | 2 +- changelogs/room_versions/newsfragments/3737.clarification | 2 +- changelogs/room_versions/newsfragments/3739.feature | 2 +- changelogs/server_server/newsfragments/1038.clarification | 2 +- changelogs/server_server/newsfragments/1042.clarification | 1 - changelogs/server_server/newsfragments/1045.clarification | 2 +- changelogs/server_server/newsfragments/1055.clarification | 2 +- changelogs/server_server/newsfragments/1067.feature | 2 +- changelogs/server_server/newsfragments/1070.clarification | 2 +- changelogs/server_server/newsfragments/998.clarification | 2 +- 17 files changed, 16 insertions(+), 17 deletions(-) create mode 100644 changelogs/room_versions/newsfragments/1042.clarification delete mode 100644 changelogs/server_server/newsfragments/1042.clarification diff --git a/changelogs/client_server/newsfragments/1003.clarification b/changelogs/client_server/newsfragments/1003.clarification index efe68e48..9b2788cc 100644 --- a/changelogs/client_server/newsfragments/1003.clarification +++ b/changelogs/client_server/newsfragments/1003.clarification @@ -1 +1 @@ -Adjust the OpenAPI specification so that the type `Flow information` is explicitly defined when the CS spec is rendered. +Adjust the OpenAPI specification so that the type `Flow information` is explicitly defined when the client-server API is rendered. diff --git a/changelogs/client_server/newsfragments/1059.clarification b/changelogs/client_server/newsfragments/1059.clarification index 2e94fb7f..ca5f3aea 100644 --- a/changelogs/client_server/newsfragments/1059.clarification +++ b/changelogs/client_server/newsfragments/1059.clarification @@ -1 +1 @@ -Fix room state 400 code error examples to match known error codes. +Fix various typos throughout the specification. \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/3681.clarification b/changelogs/client_server/newsfragments/3681.clarification index f7c29003..ca5f3aea 100644 --- a/changelogs/client_server/newsfragments/3681.clarification +++ b/changelogs/client_server/newsfragments/3681.clarification @@ -1 +1 @@ -Fix broken syntax in Server Access Control Lists definition. \ No newline at end of file +Fix various typos throughout the specification. \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1037.clarification b/changelogs/room_versions/newsfragments/1037.clarification index 9ac53c6d..e8aa3349 100644 --- a/changelogs/room_versions/newsfragments/1037.clarification +++ b/changelogs/room_versions/newsfragments/1037.clarification @@ -1,2 +1 @@ -Improve readability of definitions in the state resolution v2 algorithm. - +Improve readability and understanding of the state resolution algorithms. \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1042.clarification b/changelogs/room_versions/newsfragments/1042.clarification new file mode 100644 index 00000000..e8aa3349 --- /dev/null +++ b/changelogs/room_versions/newsfragments/1042.clarification @@ -0,0 +1 @@ +Improve readability and understanding of the state resolution algorithms. \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1043.clarification b/changelogs/room_versions/newsfragments/1043.clarification index 49e552f4..e8aa3349 100644 --- a/changelogs/room_versions/newsfragments/1043.clarification +++ b/changelogs/room_versions/newsfragments/1043.clarification @@ -1 +1 @@ -Adjust mathematical notation used in the description of state resolution to render better in browsers. \ No newline at end of file +Improve readability and understanding of the state resolution algorithms. \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1050.clarification b/changelogs/room_versions/newsfragments/1050.clarification index 0da1098a..f548b987 100644 --- a/changelogs/room_versions/newsfragments/1050.clarification +++ b/changelogs/room_versions/newsfragments/1050.clarification @@ -1 +1 @@ -Add cross-references to PDU content definitions from the authorisation rules. +Improve readability of the authorization rules. \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1093.clarification b/changelogs/room_versions/newsfragments/1093.clarification index 6176b102..06145378 100644 --- a/changelogs/room_versions/newsfragments/1093.clarification +++ b/changelogs/room_versions/newsfragments/1093.clarification @@ -1 +1 @@ -Auth rules: clarify that the resident server must sign a restricted join event. +For room versions 8, 9, and 10: clarify which homeserver is required to sign the join event. \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/3737.clarification b/changelogs/room_versions/newsfragments/3737.clarification index c3b51679..6caf519a 100644 --- a/changelogs/room_versions/newsfragments/3737.clarification +++ b/changelogs/room_versions/newsfragments/3737.clarification @@ -1 +1 @@ -Fix join membership auth rules when `join_rule` is `knock`. +For room versions 7, 8, 9, and 10: fix join membership authorization rules when `join_rule` is `knock`. diff --git a/changelogs/room_versions/newsfragments/3739.feature b/changelogs/room_versions/newsfragments/3739.feature index c20ec3ad..ddb88446 100644 --- a/changelogs/room_versions/newsfragments/3739.feature +++ b/changelogs/room_versions/newsfragments/3739.feature @@ -1 +1 @@ -Update the default room version to 9. \ No newline at end of file +Update the default room version to 9 as per [MSC3589](https://github.com/matrix-org/matrix-spec-proposals/pull/3589). \ No newline at end of file diff --git a/changelogs/server_server/newsfragments/1038.clarification b/changelogs/server_server/newsfragments/1038.clarification index 39fbeddb..ad1ca8f0 100644 --- a/changelogs/server_server/newsfragments/1038.clarification +++ b/changelogs/server_server/newsfragments/1038.clarification @@ -1 +1 @@ -Fix origin server name in S2S Request Authentication example. +Clarify the format for the Authorization header. diff --git a/changelogs/server_server/newsfragments/1042.clarification b/changelogs/server_server/newsfragments/1042.clarification deleted file mode 100644 index 2f3fad6f..00000000 --- a/changelogs/server_server/newsfragments/1042.clarification +++ /dev/null @@ -1 +0,0 @@ -Clarify the meaning of "unconflicted state map" and "auth chain" in state res v2. diff --git a/changelogs/server_server/newsfragments/1045.clarification b/changelogs/server_server/newsfragments/1045.clarification index 7b805569..adb3ec16 100644 --- a/changelogs/server_server/newsfragments/1045.clarification +++ b/changelogs/server_server/newsfragments/1045.clarification @@ -1 +1 @@ -Expand a little on what it means for a PDU to be valid when discussing checks on PDUs. +Clarify what a "valid event" means when performing checks on a received PDU. \ No newline at end of file diff --git a/changelogs/server_server/newsfragments/1055.clarification b/changelogs/server_server/newsfragments/1055.clarification index 748f7793..f9394c6a 100644 --- a/changelogs/server_server/newsfragments/1055.clarification +++ b/changelogs/server_server/newsfragments/1055.clarification @@ -1 +1 @@ -Clarify that valid_until_ts is in milliseconds, like other timestamps used in Matrix +Clarify that `valid_until_ts` is in milliseconds, like other timestamps used in Matrix. diff --git a/changelogs/server_server/newsfragments/1067.feature b/changelogs/server_server/newsfragments/1067.feature index 1e88a3d9..0b067b36 100644 --- a/changelogs/server_server/newsfragments/1067.feature +++ b/changelogs/server_server/newsfragments/1067.feature @@ -1 +1 @@ -Add a destination property to the Authorization header. +Add a `destination` property to the Authorization header, as per [MSC3383](https://github.com/matrix-org/matrix-spec-proposals/pull/3383). diff --git a/changelogs/server_server/newsfragments/1070.clarification b/changelogs/server_server/newsfragments/1070.clarification index 6661503e..ef4b1e6a 100644 --- a/changelogs/server_server/newsfragments/1070.clarification +++ b/changelogs/server_server/newsfragments/1070.clarification @@ -1 +1 @@ -Clarify that checks on PDUs should refer to the state _before_ an event. +Clarify that checks on PDUs should refer to the state *before* an event. diff --git a/changelogs/server_server/newsfragments/998.clarification b/changelogs/server_server/newsfragments/998.clarification index c285269a..495fdade 100644 --- a/changelogs/server_server/newsfragments/998.clarification +++ b/changelogs/server_server/newsfragments/998.clarification @@ -1 +1 @@ -Remove `origin` field from PDUs which exists on many but not all PDUs in practice and doesn't serve an actual purpose. +Remove largely unused `origin` field from PDUs. From a91030f27bf6b54f9fd32c38a653c0881070c0f4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 31 May 2022 09:24:25 -0600 Subject: [PATCH 04/17] Re-add paragraph about how some state keys are reserved (#1100) * Re-add paragraph about how some state keys are reserved Fixes https://github.com/matrix-org/matrix-spec/issues/1013 * changelog --- changelogs/client_server/newsfragments/1100.clarification | 1 + .../definitions/client_event_without_room_id.yaml | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 changelogs/client_server/newsfragments/1100.clarification diff --git a/changelogs/client_server/newsfragments/1100.clarification b/changelogs/client_server/newsfragments/1100.clarification new file mode 100644 index 00000000..e17a2538 --- /dev/null +++ b/changelogs/client_server/newsfragments/1100.clarification @@ -0,0 +1 @@ +Clarify that state keys starting with `@` are in fact reserved. Regressed from [#3658](https://github.com/matrix-org/matrix-spec-proposals/pull/3658). \ No newline at end of file diff --git a/data/api/client-server/definitions/client_event_without_room_id.yaml b/data/api/client-server/definitions/client_event_without_room_id.yaml index 1b6d6073..c4db8b0e 100644 --- a/data/api/client-server/definitions/client_event_without_room_id.yaml +++ b/data/api/client-server/definitions/client_event_without_room_id.yaml @@ -38,6 +38,10 @@ properties: Present if, and only if, this event is a *state* event. The key making this piece of state unique in the room. Note that it is often an empty string. + + State keys starting with an `@` are reserved for referencing user IDs, such + as room members. With the exception of a few events, state events set with a + given user's ID as the state key MUST only be set by that user. type: string example: '@user:example.org' sender: From 10bd1b50383c43a1b83aa01b42de5578a49aa200 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 31 May 2022 09:24:45 -0600 Subject: [PATCH 05/17] Re-add timestamp massaging (#1094) * Re-add timestamp massaging Per [MSC3316](https://github.com/matrix-org/matrix-spec-proposals/pull/3316) * changelog --- .../newsfragments/1094.feature | 1 + content/application-service-api.md | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 changelogs/application_service/newsfragments/1094.feature diff --git a/changelogs/application_service/newsfragments/1094.feature b/changelogs/application_service/newsfragments/1094.feature new file mode 100644 index 00000000..e1b2d5cb --- /dev/null +++ b/changelogs/application_service/newsfragments/1094.feature @@ -0,0 +1 @@ +Add timestamp massaging as per [MSC3316](https://github.com/matrix-org/matrix-spec-proposals/pull/3316). \ No newline at end of file diff --git a/content/application-service-api.md b/content/application-service-api.md index 07362c51..61227660 100644 --- a/content/application-service-api.md +++ b/content/application-service-api.md @@ -300,13 +300,38 @@ An example request would be: #### Timestamp massaging -Previous drafts of the Application Service API permitted application -services to alter the timestamp of their sent events by providing a `ts` -query parameter when sending an event. This API has been excluded from -the first release due to design concerns, however some servers may still -support the feature. Please visit [issue -\#1585](https://github.com/matrix-org/matrix-doc/issues/1585) for more -information. +{{% added-in v="1.3" %}} + +Application services can alter the timestamp associated with an event, allowing +the application service to better represent the "real" time an event was sent +at. While this doesn't affect the server-side ordering of the event, it can allow +an application service to better represent when an event would have been sent/received +at, such as in the case of bridges where the remote network might have a slight +delay and the application service wishes to bridge the proper time onto the message. + +When authenticating requests as an application service, the caller can append a `ts` +query string argument to change the `origin_server_ts` of the resulting event. Attempting +to set the timestamp to anything other than what is accepted by `origin_server_ts` should +be rejected by the server as a bad request. + +When not present, the server's behaviour is unchanged: the local system time of the server +will be used to provide a timestamp, representing "now". + +The `ts` query string argument is only valid on the following endpoints: + +* [`PUT /rooms/{roomId}/send/{eventType}/{txnId}`](/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid) +* [`PUT /rooms/{roomId}/state/{eventType}/{stateKey}`](/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey) + +Other endpoints, such as `/kick`, do not support `ts`: instead, callers can use the +`PUT /state` endpoint to mimic the behaviour of the other APIs. + +{{% boxes/warning %}} +Changing the time of an event does not change the server-side (DAG) ordering for the +event. The event will still be appended at the tip of the DAG as though the timestamp +was set to "now". Future MSCs, like [MSC2716](https://github.com/matrix-org/matrix-spec-proposals/pull/2716), +are expected to provide functionality which can allow DAG order manipulation (for history +imports and similar behaviour). +{{% /boxes/warning %}} #### Server admin style permissions From 91b2a49b6f5a912975b3492a4c7615f2245cbf1b Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 31 May 2022 16:19:15 -0400 Subject: [PATCH 06/17] apply suggestions from review, and other clarifications --- content/client-server-api/_index.md | 92 +++++++++++++++-------------- data/api/client-server/refresh.yaml | 3 + 2 files changed, 51 insertions(+), 44 deletions(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 5eb5f8b5..5f2740b9 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -337,15 +337,15 @@ to prevent the access token being leaked in access/HTTP logs. The query string should only be used in cases where the `Authorization` header is inaccessible for the client. -{{% changed-in v="1.3" %}} When credentials are required but missing or -invalid, the HTTP call will return with a status of 401 and the error code, -`M_MISSING_TOKEN` or `M_UNKNOWN_TOKEN` respectively. Note that an error code -of `M_UNKNOWN_TOKEN` could mean one of four things: +When credentials are required but missing or invalid, the HTTP call will +return with a status of 401 and the error code, `M_MISSING_TOKEN` or +`M_UNKNOWN_TOKEN` respectively. Note that an error code of `M_UNKNOWN_TOKEN` +could mean one of four things: 1. the access token was never valid. 2. the access token has been logged out. 3. the access token has been [soft logged out](#soft-logout). -4. the access token [needs to be refreshed](#refreshing-access-tokens). +4. {{< added-in v="1.3" >}} the access token [needs to be refreshed](#refreshing-access-tokens). When a client receives an error code of `M_UNKNOWN_TOKEN`, it should: @@ -377,52 +377,56 @@ invalidate any access and refresh tokens previously assigned to that device. {{% added-in v="1.3" %}} -Access tokens can expire after a certain amount of time. Any HTTP -calls that use an expired access token will return with an error code -`M_UNKNOWN_TOKEN`, preferably with `soft_logout: true`. When a client -receives this error and it has a refresh token, it should attempt to -refresh the access token by calling `/refresh`. Clients can also -refresh their access token at any time, even if it has not yet -expired. If the token refresh succeeds, it should use the new token -for future requests, and can re-try previously-failed requests with -the new token. When an access token is refreshed, a new refresh token -may be returned; if a new refresh token is given, the old refresh -token will be invalidated, and the new refresh token should be used -when the access token needs to be refreshed. +Access tokens can expire after a certain amount of time. Any HTTP calls that +use an expired access token will return with an error code `M_UNKNOWN_TOKEN`, +preferably with `soft_logout: true`. When a client receives this error and it +has a refresh token, it should attempt to refresh the access token by calling +[`/refresh`](#post_matrixclientv3refresh). Clients can also refresh their +access token at any time, even if it has not yet expired. If the token refresh +succeeds, the client should use the new token for future requests, and can +re-try previously-failed requests with the new token. When an access token is +refreshed, a new refresh token may be returned; if a new refresh token is +given, the old refresh token will be invalidated, and the new refresh token +should be used when the access token needs to be refreshed. -If the token refresh fails, then the client can treat it as a [soft -logout](#soft-logout), if the error response included a `soft_logout: -true` property, and attempt to obtain a new access token by re-logging -in. If the error response does not include a `soft_logout: true` -property, the client should consider the user as being logged out. +The old refresh token remains valid until the new access token or refresh token +is used, at which point the old refresh token is revoked. This ensures that if +a client fails to receive or persist the new tokens, it will still be able to +refresh them. -Handling of clients that do not support refresh tokens is up to the -homeserver; clients indicate their support for refresh tokens by -including a `refresh_token: true` property in the request body of the -`/login` and `/register` endpoints. For example, homeservers may allow -the use of non-expiring access tokens, or may expire access tokens -anyways and rely on soft logout behaviour on clients that don't -support refreshing. +If the token refresh fails and the error response included a `soft_logout: +true` property, then the client can treat it as a [soft logout](#soft-logout) +and attempt to obtain a new access token by re-logging in. If the error +response does not include a `soft_logout: true` property, the client should +consider the user as being logged out. + +Handling of clients that do not support refresh tokens is up to the homeserver; +clients indicate their support for refresh tokens by including a +`refresh_token: true` property in the request body of the +[`/login`](#post_matrixclientv3login) and +[`/register`](#post_matrixclientv3register) endpoints. For example, homeservers +may allow the use of non-expiring access tokens, or may expire access tokens +anyways and rely on soft logout behaviour on clients that don't support +refreshing. ### Soft logout A client can be in a "soft logout" state if the server requires -re-authentication before continuing, but does not want to invalidate -the client's session. That is, any persisted state held by the client, -such as encryption keys and device information, must not be reused and -must be discarded, and can be re-used if the client successfully -re-authenticates. +re-authentication before continuing, but does not want to invalidate the +client's session. The server indicates that the client is in a soft logout +state by including a `soft_logout: true` parameter in an `M_UNKNOWN_TOKEN` +error response; the `soft_logout` parameter defaults to `false`. If the +`soft_logout` parameter is omitted or is `false`, this means the server has +destroyed the session and the client should not reuse it. That is, any +persisted state held by the client, such as encryption keys and device +information, must not be reused and must be discarded. If `soft_logout` is +`true` the client can reuse any persisted state. -The server indicates that the client is in a soft logout state by -including a `soft_logout: true` parameter in an `M_UNKNOWN_TOKEN` -error response; the `soft_logout` parameter defaults to `false`. - -A client that receives such a response can try to [refresh its access -token](#refreshing-access-tokens), if it has a refresh token -available. If it does not have a refresh token available, or -refreshing fails with `soft_logout: true`, the client can acquire a -new access token by specifying the device ID it is already using to -the login API. +{{% changed-in v="1.3" %}} A client that receives such a response can try to +[refresh its access token](#refreshing-access-tokens), if it has a refresh +token available. If it does not have a refresh token available, or refreshing +fails with `soft_logout: true`, the client can acquire a new access token by +specifying the device ID it is already using to the login API. ### User-Interactive Authentication API diff --git a/data/api/client-server/refresh.yaml b/data/api/client-server/refresh.yaml index abe8c8fb..273e6d6a 100644 --- a/data/api/client-server/refresh.yaml +++ b/data/api/client-server/refresh.yaml @@ -42,6 +42,9 @@ paths: not make any assumptions about the old access token still being valid, and should use the newly provided access token instead. + The old refresh token remains valid until the new access token or refresh token + is used, at which point the old refresh token is revoked. + Note that this endpoint does not require authentication, since authentication is provided via the refresh token. From 31304300f5dc588f06a798cbda34b08b5800eb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=20one=20with=20the=20braid=20=28she/her=29=20=7C=20D?= =?UTF-8?q?=D1=84=D2=BF=20mit=20dem=20Zopf=20=28sie/ihr=29?= Date: Tue, 31 May 2022 22:33:04 +0200 Subject: [PATCH 07/17] fix: spaces hierarchy paramater types (#1097) * fix: spaces hierarchy paramater types - changed `limit` parameter type to integer - changed `query` parameter type to integer A floating number does not make any sense here. Also, at least Synapse does not allow floating point numbers in here. Signed-off-by: TheOneWithTheBraid * Update changelogs/client_server/newsfragments/1097.clarification Co-authored-by: Travis Ralston --- changelogs/client_server/newsfragments/1097.clarification | 1 + data/api/client-server/space_hierarchy.yaml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelogs/client_server/newsfragments/1097.clarification diff --git a/changelogs/client_server/newsfragments/1097.clarification b/changelogs/client_server/newsfragments/1097.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/1097.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/data/api/client-server/space_hierarchy.yaml b/data/api/client-server/space_hierarchy.yaml index 607eb8d1..12640b53 100644 --- a/data/api/client-server/space_hierarchy.yaml +++ b/data/api/client-server/space_hierarchy.yaml @@ -58,7 +58,7 @@ paths: contents. x-example: true - in: query - type: number + type: integer name: limit description: |- Optional limit for the maximum number of rooms to include per response. Must be an integer @@ -67,7 +67,7 @@ paths: Servers should apply a default value, and impose a maximum value to avoid resource exhaustion. x-example: 20 - in: query - type: number + type: integer name: max_depth description: |- Optional limit for how far to go into the space. Must be a non-negative integer. From ef3df9d549c4f06f6744c18bb17927878a5dd0f8 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 2 Jun 2022 16:17:46 -0400 Subject: [PATCH 08/17] Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- content/client-server-api/_index.md | 4 ++-- data/api/client-server/login.yaml | 2 +- data/api/client-server/refresh.yaml | 4 ++-- data/api/client-server/registration.yaml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 5f2740b9..4f82c9a8 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -391,8 +391,8 @@ should be used when the access token needs to be refreshed. The old refresh token remains valid until the new access token or refresh token is used, at which point the old refresh token is revoked. This ensures that if -a client fails to receive or persist the new tokens, it will still be able to -refresh them. +a client fails to receive or persist the new tokens, it will be able to repeat +the refresh operation. If the token refresh fails and the error response included a `soft_logout: true` property, then the client can treat it as a [soft logout](#soft-logout) diff --git a/data/api/client-server/login.yaml b/data/api/client-server/login.yaml index 3eb1e0d2..d279445d 100644 --- a/data/api/client-server/login.yaml +++ b/data/api/client-server/login.yaml @@ -184,7 +184,7 @@ paths: The lifetime of the access token, in milliseconds. Once the access token has expired a new access token can be obtained by using the provided refresh token. If no - refresh token is provided, the client will need re-log in + refresh token is provided, the client will need to re-log in to obtain a new access token. If not given, the client can assume that the access token will not expire. x-addedInMatrixVersion: "1.3" diff --git a/data/api/client-server/refresh.yaml b/data/api/client-server/refresh.yaml index 273e6d6a..29013c15 100644 --- a/data/api/client-server/refresh.yaml +++ b/data/api/client-server/refresh.yaml @@ -45,8 +45,8 @@ paths: The old refresh token remains valid until the new access token or refresh token is used, at which point the old refresh token is revoked. - Note that this endpoint does not require authentication, since - authentication is provided via the refresh token. + Note that this endpoint does not require authentication via an + access token. Authentication is provided via the refresh token. Application Service identity assertion is disabled for this endpoint. operationId: refresh diff --git a/data/api/client-server/registration.yaml b/data/api/client-server/registration.yaml index 7e6a34cd..21fc1b84 100644 --- a/data/api/client-server/registration.yaml +++ b/data/api/client-server/registration.yaml @@ -173,7 +173,7 @@ paths: The lifetime of the access token, in milliseconds. Once the access token has expired a new access token can be obtained by using the provided refresh token. If no - refresh token is provided, the client will need re-log in + refresh token is provided, the client will need to re-log in to obtain a new access token. If not given, the client can assume that the access token will not expire. From 9bf02ada5509ccaa72c58db55c5b60f8025f9694 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 2 Jun 2022 16:19:11 -0400 Subject: [PATCH 09/17] don't need securityDefinitions since we don't use access token --- data/api/client-server/refresh.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/data/api/client-server/refresh.yaml b/data/api/client-server/refresh.yaml index 29013c15..da34070c 100644 --- a/data/api/client-server/refresh.yaml +++ b/data/api/client-server/refresh.yaml @@ -24,8 +24,6 @@ consumes: - application/json produces: - application/json -securityDefinitions: - $ref: definitions/security.yaml paths: "/refresh": post: From e70045f4ccbe927e7d7ee2e20160bec6ae6d0d39 Mon Sep 17 00:00:00 2001 From: Nicolas Werner <89468146+nico-famedly@users.noreply.github.com> Date: Tue, 7 Jun 2022 23:27:53 +0000 Subject: [PATCH 10/17] room_type is not a required parameter in practice (#1110) * room_type is not a required parameter in practice In practice servers seem to mirror what the room create event does and leave out the room_type when unset. Signed-off-by: Nicolas Werner * Add changelog Signed-off-by: Nicolas Werner * Also make room_type and allowed_room_ids optional in the openapi They are optional according to the text, but the openapi marks them as required instead. Signed-off-by: Nicolas Werner * Fix copy and paste error of newsfragment Signed-off-by: Nicolas Werner --- changelogs/client_server/newsfragments/1110.clarification | 1 + changelogs/server_server/newsfragments/1110.clarification | 1 + data/api/client-server/space_hierarchy.yaml | 2 +- data/api/server-server/space_hierarchy.yaml | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelogs/client_server/newsfragments/1110.clarification create mode 100644 changelogs/server_server/newsfragments/1110.clarification diff --git a/changelogs/client_server/newsfragments/1110.clarification b/changelogs/client_server/newsfragments/1110.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/1110.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/changelogs/server_server/newsfragments/1110.clarification b/changelogs/server_server/newsfragments/1110.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/server_server/newsfragments/1110.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/data/api/client-server/space_hierarchy.yaml b/data/api/client-server/space_hierarchy.yaml index 12640b53..309b99e3 100644 --- a/data/api/client-server/space_hierarchy.yaml +++ b/data/api/client-server/space_hierarchy.yaml @@ -149,7 +149,7 @@ paths: format: int64 description: The `origin_server_ts` for the event. required: [origin_server_ts] - required: [room_type, children_state] + required: [children_state] next_batch: type: string description: |- diff --git a/data/api/server-server/space_hierarchy.yaml b/data/api/server-server/space_hierarchy.yaml index cb30fa0f..834848c9 100644 --- a/data/api/server-server/space_hierarchy.yaml +++ b/data/api/server-server/space_hierarchy.yaml @@ -158,7 +158,7 @@ paths: format: int64 description: The `origin_server_ts` for the event. required: [origin_server_ts] - required: [room_type, allowed_room_ids, children_state] + required: [children_state] children: type: array description: |- From f14e18131b0bff3b599380a4028065ffdb7177bd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 8 Jun 2022 15:22:47 -0600 Subject: [PATCH 11/17] Specify room version 10: `knock_restricted` and int power levels (#1099) * Clarification on historical power level handling * Revert "Clarification on historical power level handling" This reverts commit f443b3d5a9afac3095b14a72ec471ba06f4cc78b. * Clean up * Let us try this again not using VS Code * Markdown is full of mysteries * Move stringy power levels to room versions * Describe range * Fix minor issues with previous room version stuff * Copy/paste v9 into v10 * Describe deprecated formatting * Paste unmodified auth rules from v8 into v10 * Move 9.1 to 9.3, add 9.1 and 9.2 for integer enforcement * Add knock_restricted to v10 auth * Misc cleanup and clarification for fragments * Describe `knock_restricted` client changes * Changelogs * spelling * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Apply code review suggestions manually * Fix v9 redactions * Fix auth rules clarity issues * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Remove false integer requirements Co-authored-by: Neil Alexander Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- .../client_server/newsfragments/1099.feature | 1 + .../newsfragments/1099.clarification | 1 + .../newsfragments/1099.feature.1 | 1 + .../newsfragments/1099.feature.2 | 1 + .../newsfragments/1099.feature.3 | 1 + .../newsfragments/1099.clarification | 1 + content/client-server-api/_index.md | 25 ++ content/rooms/_index.md | 11 +- .../v1-deprecated-formatting-off-spec.md | 13 + .../fragments/v1-stringy-power-levels.md | 43 +++ content/rooms/fragments/v9-redactions.md | 51 ++++ content/rooms/v1.md | 6 + content/rooms/v10.md | 277 ++++++++++++++++++ content/rooms/v2.md | 6 + content/rooms/v3.md | 6 + content/rooms/v4.md | 6 + content/rooms/v5.md | 6 + content/rooms/v6.md | 6 + content/rooms/v7.md | 8 +- content/rooms/v8.md | 8 +- content/rooms/v9.md | 58 +--- content/server-server-api.md | 10 + .../schema/m.room.join_rules.yaml | 4 + 23 files changed, 494 insertions(+), 56 deletions(-) create mode 100644 changelogs/client_server/newsfragments/1099.feature create mode 100644 changelogs/room_versions/newsfragments/1099.clarification create mode 100644 changelogs/room_versions/newsfragments/1099.feature.1 create mode 100644 changelogs/room_versions/newsfragments/1099.feature.2 create mode 100644 changelogs/room_versions/newsfragments/1099.feature.3 create mode 100644 changelogs/server_server/newsfragments/1099.clarification create mode 100644 content/rooms/fragments/v1-deprecated-formatting-off-spec.md create mode 100644 content/rooms/fragments/v1-stringy-power-levels.md create mode 100644 content/rooms/fragments/v9-redactions.md create mode 100644 content/rooms/v10.md diff --git a/changelogs/client_server/newsfragments/1099.feature b/changelogs/client_server/newsfragments/1099.feature new file mode 100644 index 00000000..632a1c46 --- /dev/null +++ b/changelogs/client_server/newsfragments/1099.feature @@ -0,0 +1 @@ +Add support for a new `knock_restricted` join rule in supported room versions, as per [MSC3787](https://github.com/matrix-org/matrix-spec-proposals/pull/3787). \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1099.clarification b/changelogs/room_versions/newsfragments/1099.clarification new file mode 100644 index 00000000..272aec3e --- /dev/null +++ b/changelogs/room_versions/newsfragments/1099.clarification @@ -0,0 +1 @@ +Clarify that room versions 1 through 9 accept stringy power levels, as noted by [MSC3667](https://github.com/matrix-org/matrix-spec-proposals/pull/3667). \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1099.feature.1 b/changelogs/room_versions/newsfragments/1099.feature.1 new file mode 100644 index 00000000..d8fabdba --- /dev/null +++ b/changelogs/room_versions/newsfragments/1099.feature.1 @@ -0,0 +1 @@ +Add room version 10 as per [MSC3604](https://github.com/matrix-org/matrix-spec-proposals/pull/3604). \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1099.feature.2 b/changelogs/room_versions/newsfragments/1099.feature.2 new file mode 100644 index 00000000..b89704b8 --- /dev/null +++ b/changelogs/room_versions/newsfragments/1099.feature.2 @@ -0,0 +1 @@ +Enforce integer power levels in room version 10 as per [MSC3667](https://github.com/matrix-org/matrix-spec-proposals/pull/3667). \ No newline at end of file diff --git a/changelogs/room_versions/newsfragments/1099.feature.3 b/changelogs/room_versions/newsfragments/1099.feature.3 new file mode 100644 index 00000000..b10228de --- /dev/null +++ b/changelogs/room_versions/newsfragments/1099.feature.3 @@ -0,0 +1 @@ +Add a `knock_restricted` join rule supported by room version 10 as per [MSC3787](https://github.com/matrix-org/matrix-spec-proposals/pull/3787). \ No newline at end of file diff --git a/changelogs/server_server/newsfragments/1099.clarification b/changelogs/server_server/newsfragments/1099.clarification new file mode 100644 index 00000000..76ce9849 --- /dev/null +++ b/changelogs/server_server/newsfragments/1099.clarification @@ -0,0 +1 @@ +Clarify the historical handling of non-integer power levels. \ No newline at end of file diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 4f82c9a8..67bca5e5 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -2026,6 +2026,13 @@ room listed in the join rules. If the server cannot verify membership for any of the listed rooms then you can only join with an invite. Note that this rule is only expected to work in room versions [which support it](/rooms/#feature-matrix). +{{% added-in v="1.3" %}} `knock_restricted` +This room can be joined as though it was `restricted` *or* `knock`. If you +interact with the room using knocking, the `knock` rule takes effect whereas +trying to join the room without an invite applies the `restricted` join rule. +Note that this rule is only expected to work in room versions +[which support it](/rooms/#feature-matrix). + The allowable state transitions of membership are: ![membership-flow-diagram](/diagrams/membership.png) @@ -2041,6 +2048,15 @@ The allowable state transitions of membership are: ##### Knocking on rooms {{% added-in v="1.1" %}} +{{% changed-in v="1.3" %}} + +{{% boxes/note %}} +As of `v1.3`, it is possible to knock on a [restricted room](#restricted-rooms) +if the room supports and is using the `knock_restricted` join rule. + +Note that `knock_restricted` is only expected to work in room versions +[which support it](/rooms/#feature-matrix). +{{% /boxes/note %}} -| Feature \ Version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -|-------------------|---|---|---|---|---|---|---|---|---| -| **Knocking** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | ✔ | -| **Restricted join rules** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | +| Feature \ Version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | +|-------------------|---|---|---|---|---|---|---|---|---|----| +| **Knocking** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | +| **Restricted join rules** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | ✔ | +| **`knock_restricted` join rule** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ## Complete list of room versions @@ -73,6 +74,8 @@ The available room versions are: of another room to join without invite. - [Version 9](/rooms/v9) - **Stable**. Builds on v8 to fix issues when redacting some membership events. +- [Version 10](/rooms/v10) - **Stable**. Enforces integer-only power levels + and adds `knock_restricted` join rule. ## Room version grammar diff --git a/content/rooms/fragments/v1-deprecated-formatting-off-spec.md b/content/rooms/fragments/v1-deprecated-formatting-off-spec.md new file mode 100644 index 00000000..1cf7d49f --- /dev/null +++ b/content/rooms/fragments/v1-deprecated-formatting-off-spec.md @@ -0,0 +1,13 @@ +--- +toc_hide: true +--- + +Events sent into rooms of this version can have formats which are different +from their normal schema. Those cases are documented here. + +{{% boxes/warning %}} +The behaviour described here is preserved strictly for backwards compatibility +only. A homeserver should take reasonable precautions to prevent users from +sending these so-called "malformed" events, and must never rely on the behaviours +described here as a default. +{{% /boxes/warning %}} diff --git a/content/rooms/fragments/v1-stringy-power-levels.md b/content/rooms/fragments/v1-stringy-power-levels.md new file mode 100644 index 00000000..054c0506 --- /dev/null +++ b/content/rooms/fragments/v1-stringy-power-levels.md @@ -0,0 +1,43 @@ +--- +toc_hide: true +--- + +##### `m.room.power_levels` events accept values as strings + +In order to maintain backwards compatibility with early implementations, +each of the integer-valued properties within +[`m.room.power_levels`](/client-server-api#mroompower_levels) events can +be encoded as strings instead of integers. This includes the nested values +within the `events`, `notifications` and `users` properties. +For example, the following is a valid `m.room.power_levels` event in this room version: + +```json +{ + "content": { + "ban": "50", + "events": { + "m.room.power_levels": "100" + }, + "events_default": "0", + "state_default": "50", + "users": { + "@example:localhost": "100" + }, + "users_default": "0" + }, + "origin_server_ts": 1432735824653, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "sender": "@example:example.org", + "state_key": "", + "type": "m.room.power_levels" +} +``` + +When the value is representative of an integer, they must be the following format: + +* a single base 10 integer, no float values or decimal points, optionally with + any number of leading zeroes (`"100"`, `"000100"`); +* optionally prefixed with a single `-` or `+` character before the integer (`"+100"`, + `"-100"`). +* optionally with any number of leading or trailing whitespace characters (`" 100 "`, + `" 00100 "`, `" +100 "`, `" -100 "`); diff --git a/content/rooms/fragments/v9-redactions.md b/content/rooms/fragments/v9-redactions.md new file mode 100644 index 00000000..3247c59f --- /dev/null +++ b/content/rooms/fragments/v9-redactions.md @@ -0,0 +1,51 @@ +--- +toc_hide: true +--- + +{{% added-in this=true %}} `m.room.member` events now keep `join_authorised_via_users_server` +in addition to other keys in `content` when being redacted. + +{{% boxes/rationale %}} +Without the `join_authorised_via_users_server` property, redacted join events +can become invalid when verifying the auth chain of a given event, thus creating +a split-brain scenario where the user is able to speak from one server's +perspective but most others will continually reject their events. + +This can theoretically be worked around with a rejoin to the room, being careful +not to use the faulty events as `prev_events`, though instead it is encouraged +to use v9 rooms over v8 rooms to outright avoid the situation. + +[Issue #3373](https://github.com/matrix-org/matrix-doc/issues/3373) has further +information. +{{% /boxes/rationale %}} + +The full redaction algorithm follows. + +Upon receipt of a redaction event, the server must strip off any keys +not in the following list: + +- `event_id` +- `type` +- `room_id` +- `sender` +- `state_key` +- `content` +- `hashes` +- `signatures` +- `depth` +- `prev_events` +- `prev_state` +- `auth_events` +- `origin` +- `origin_server_ts` +- `membership` + +The content object must also be stripped of all keys, unless it is one +of one of the following event types: + +- `m.room.member` allows keys `membership`, `join_authorised_via_users_server`. +- `m.room.create` allows key `creator`. +- `m.room.join_rules` allows keys `join_rule`, `allow`. +- `m.room.power_levels` allows keys `ban`, `events`, `events_default`, + `kick`, `redact`, `state_default`, `users`, `users_default`. +- `m.room.history_visibility` allows key `history_visibility`. \ No newline at end of file diff --git a/content/rooms/v1.md b/content/rooms/v1.md index a69e3d5d..3d3e9738 100644 --- a/content/rooms/v1.md +++ b/content/rooms/v1.md @@ -48,6 +48,12 @@ Events in version 1 rooms have the following structure: {{% definition path="api/server-server/definitions/pdu" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### Authorization rules {{% rver-fragment name="v1-auth-rules" %}} diff --git a/content/rooms/v10.md b/content/rooms/v10.md new file mode 100644 index 00000000..63a2e529 --- /dev/null +++ b/content/rooms/v10.md @@ -0,0 +1,277 @@ +--- +title: Room Version 10 +type: docs +weight: 100 +--- + +This room version builds on [version 9](/rooms/v9) to enforce that power level +values be integers, and to introduce a new `knock_restricted` join rule, allowing +prospective members to more easily join such a room. + +## Client considerations + +This room version adds a new `knock_restricted` join rule to allow prospective +members of a room join either through knocking (introduced in [room version 7](/rooms/v7)) +or through "join restrictions" (introduced in [room version 8](/rooms/v8) and +refined in [room version 9](/rooms/v9)). + +Clients should render the new join rule accordingly for such rooms. For example: + +``` +This room is: +[ ] Public +[x] Private + +Join rules (disabled when Public): +[x] Allow members of `#space:example.org` to join +[x] Allow knocking +``` + +Clients which implement the redaction algorithm locally should refer to the +[redactions](#redactions) section below for a full overview. + +## Server implementation components + +{{% boxes/warning %}} +The information contained in this section is strictly for server +implementors. Applications which use the Client-Server API are generally +unaffected by the intricacies contained here. The section above +regarding client considerations is the resource that Client-Server API +use cases should reference. +{{% /boxes/warning %}} + +[Room version 7](/rooms/v7) added "knocking" and [room version 8](/rooms/v8) +added "join restrictions" (refined by [room version 9](/rooms/v9)) — both allow +prospective members an avenue to join, however it was not possible to use +the two mechanisms together. This room version adds a new +`knock_restricted` join rule as a mix of the two behaviours, allowing a user to +join the room if they meet either the `restricted` join rule criteria or the +`knock` join rule criteria. + +This room version additionally requires that values in the power levels event +be integers and not string representations, unlike other room versions. + +Room version 10 is based upon room version 9 with the following considerations. + +### Event format + +The event format is unchanged by this room version. See [below](#event-format-1) +for details on the current event format. + +#### Deprecated event content schemas + +While this room version does not change the event format specifically, some +deprecated behaviours are strictly no longer supported. + +##### Values in `m.room.power_levels` events must be integers + +In other room versions, such as [v9](/rooms/v9/#mroompower_levels-events-accept-values-as-strings), +power levels could be represented as strings for backwards compatibility. + +This backwards compatibility is removed in this room version - power levels MUST NOT +be represented as strings within this room version. Power levels which are not +correctly structured are rejected under the authorization rules below. + +### Authorization rules + +Events must be signed by the server denoted by the `sender` key. + +`m.room.redaction` events are not explicitly part of the auth rules. +They are still subject to the minimum power level rules, but should always +fall into "10. Otherwise, allow". Instead of being authorized at the time +of receipt, they are authorized at a later stage: see the +[Redactions](#redactions) section below for more information. + +The types of state events that affect authorization are: + +- [`m.room.create`](/client-server-api#mroomcreate) +- [`m.room.member`](/client-server-api#mroommember) +- [`m.room.join_rules`](/client-server-api#mroom) +- [`m.room.power_levels`](/client-server-api#mroompower_levels) +- [`m.room.third_party_invite`](/client-server-api#mroomthird_party_invite) + +{{% boxes/note %}} +Power levels are inferred from defaults when not explicitly supplied. +For example, mentions of the `sender`'s power level can also refer to +the default power level for users in the room. +{{% /boxes/note %}} + +The rules are as follows: + +1. If type is `m.room.create`: + 1. If it has any previous events, reject. + 2. If the domain of the `room_id` does not match the domain of the + `sender`, reject. + 3. If `content.room_version` is present and is not a recognised + version, reject. + 4. If `content` has no `creator` field, reject. + 5. Otherwise, allow. +2. Reject if event has `auth_events` that: + 1. have duplicate entries for a given `type` and `state_key` pair + 2. have entries whose `type` and `state_key` don't match those + specified by the [auth events + selection](/server-server-api#auth-events-selection) + algorithm described in the server specification. +3. If event does not have a `m.room.create` in its `auth_events`, + reject. +4. If type is `m.room.member`: + 1. If no `state_key` key or `membership` key in `content`, reject. + 2. If `content` has a `join_authorised_via_users_server` + key: + 1. If the event is not validly signed by the homeserver of the user ID denoted + by the key, reject. + 3. If `membership` is `join`: + 1. If the only previous event is an `m.room.create` and the + `state_key` is the creator, allow. + 2. If the `sender` does not match `state_key`, reject. + 3. If the `sender` is banned, reject. + 4. If the `join_rule` is `invite` or `knock` then allow if + membership state is `invite` or `join`. + 5. {{< changed-in this="true" >}} + If the `join_rule` is `restricted` or `knock_restricted`: + 1. If membership state is `join` or `invite`, allow. + 2. If the `join_authorised_via_users_server` key in `content` + is not a user with sufficient permission to invite other + users, reject. + 3. Otherwise, allow. + 6. If the `join_rule` is `public`, allow. + 7. Otherwise, reject. + 4. If `membership` is `invite`: + 1. If `content` has `third_party_invite` key: + 1. If *target user* is banned, reject. + 2. If `content.third_party_invite` does not have a `signed` + key, reject. + 3. If `signed` does not have `mxid` and `token` keys, + reject. + 4. If `mxid` does not match `state_key`, reject. + 5. If there is no `m.room.third_party_invite` event in the + current room state with `state_key` matching `token`, + reject. + 6. If `sender` does not match `sender` of the + `m.room.third_party_invite`, reject. + 7. If any signature in `signed` matches any public key in + the `m.room.third_party_invite` event, allow. The public + keys are in `content` of `m.room.third_party_invite` as: + 1. A single public key in the `public_key` field. + 2. A list of public keys in the `public_keys` field. + 8. Otherwise, reject. + 2. If the `sender`'s current membership state is not `join`, + reject. + 3. If *target user*'s current membership state is `join` or + `ban`, reject. + 4. If the `sender`'s power level is greater than or equal to + the *invite level*, allow. + 5. Otherwise, reject. + 5. If `membership` is `leave`: + 1. If the `sender` matches `state_key`, allow if and only if + that user's current membership state is `invite`, `join`, + or `knock`. + 2. If the `sender`'s current membership state is not `join`, + reject. + 3. If the *target user*'s current membership state is `ban`, + and the `sender`'s power level is less than the *ban level*, + reject. + 4. If the `sender`'s power level is greater than or equal to + the *kick level*, and the *target user*'s power level is + less than the `sender`'s power level, allow. + 5. Otherwise, reject. + 6. If `membership` is `ban`: + 1. If the `sender`'s current membership state is not `join`, + reject. + 2. If the `sender`'s power level is greater than or equal to + the *ban level*, and the *target user*'s power level is less + than the `sender`'s power level, allow. + 3. Otherwise, reject. + 7. If `membership` is `knock`: + 1. {{< changed-in this="true" >}} + If the `join_rule` is anything other than `knock` or + `knock_restricted`, reject. + 2. If `sender` does not match `state_key`, reject. + 3. If the `sender`'s current membership is not `ban`, `invite`, + or `join`, allow. + 4. Otherwise, reject. + 8. Otherwise, the membership is unknown. Reject. +5. If the `sender`'s current membership state is not `join`, reject. +6. If type is `m.room.third_party_invite`: + 1. Allow if and only if `sender`'s current power level is greater + than or equal to the *invite level*. +7. If the event type's *required power level* is greater than the + `sender`'s power level, reject. +8. If the event has a `state_key` that starts with an `@` and does not + match the `sender`, reject. +9. If type is `m.room.power_levels`: + 1. {{< added-in this="true" >}} + If any of the keys `users_default`, `events_default`, `state_default`, + `ban`, `redact`, `kick`, or `invite` in `content` are present and + not an integer, reject. + 2. {{< added-in this="true" >}} + If either of the keys `events` or `notifications` in `content` + are present and not a dictionary with values that are integers, + reject. + 3. If `users` key in `content` is not a dictionary with keys that + are valid user IDs with values that are integers, reject. + 2. If there is no previous `m.room.power_levels` event in the room, + allow. + 3. For the keys `users_default`, `events_default`, `state_default`, + `ban`, `redact`, `kick`, `invite` check if they were added, + changed or removed. For each found alteration: + 1. If the current value is higher than the `sender`'s current + power level, reject. + 2. If the new value is higher than the `sender`'s current power + level, reject. + 4. For each entry being added, changed or removed in both the + `events`, `users`, and `notifications` keys: + 1. If the current value is higher than the `sender`'s current + power level, reject. + 2. If the new value is higher than the `sender`'s current power + level, reject. + 5. For each entry being changed under the `users` key, other than + the `sender`'s own entry: + 1. If the current value is equal to the `sender`'s current + power level, reject. + 6. Otherwise, allow. +10. Otherwise, allow. + +{{% boxes/note %}} +Some consequences of these rules: + +- Unless you are a member of the room, the only permitted operations + (apart from the initial create/join) are: joining a public room; + accepting or rejecting an invitation to a room. +- To unban somebody, you must have power level greater than or equal + to both the kick *and* ban levels, *and* greater than the target + user's power level. +{{% /boxes/note %}} + +## Unchanged from v9 + +The following sections have not been modified since v9, but are included for +completeness. + +### Redactions + +{{% rver-fragment name="v9-redactions" %}} + +### Handling redactions + +{{% rver-fragment name="v3-handling-redactions" %}} + +### Event IDs + +{{% rver-fragment name="v4-event-ids" %}} + +### Event format + +{{% rver-fragment name="v4-event-format" %}} + +### State resolution + +{{% rver-fragment name="v2-state-res" %}} + +### Canonical JSON + +{{% rver-fragment name="v6-canonical-json" %}} + +### Signing key validity period + +{{% rver-fragment name="v5-signing-requirements" %}} diff --git a/content/rooms/v2.md b/content/rooms/v2.md index cdc59190..274e0ea0 100644 --- a/content/rooms/v2.md +++ b/content/rooms/v2.md @@ -46,6 +46,12 @@ Events in rooms of this version have the following structure: {{% definition path="api/server-server/definitions/pdu" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### Authorization rules {{% rver-fragment name="v1-auth-rules" %}} diff --git a/content/rooms/v3.md b/content/rooms/v3.md index 310c6625..8dd261e8 100644 --- a/content/rooms/v3.md +++ b/content/rooms/v3.md @@ -81,6 +81,12 @@ The complete structure of a event in a v3 room is shown below. {{% definition path="api/server-server/definitions/pdu_v3" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### Authorization rules {{% added-in this=true %}} `m.room.redaction` events are no longer diff --git a/content/rooms/v4.md b/content/rooms/v4.md index 01c695ba..c329f342 100644 --- a/content/rooms/v4.md +++ b/content/rooms/v4.md @@ -69,6 +69,12 @@ the changes in this room version. {{% rver-fragment name="v4-event-format" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### Authorization rules {{% rver-fragment name="v3-auth-rules" %}} diff --git a/content/rooms/v5.md b/content/rooms/v5.md index b4283071..25147e9e 100644 --- a/content/rooms/v5.md +++ b/content/rooms/v5.md @@ -51,6 +51,12 @@ completeness. {{% rver-fragment name="v4-event-format" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### Authorization rules {{% rver-fragment name="v3-auth-rules" %}} diff --git a/content/rooms/v6.md b/content/rooms/v6.md index cb87082c..0e2e70dc 100644 --- a/content/rooms/v6.md +++ b/content/rooms/v6.md @@ -212,6 +212,12 @@ completeness. {{% rver-fragment name="v4-event-format" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### State resolution {{% rver-fragment name="v2-state-res" %}} diff --git a/content/rooms/v7.md b/content/rooms/v7.md index 2ada06f7..5960c198 100644 --- a/content/rooms/v7.md +++ b/content/rooms/v7.md @@ -1,7 +1,7 @@ --- title: Room Version 7 type: docs -weight: 60 +weight: 70 --- This room version builds on [version 6](/rooms/v6) to introduce knocking @@ -205,6 +205,12 @@ completeness. {{% rver-fragment name="v4-event-format" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### State resolution {{% rver-fragment name="v2-state-res" %}} diff --git a/content/rooms/v8.md b/content/rooms/v8.md index 50a1f065..2a8cf125 100644 --- a/content/rooms/v8.md +++ b/content/rooms/v8.md @@ -1,7 +1,7 @@ --- title: Room Version 8 type: docs -weight: 60 +weight: 80 --- This room version builds on [version 7](/rooms/v7) to introduce a new @@ -110,6 +110,12 @@ completeness. {{% rver-fragment name="v4-event-format" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### State resolution {{% rver-fragment name="v2-state-res" %}} diff --git a/content/rooms/v9.md b/content/rooms/v9.md index 285a7640..d4da72de 100644 --- a/content/rooms/v9.md +++ b/content/rooms/v9.md @@ -1,7 +1,7 @@ --- title: Room Version 9 type: docs -weight: 60 +weight: 90 --- This room version builds on [version 8](/rooms/v8) to add additional redaction @@ -17,55 +17,7 @@ Clients which implement the redaction algorithm locally should refer to the ### Redactions -{{% added-in this=true %}} `m.room.member` now keep `join_authorised_via_users_server` -in addition to other keys in `content` when being redacted. - -{{% boxes/rationale %}} -Without the `join_authorised_via_users_server` property, redacted join events -can become invalid when verifying the auth chain of a given event, thus creating -a split-brain scenario where the user is able to speak from one server's -perspective but most others will continually reject their events. - -This can theoretically be worked around with a rejoin to the room, being careful -not to use the faulty events as `prev_events`, though instead it is encouraged -to use v9 rooms over v8 rooms to outright avoid the situation. - -[Issue #3373](https://github.com/matrix-org/matrix-doc/issues/3373) has further -information. -{{% /boxes/rationale %}} - -The full redaction algorithm follows. - -{{% rver-fragment name="v3-handling-redactions" %}} - -Upon receipt of a redaction event, the server must strip off any keys -not in the following list: - -- `event_id` -- `type` -- `room_id` -- `sender` -- `state_key` -- `content` -- `hashes` -- `signatures` -- `depth` -- `prev_events` -- `prev_state` -- `auth_events` -- `origin` -- `origin_server_ts` -- `membership` - -The content object must also be stripped of all keys, unless it is one -of one of the following event types: - -- `m.room.member` allows keys `membership`, `join_authorised_via_users_server`. -- `m.room.create` allows key `creator`. -- `m.room.join_rules` allows keys `join_rule`, `allow`. -- `m.room.power_levels` allows keys `ban`, `events`, `events_default`, - `kick`, `redact`, `state_default`, `users`, `users_default`. -- `m.room.history_visibility` allows key `history_visibility`. +{{% rver-fragment name="v9-redactions" withVersioning="true" %}} ## Server implementation components @@ -102,6 +54,12 @@ completeness. {{% rver-fragment name="v4-event-format" %}} +#### Deprecated event content schemas + +{{% rver-fragment name="v1-deprecated-formatting-off-spec" %}} + +{{% rver-fragment name="v1-stringy-power-levels" %}} + ### Authorization rules {{% rver-fragment name="v8-auth-rules" %}} diff --git a/content/server-server-api.md b/content/server-server-api.md index f70e8994..12d4bc45 100644 --- a/content/server-server-api.md +++ b/content/server-server-api.md @@ -432,6 +432,16 @@ unspecified. For an `m.room.member` state event, the user given by the `state_key` of the event. +{{% boxes/warning %}} +Some [room versions](/rooms) accept power level values to be represented as +strings rather than integers. This is strictly for backwards compatibility. +A homeserver should take reasonable precautions to prevent users from sending +new power level events with string values (eg: by rejecting the API request), +and must never populate the default power levels in a room as string values. + +See the [room version specification](/rooms) for more information. +{{% /boxes/warning %}} + #### Authorization rules The rules governing whether an event is authorized depends on a set of diff --git a/data/event-schemas/schema/m.room.join_rules.yaml b/data/event-schemas/schema/m.room.join_rules.yaml index dbac60d5..d5f8eb2d 100644 --- a/data/event-schemas/schema/m.room.join_rules.yaml +++ b/data/event-schemas/schema/m.room.join_rules.yaml @@ -12,6 +12,10 @@ description: | * `restricted` - anyone able to satisfy at least one of the allow conditions is able to join the room without prior action. Otherwise, an invite is required. Only available in rooms [which support the join rule](/rooms/#feature-matrix). + * `knock_restricted` - a user can request an invite using the same functions offered + by the `knock` join rule, or can attempt to join having satisfied an allow condition + per the `restricted` join rule. Only available in rooms + [which support the join rule](/rooms/#feature-matrix). * `private` - reserved without implementation. No significant meaning. properties: content: From c4db688af865892857242d4ec810598a8eb400c8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 8 Jun 2022 15:32:55 -0600 Subject: [PATCH 12/17] Spec `/relations` and aggregations (#1062) * Commit to show changes to rich replies section * Move rich replies to a module * Add remainder of MSC2674 * Pivot away from MSC3440: Threads * Add changelog entries so far * Make a note for why we have aggregations/relations if nothing uses it * Outright remove threads references Apparently this breaks the table of contents * Define MSC2675 * Define MSC3666 * Add note for rich replies? * Update content/client-server-api/_index.md Co-authored-by: Patrick Cloke * Clarify how ignoring works for aggregations. * Try to clarify redactions a bit * Clarify using parent/child language * Add missing bits of MSC2675 * Add changelog for aggregations * Appease the linters * Update data/api/client-server/relations.yaml Co-authored-by: Patrick Cloke * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Try to clarify the return of /relations * Fix required attribute * Fix wording round 1 * Try to fix pagination * Copy/paste the endpoint to make Open API happy * Fix code block examples for rich replies * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Apply suggestions on all 3 endpoints * Fix description of relationships API * Fix warning about server-side aggregation/bundling Co-authored-by: Patrick Cloke Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- .../newsfragments/1062.feature.1 | 1 + .../newsfragments/1062.feature.2 | 1 + .../newsfragments/1062.feature.3 | 1 + content/client-server-api/_index.md | 222 +++++++++ .../modules/instant_messaging.md | 163 ------- .../client-server-api/modules/rich_replies.md | 182 ++++++++ .../definitions/m.relates_to.yaml | 43 ++ data/api/client-server/relations.yaml | 425 ++++++++++++++++++ 8 files changed, 875 insertions(+), 163 deletions(-) create mode 100644 changelogs/client_server/newsfragments/1062.feature.1 create mode 100644 changelogs/client_server/newsfragments/1062.feature.2 create mode 100644 changelogs/client_server/newsfragments/1062.feature.3 create mode 100644 content/client-server-api/modules/rich_replies.md create mode 100644 data/api/client-server/definitions/m.relates_to.yaml create mode 100644 data/api/client-server/relations.yaml diff --git a/changelogs/client_server/newsfragments/1062.feature.1 b/changelogs/client_server/newsfragments/1062.feature.1 new file mode 100644 index 00000000..6c669e43 --- /dev/null +++ b/changelogs/client_server/newsfragments/1062.feature.1 @@ -0,0 +1 @@ +Relax the restrictions on Rich Replies, as per [MSC3676](https://github.com/matrix-org/matrix-spec-proposals/pull/3676). \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1062.feature.2 b/changelogs/client_server/newsfragments/1062.feature.2 new file mode 100644 index 00000000..8234ef2a --- /dev/null +++ b/changelogs/client_server/newsfragments/1062.feature.2 @@ -0,0 +1 @@ +Describe a structured system for event relationships, as per [MSC2674](https://github.com/matrix-org/matrix-spec-proposals/pull/2674). \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1062.feature.3 b/changelogs/client_server/newsfragments/1062.feature.3 new file mode 100644 index 00000000..d2a376af --- /dev/null +++ b/changelogs/client_server/newsfragments/1062.feature.3 @@ -0,0 +1 @@ +Describe how relationships between events can be "aggregated", as per [MSC2675](https://github.com/matrix-org/matrix-spec-proposals/pull/2675) and [MSC3666](https://github.com/matrix-org/matrix-spec-proposals/pull/3666). \ No newline at end of file diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 67bca5e5..00e3f4ad 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1798,6 +1798,16 @@ There are several APIs provided to `GET` events for a room: ### Sending events to a room +{{% boxes/note %}} +{{% added-in v="1.3" %}} + +Servers might need to post-process some events if they +[relate to](#forming-relationships-between-events) another event. The event's +relationship type (`rel_type`) determines any restrictions which might apply, +such as the user only being able to send one event of a given type in relation +to another. +{{% /boxes/note %}} + {{% http-api spec="client-server" api="room_state" %}} **Examples** @@ -1888,6 +1898,216 @@ the topic to be removed from the room. {{% http-api spec="client-server" api="redaction" %}} +### Forming relationships between events + +{{% changed-in v="1.3" %}} + +In some cases it is desirable to logically associate one event's contents with +another event's contents — for example, when replying to a message, editing an +event, or simply looking to add context for an event's purpose. + +Events are related to each other in a parent/child structure, where any event can +become a parent by simply having a child event point at it. Parent events do not +define their children, instead relying on the children to describe their parent. + +The relationship between a child and its parent event is described in the child +event's `content` as `m.relates_to` (defined below). A child event can point at +any other event, including another child event, to build the relationship so long +as both events are in the same room, however additional restrictions might be imposed +by the type of the relationship (the `rel_type`). + +{{% boxes/note %}} +Child events can point at other child events, forming a chain of events. These chains +can naturally take the shape of a tree if two independent children point at a single +parent event, for example. +{{% /boxes/note %}} + +To allow the server to aggregate and find child events for a parent, the `m.relates_to` +key of an event MUST be included in the plaintext copy of the event. It cannot be +exclusively recorded in the encrypted payload as the server cannot decrypt the event +for processing. + +{{% boxes/warning %}} +If an encrypted event contains an `m.relates_to` in its payload, it should be +ignored and instead favour the plaintext `m.relates_to` copy (including when there +is no plaintext copy). This is to ensure the client's behaviour matches the server's +capability to handle relationships. +{{% /boxes/warning %}} + +Relationships which don't match the schema, or which break the rules of a relationship, +are simply ignored. An example might be the parent and child being in different +rooms, or the relationship missing properties required by the schema below. Clients +handling such invalid relationships should show the events independently of each +other, optionally with an error message. + +{{% boxes/note %}} +While this specification describes an `m.relates_to` object containing a `rel_type`, there +is not currently any relationship type which uses this structure. Replies, described below, +form their relationship outside of the `rel_type` as a legacy type of relationship. Future +versions of the specification might change replies to better match the relationship structures. + +Custom `rel_type`s can, and should, still use the schema described above for relevant +behaviour. +{{% /boxes/note %}} + +`m.relates_to` is defined as follows: + +{{% definition path="api/client-server/definitions/m.relates_to" %}} + +#### Relationship types + +This specification describes the following relationship types: + +* [Rich replies](#rich-replies) (**Note**: does not use `rel_type`). + +#### Aggregations + +{{% added-in v="1.3" %}} + +Some child events can be "aggregated" by the server, depending on their +`rel_type`. This can allow a set of child events to be summarised to the client without +the client needing the child events themselves. + +An example of this might be that a `rel_type` requires an extra `key` field which, when +appropriately specified, would mean that the client receives a total count for the number +of times that `key` was used by child events. + +The actual aggregation format depends on the `rel_type`. + +{{% boxes/note %}} +This specification does not currently describe any `rel_type`s which require +aggregation. This functionality forms a framework for future extensions. +{{% /boxes/note %}} + +Aggregations are sometimes automatically included by a server alongside the parent +event. This is known as a "bundled aggregation" or "bundle" for simplicity. The +act of doing this is "bundling". + +When an event is served to the client through the APIs listed below, a `m.relations` property +is included under `unsigned` if the event has child events which can be aggregated and point +at it. The `m.relations` property is an object keyed by `rel_type` and value being the type-specific +aggregated format for that `rel_type`, also known as the bundle. + +For example (unimportant fields not included): + +```json +{ + "event_id": "$my_event", + "unsigned": { + "m.relations": { + "org.example.possible_annotations": [ + { + "key": "👍", + "origin_server_ts": 1562763768320, + "count": 3 + }, + { + "key": "👎", + "origin_server_ts": 1562763768320, + "count": 1 + } + ], + "org.example.possible_thread": { + "current_server_participated": true, + "count": 7, + "latest_event": { + "event_id": "$another_event", + "content": { + "body": "Hello world" + } + } + } + } + } +} +``` + +Note how the `org.example.possible_annotations` bundle is an array compared to the +`org.example.possible_thread` bundle where the server is summarising the state of +the relationship in a single object. Both are valid ways to aggregate, and their +exact types depend on the `rel_type`. + +{{% boxes/warning %}} +State events do not currently receive bundled aggregations. This is not +necessarily a deliberate design decision, and MSCs which aim to fix this are welcome. +{{% /boxes/warning %}} + +The endpoints where the server *should* include bundled aggregations are: + +* [`GET /rooms/{roomId}/messages`](#get_matrixclientv3roomsroomidmessages) +* [`GET /rooms/{roomId}/context/{eventId}`](#get_matrixclientv3roomsroomidcontexteventid) +* [`GET /rooms/{roomId}/event/{eventId}`](#get_matrixclientv3roomsroomideventeventid) +* [`GET /rooms/{roomId}/relations/{eventId}`](#get_matrixclientv1roomsroomidrelationseventid) +* [`GET /rooms/{roomId}/relations/{eventId}/{relType}`](#get_matrixclientv1roomsroomidrelationseventidreltype) +* [`GET /rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`](#get_matrixclientv1roomsroomidrelationseventidreltypeeventtype) +* [`GET /sync`](#get_matrixclientv3sync) when the relevant section has a `limited` value + of `true`. +* [`POST /search`](#post_matrixclientv3search) for any matching events under `room_events`. + +{{% boxes/note %}} +The server is **not** required to return bundled aggregations on deprecated endpoints +such as `/initialSync`. +{{% /boxes/note %}} + +While this functionality allows the client to see what was known to the server at the +time of handling, the client should continue to aggregate locally if it is aware of +the relationship type's behaviour. For example, a client might increment a `count` +on a parent event's bundle if it saw a new child event which referenced that parent. + +The bundle provided by the server only includes child events which were known at the +time the client would receive the bundle. For example, in a single `/sync` response +with the parent and multiple child events the child events would have already been +included on the parent's `m.relations` field. Events received in future syncs would +need to be aggregated manually by the client. + +{{% boxes/note %}} +Events from [ignored users](#ignoring-users) do not appear in the aggregation +from the server, however clients might still have events from ignored users cached. Like +with normal events, clients will need to de-aggregate child events sent by ignored users to +avoid them being considered in counts. Servers must additionally ensure they do not +consider child events from ignored users when preparing a bundle for the client. +{{% /boxes/note %}} + +When a parent event is redacted, the child events which pointed to that parent remain, however +when a child event is redacted then the relationship is broken. Therefore, the server needs +to de-aggregate or disassociate the event once the relationship is lost. Clients with local +aggregation or which handle redactions locally should do the same. + +It is suggested that clients perform local echo on aggregations — for instance, aggregating +a new child event into a bundle optimistically until the server returns a failure or the client +gives up on sending the event, at which point the event should be de-aggregated and an +error or similar shown. The client should be cautious to not aggregate an event twice if +it has already optimistically aggregated the event. Clients are encouraged to take this +a step further to additionally track child events which target unsent/pending events, +likely using the transaction ID as a temporary event ID until a proper event ID is known. + +{{% boxes/warning %}} +Due to history visibility restrictions, child events might not be visible to the user +if they are in a section of history the user cannot see. This means any bundles which would +normally include those events will be lacking them and the client will not be able to +locally aggregate the events either — relating events of importance (such as votes) should +take into consideration history visibility. + +Additionally, if the server is missing portions of the room history then it may not be +able to accurately aggregate the events. +{{% /boxes/warning %}} + +#### Relationships API + +{{% added-in v="1.3" %}} + +To retrieve the child events for a parent from the server, the client can call the +following endpoint. + +This endpoint is particularly useful if the client has lost context on the aggregation for +a parent event and needs to rebuild/verify it. + +{{% boxes/note %}} +Because replies do not use `rel_type`, they will not be accessible via this API. +{{% /boxes/note %}} + +{{% http-api spec="client-server" api="relations" %}} + ## Rooms ### Types @@ -2294,6 +2514,7 @@ that profile. | Module / Profile | Web | Mobile | Desktop | CLI | Embedded | |------------------------------------------------------------|-----------|----------|----------|----------|----------| | [Instant Messaging](#instant-messaging) | Required | Required | Required | Required | Optional | +| [Rich replies](#rich-replies) | Optional | Optional | Optional | Optional | Optional | | [Direct Messaging](#direct-messaging) | Required | Required | Required | Required | Optional | | [Mentions](#user-room-and-group-mentions) | Required | Required | Required | Optional | Optional | | [Presence](#presence) | Required | Required | Required | Required | Optional | @@ -2373,6 +2594,7 @@ applications, they are not intended to be fully-fledged communication systems. {{% cs-module name="instant_messaging" %}} +{{% cs-module name="rich_replies" %}} {{% cs-module name="voip_events" %}} {{% cs-module name="typing_notifications" %}} {{% cs-module name="receipts" %}} diff --git a/content/client-server-api/modules/instant_messaging.md b/content/client-server-api/modules/instant_messaging.md index e2138e0b..7f9b3ca1 100644 --- a/content/client-server-api/modules/instant_messaging.md +++ b/content/client-server-api/modules/instant_messaging.md @@ -287,169 +287,6 @@ when using the `m.heroes` to calculate the name. Clients SHOULD use minimum 5 heroes to calculate room names where possible, but may use more or less to fit better with their user experience. -##### Rich replies - -In some cases, events may wish to reference other events. This could be -to form a thread of messages for the user to follow along with, or to -provide more context as to what a particular event is describing. -Currently, the only kind of relation defined is a "rich reply" where a -user may reference another message to create a thread-like conversation. - -Relationships are defined under an `m.relates_to` key in the event's -`content`. If the event is of the type `m.room.encrypted`, the -`m.relates_to` key MUST NOT be covered by the encryption and instead be -put alongside the encryption information held in the `content`. - -A rich reply is formed through use of an `m.relates_to` relation for -`m.in_reply_to` where a single key, `event_id`, is used to reference the -event being replied to. The referenced event ID SHOULD belong to the -same room where the reply is being sent. Clients should be cautious of -the event ID belonging to another room, or being invalid entirely. Rich -replies can only be constructed in the form of `m.room.message` events -with a `msgtype` of `m.text` or `m.notice`. Due to the fallback -requirements, rich replies cannot be constructed for types of `m.emote`, -`m.file`, etc. Rich replies may reference any other `m.room.message` -event, however. Rich replies may reference another event which also has -a rich reply, infinitely. - -An `m.in_reply_to` relationship looks like the following: - -``` -{ - ... - "type": "m.room.message", - "content": { - "msgtype": "m.text", - "body": "", - "format": "org.matrix.custom.html", - "formatted_body": "", - "m.relates_to": { - "m.in_reply_to": { - "event_id": "$another:event.com" - } - } - } -} -``` - -##### Fallbacks for rich replies - -Some clients may not have support for rich replies and therefore need a -fallback to use instead. Clients that do not support rich replies should -render the event as if rich replies were not special. - -Clients that do support rich replies MUST provide the fallback format on -replies, and MUST strip the fallback before rendering the reply. Rich -replies MUST have a `format` of `org.matrix.custom.html` and therefore a -`formatted_body` alongside the `body` and appropriate `msgtype`. The -specific fallback text is different for each `msgtype`, however the -general format for the `body` is: - - > <@alice:example.org> This is the original body - - This is where the reply goes - -The `formatted_body` should use the following template: - - -
- In reply to - @alice:example.org -
- -
-
- This is where the reply goes. - -If the related event does not have a `formatted_body`, the event's -`body` should be considered after encoding any HTML special characters. -Note that the `href` in both of the anchors use a [matrix.to -URI](/appendices#matrixto-navigation). - -###### Stripping the fallback - -Clients which support rich replies MUST strip the fallback from the -event before rendering the event. This is because the text provided in -the fallback cannot be trusted to be an accurate representation of the -event. After removing the fallback, clients are recommended to represent -the event referenced by `m.in_reply_to` similar to the fallback's -representation, although clients do have creative freedom for their user -interface. Clients should prefer the `formatted_body` over the `body`, -just like with other `m.room.message` events. - -To strip the fallback on the `body`, the client should iterate over each -line of the string, removing any lines that start with the fallback -prefix ("> ", including the space, without quotes) and stopping when -a line is encountered without the prefix. This prefix is known as the -"fallback prefix sequence". - -To strip the fallback on the `formatted_body`, the client should remove -the entirety of the `mx-reply` tag. - -###### Fallback for `m.text`, `m.notice`, and unrecognised message types - -Using the prefix sequence, the first line of the related event's `body` -should be prefixed with the user's ID, followed by each line being -prefixed with the fallback prefix sequence. For example: - - > <@alice:example.org> This is the first line - > This is the second line - - This is the reply - -The `formatted_body` uses the template defined earlier in this section. - -###### Fallback for `m.emote` - -Similar to the fallback for `m.text`, each line gets prefixed with the -fallback prefix sequence. However an asterisk should be inserted before -the user's ID, like so: - - > * <@alice:example.org> feels like today is going to be a great day - - This is the reply - -The `formatted_body` has a subtle difference for the template where the -asterisk is also inserted ahead of the user's ID: - - -
- In reply to - * @alice:example.org -
- -
-
- This is where the reply goes. - -###### Fallback for `m.image`, `m.video`, `m.audio`, and `m.file` - -The related event's `body` would be a file name, which may not be very -descriptive. The related event should additionally not have a `format` -or `formatted_body` in the `content` - if the event does have a `format` -and/or `formatted_body`, those fields should be ignored. Because the -filename alone may not be descriptive, the related event's `body` should -be considered to be `"sent a file."` such that the output looks similar -to the following: - - > <@alice:example.org> sent a file. - - This is the reply - - -
- In reply to - @alice:example.org -
- sent a file. -
-
- This is where the reply goes. - -For `m.image`, the text should be `"sent an image."`. For `m.video`, the -text should be `"sent a video."`. For `m.audio`, the text should be -`"sent an audio file"`. - ##### Spoiler messages {{% added-in v="1.1" %}} diff --git a/content/client-server-api/modules/rich_replies.md b/content/client-server-api/modules/rich_replies.md new file mode 100644 index 00000000..60c02363 --- /dev/null +++ b/content/client-server-api/modules/rich_replies.md @@ -0,0 +1,182 @@ +--- +type: module +--- + +### Rich replies + +{{% changed-in v="1.3" %}} + +Rich replies are a +special kind of [relationship](#forming-relationships-between-events) which +effectively quotes the referenced event for the client to render/process how +it wishes. They are normally used with [`m.room.message`](#mroommessage) events. + +{{% boxes/note %}} +Until v1.3 of the spec, rich replies were limited to `m.room.message` events +which could represent an HTML-formatted body. As of v1.3 this is now expanded +to *all* event types by dropping the requirement that an HTML-formatted body +be included. + +Additionally, a rich reply can reference any other event type as of v1.3. +Previously, a rich reply could only reference another `m.room.message` event. +{{% /boxes/note %}} + +When possible, events SHOULD include a [fallback representation](#fallbacks-for-rich-replies) +to allow clients which do not render rich replies to still see something which +appears to be a quoted reply. + +Though rich replies form a relationship to another event, they do not +use `rel_type` to create this relationship. Instead, a subkey named `m.in_reply_to` +is used to describe the reply's relationship, leaving the other properties of +`m.relates_to` to describe the primary relationship of the event. This means +that if an event is simply in reply to another event, without further relationship, +the `rel_type` and `event_id` properties of `m.relates_to` become *optional*. + +An example reply would be: + +```json5 +{ + "content": { + "m.relates_to": { + "m.in_reply_to": { + "event_id": "$another_event" + } + }, + "body": "That sounds like a great idea!" + }, + // other fields as required by events +} +``` + +Note that the `event_id` of the `m.in_reply_to` object has the same requirements +as if it were to be under `m.relates_to` directly instead. + +#### Fallbacks for rich replies + +Some clients may not have support for rich replies and therefore need a +fallback to use instead. Clients that do not support rich replies should +render the event as if rich replies were not special. + +Clients that do support rich replies SHOULD provide the fallback format on +replies, and MUST strip the fallback before rendering the reply. The +specific fallback text is different for each `msgtype`, however the +general format for the `body` is: + +```text +> <@alice:example.org> This is the original body + +This is where the reply goes +``` + +The `formatted_body`, if present and using an associated `format` of +`org.matrix.custom.html`, should use the following template: + +```html + +
+ In reply to + @alice:example.org +
+ +
+
+This is where the reply goes. +``` + +If the related event does not have a `formatted_body`, the event's +`body` should be considered after encoding any HTML special characters. +Note that the `href` in both of the anchors use a [matrix.to +URI](/appendices#matrixto-navigation). + +##### Stripping the fallback + +Clients which support rich replies MUST strip the fallback from the +event before rendering the event. This is because the text provided in +the fallback cannot be trusted to be an accurate representation of the +event. After removing the fallback, clients are recommended to represent +the event referenced by `m.in_reply_to` similar to the fallback's +representation, although clients do have creative freedom for their user +interface. Clients should prefer the `formatted_body` over the `body`, +just like with other `m.room.message` events. + +To strip the fallback on the `body`, the client should iterate over each +line of the string, removing any lines that start with the fallback +prefix ("> ", including the space, without quotes) and stopping when +a line is encountered without the prefix. This prefix is known as the +"fallback prefix sequence". + +To strip the fallback on the `formatted_body`, the client should remove +the entirety of the `mx-reply` tag. + +##### Fallback for `m.text`, `m.notice`, and unrecognised message types + +Using the prefix sequence, the first line of the related event's `body` +should be prefixed with the user's ID, followed by each line being +prefixed with the fallback prefix sequence. For example: + +```text +> <@alice:example.org> This is the first line +> This is the second line + +This is the reply +``` + +The `formatted_body` uses the template defined earlier in this section. + +##### Fallback for `m.emote` + +Similar to the fallback for `m.text`, each line gets prefixed with the +fallback prefix sequence. However an asterisk should be inserted before +the user's ID, like so: + +```text +> * <@alice:example.org> feels like today is going to be a great day + +This is the reply +``` + +The `formatted_body` has a subtle difference for the template where the +asterisk is also inserted ahead of the user's ID: + +```html + +
+ In reply to + * @alice:example.org +
+ +
+
+This is where the reply goes. +``` + +##### Fallback for `m.image`, `m.video`, `m.audio`, and `m.file` + +The related event's `body` would be a file name, which may not be very +descriptive. The related event should additionally not have a `format` +or `formatted_body` in the `content` - if the event does have a `format` +and/or `formatted_body`, those fields should be ignored. Because the +filename alone may not be descriptive, the related event's `body` should +be considered to be `"sent a file."` such that the output looks similar +to the following: + +```text +> <@alice:example.org> sent a file. + +This is the reply +``` +```html + +
+ In reply to + @alice:example.org +
+ sent a file. +
+
+This is where the reply goes. +``` + +For `m.image`, the text should be `"sent an image."`. For `m.video`, the +text should be `"sent a video."`. For `m.audio`, the text should be +`"sent an audio file"`. \ No newline at end of file diff --git a/data/api/client-server/definitions/m.relates_to.yaml b/data/api/client-server/definitions/m.relates_to.yaml new file mode 100644 index 00000000..591281c3 --- /dev/null +++ b/data/api/client-server/definitions/m.relates_to.yaml @@ -0,0 +1,43 @@ +# Copyright 2022 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: m.relates_to +description: |- + Describes the relationship of an event to its parent. This is contained + within the event's `content` alongside other fields for the relevant event type. +example: { + # We deliberately "break" the example by including the top-level field so it renders + # sensibly for readers of the spec. + "m.relates_to": { + "rel_type": "org.example.relationship", + "event_id": "$an_event" + } +} +properties: + rel_type: + type: string + description: |- + The namespaced relationship type. Values must use the + [Common Namespaced Identifier Grammar](/appendices/#common-namespaced-identifier-grammar). + + The relationship type determines how clients should perceive the event, and in what + context. Some relationship types are processed server-side for "bundling", though not + all relationships require such behaviour. For example, an `m.thread` relationship type + might denote that the event is part of a "thread" of messages and should be rendered as + such. + event_id: + type: string + description: The event ID of the event that this event relates to. +required: ['rel_type', 'event_id'] diff --git a/data/api/client-server/relations.yaml b/data/api/client-server/relations.yaml new file mode 100644 index 00000000..dc0d8761 --- /dev/null +++ b/data/api/client-server/relations.yaml @@ -0,0 +1,425 @@ +# Copyright 2022 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Client-Server Relations API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/rooms/{roomId}/relations/{eventId}": + get: + summary: Get the child events for a given parent event. + description: |- + Retrieve all of the child events for a given parent event. + + Note that when paginating the `from` token should be "after" the `to` token in + terms of topological ordering, because it is only possible to paginate "backwards" + through events, starting at `from`. + + For example, passing a `from` token from page 2 of the results, and a `to` token + from page 1, would return the empty set. The caller can use a `from` token from + page 1 and a `to` token from page 2 to paginate over the same range, however. + operationId: getRelatingEvents + security: + - accessToken: [] + parameters: + - in: path + type: string + name: roomId + description: The ID of the room containing the parent event. + required: true + x-example: "!636q39766251:matrix.org" + - in: path + type: string + name: eventId + description: The ID of the parent event whose child events are to be returned. + required: true + x-example: "$asfDuShaf7Gafaw" + - in: query + type: string + name: from + description: |- + The pagination token to start returning results from. If not supplied, results + start at the most recent topological event known to the server. + + Can be a `next_batch` token from a previous call, or a returned + `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), + or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). + required: false + x-example: "page2_token" + - in: query + type: string + name: to + description: |- + The pagination token to stop returning results at. If not supplied, results + continue up to `limit` or until there are no more events. + + Like `from`, this can be a previous token from a prior call to this endpoint + or from `/messages` or `/sync`. + required: false + x-example: "page3_token" + - in: query + type: integer + name: limit + description: |- + The maximum number of results to return in a single `chunk`. The server can + and should apply a maximum value to this parameter to avoid large responses. + + Similarly, the server should apply a default value when not supplied. + required: false + x-example: 20 + responses: + # note: this endpoint deliberately does not support rate limiting, therefore a + # 429 error response is not included. + + 200: + description: |- + The paginated child events which point to the parent. If no events are + pointing to the parent or the pagination yields no results, an empty `chunk` + is returned. + examples: + application/json: { + "chunk": [{ + "room_id": "!636q39766251:matrix.org", + "$ref": "../../event-schemas/examples/m.room.message$m.text.yaml", + "content": { + "m.relates_to": { + "rel_type": "org.example.my_relation", + "event_id": "$asfDuShaf7Gafaw" + } + } + }], + "next_batch": "page2_token", + "prev_batch": "page1_token" + } + schema: + type: object + properties: + chunk: + title: "ChildEventsChunk" + type: array + description: |- + The child events of the requested event, ordered topologically most-recent first. + items: + allOf: + - "$ref": "definitions/client_event.yaml" + next_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means there are no more results to fetch and the client should stop paginating. + prev_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means this is the start of the result set, i.e. this is the first batch/page. + required: ['chunk'] + 404: + description: |- + The parent event was not found or the user does not have permission to read + this event (it might be contained in history that is not accessible to the user). + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Event not found." + } + schema: + "$ref": "definitions/errors/error.yaml" + tags: + - Event relationships + # The same as above, with added `/{relType}` + "/rooms/{roomId}/relations/{eventId}/{relType}": + get: + summary: Get the child events for a given parent event, with a given `relType`. + description: |- + Retrieve all of the child events for a given parent event which relate to the parent + using the given `relType`. + + Note that when paginating the `from` token should be "after" the `to` token in + terms of topological ordering, because it is only possible to paginate "backwards" + through events, starting at `from`. + + For example, passing a `from` token from page 2 of the results, and a `to` token + from page 1, would return the empty set. The caller can use a `from` token from + page 1 and a `to` token from page 2 to paginate over the same range, however. + operationId: getRelatingEventsWithRelType + security: + - accessToken: [] + parameters: + - in: path + type: string + name: roomId + description: The ID of the room containing the parent event. + required: true + x-example: "!636q39766251:matrix.org" + - in: path + type: string + name: eventId + description: The ID of the parent event whose child events are to be returned. + required: true + x-example: "$asfDuShaf7Gafaw" + - in: path + type: string + name: relType + description: |- + The [relationship type](/client-server-api/#relationship-types) to search for. + required: true + x-example: "org.example.my_relation" + - in: query + type: string + name: from + description: |- + The pagination token to start returning results from. If not supplied, results + start at the most recent topological event known to the server. + + Can be a `next_batch` token from a previous call, or a returned + `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), + or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). + required: false + x-example: "page2_token" + - in: query + type: string + name: to + description: |- + The pagination token to stop returning results at. If not supplied, results + continue up to `limit` or until there are no more events. + + Like `from`, this can be a previous token from a prior call to this endpoint + or from `/messages` or `/sync`. + required: false + x-example: "page3_token" + - in: query + type: integer + name: limit + description: |- + The maximum number of results to return in a single `chunk`. The server can + and should apply a maximum value to this parameter to avoid large responses. + + Similarly, the server should apply a default value when not supplied. + required: false + x-example: 20 + responses: + # note: this endpoint deliberately does not support rate limiting, therefore a + # 429 error response is not included. + + 200: + description: |- + The paginated child events which point to the parent. If no events are + pointing to the parent or the pagination yields no results, an empty `chunk` + is returned. + examples: + application/json: { + "chunk": [{ + "room_id": "!636q39766251:matrix.org", + "$ref": "../../event-schemas/examples/m.room.message$m.text.yaml", + "content": { + "m.relates_to": { + "rel_type": "org.example.my_relation", + "event_id": "$asfDuShaf7Gafaw" + } + } + }], + "next_batch": "page2_token", + "prev_batch": "page1_token" + } + schema: + type: object + properties: + chunk: + title: "ChildEventsChunk" + type: array + description: |- + The child events of the requested event, ordered topologically + most-recent first. The events returned will match the `relType` + supplied in the URL. + items: + allOf: + - "$ref": "definitions/client_event.yaml" + next_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means there are no more results to fetch and the client should stop paginating. + prev_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means this is the start of the result set, i.e. this is the first batch/page. + required: ['chunk'] + 404: + description: |- + The parent event was not found or the user does not have permission to read + this event (it might be contained in history that is not accessible to the user). + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Event not found." + } + schema: + "$ref": "definitions/errors/error.yaml" + tags: + - Event relationships + # The same as above, with added `/{eventType}` + "/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}": + get: + summary: Get the child events for a given parent event, with a given `relType` and `eventType`. + description: |- + Retrieve all of the child events for a given parent event which relate to the parent + using the given `relType` and have the given `eventType`. + + Note that when paginating the `from` token should be "after" the `to` token in + terms of topological ordering, because it is only possible to paginate "backwards" + through events, starting at `from`. + + For example, passing a `from` token from page 2 of the results, and a `to` token + from page 1, would return the empty set. The caller can use a `from` token from + page 1 and a `to` token from page 2 to paginate over the same range, however. + operationId: getRelatingEventsWithRelTypeAndEventType + security: + - accessToken: [] + parameters: + - in: path + type: string + name: roomId + description: The ID of the room containing the parent event. + required: true + x-example: "!636q39766251:matrix.org" + - in: path + type: string + name: eventId + description: The ID of the parent event whose child events are to be returned. + required: true + x-example: "$asfDuShaf7Gafaw" + - in: path + type: string + name: relType + description: |- + The [relationship type](/client-server-api/#relationship-types) to search for. + required: true + x-example: "org.example.my_relation" + - in: path + type: string + name: eventType + description: |- + The event type of child events to search for. + + Note that in encrypted rooms this will typically always be `m.room.encrypted` + regardless of the event type contained within the encrypted payload. + required: true + x-example: "m.room.message" + - in: query + type: string + name: from + description: |- + The pagination token to start returning results from. If not supplied, results + start at the most recent topological event known to the server. + + Can be a `next_batch` token from a previous call, or a returned + `start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages), + or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync). + required: false + x-example: "page2_token" + - in: query + type: string + name: to + description: |- + The pagination token to stop returning results at. If not supplied, results + continue up to `limit` or until there are no more events. + + Like `from`, this can be a previous token from a prior call to this endpoint + or from `/messages` or `/sync`. + required: false + x-example: "page3_token" + - in: query + type: integer + name: limit + description: |- + The maximum number of results to return in a single `chunk`. The server can + and should apply a maximum value to this parameter to avoid large responses. + + Similarly, the server should apply a default value when not supplied. + required: false + x-example: 20 + responses: + # note: this endpoint deliberately does not support rate limiting, therefore a + # 429 error response is not included. + + 200: + description: |- + The paginated child events which point to the parent. If no events are + pointing to the parent or the pagination yields no results, an empty `chunk` + is returned. + examples: + application/json: { + "chunk": [{ + "room_id": "!636q39766251:matrix.org", + "$ref": "../../event-schemas/examples/m.room.message$m.text.yaml", + "content": { + "m.relates_to": { + "rel_type": "org.example.my_relation", + "event_id": "$asfDuShaf7Gafaw" + } + } + }], + "next_batch": "page2_token", + "prev_batch": "page1_token" + } + schema: + type: object + properties: + chunk: + title: "ChildEventsChunk" + type: array + description: |- + The child events of the requested event, ordered topologically most-recent + first. The events returned will match the `relType` and `eventType` supplied + in the URL. + items: + allOf: + - "$ref": "definitions/client_event.yaml" + next_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means there are no more results to fetch and the client should stop paginating. + prev_batch: + type: string + description: |- + An opaque string representing a pagination token. The absence of this token + means this is the start of the result set, i.e. this is the first batch/page. + required: ['chunk'] + 404: + description: |- + The parent event was not found or the user does not have permission to read + this event (it might be contained in history that is not accessible to the user). + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Event not found." + } + schema: + "$ref": "definitions/errors/error.yaml" + tags: + - Event relationships + From 926c6bad6160e6b4f936a8c9266558e01aa955dd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 9 Jun 2022 02:24:41 -0600 Subject: [PATCH 13/17] Deprecate the `sender_key` and `device_id` on Megolm events (#1101) * Deprecate the `sender_key` and `device_id` on Megolm events MSC: https://github.com/matrix-org/matrix-spec-proposals/pull/3700 ([Markdown](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3700-deprecate-sender-key.md)) The language around `m.room.encrypted` is a bit awkward because *technically* you can use the event to represent non-Megolm events, however that's considered an edge case at this time. * changelog * Apply wording changes * Remove incorrect example * Add missing sentence --- .../newsfragments/1101.deprecation | 1 + .../modules/end_to_end_encryption.md | 50 +++++++++++++++++-- .../schema/m.room.encrypted.yaml | 30 +++++++++-- .../schema/m.room_key_request.yaml | 12 ++++- 4 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 changelogs/client_server/newsfragments/1101.deprecation diff --git a/changelogs/client_server/newsfragments/1101.deprecation b/changelogs/client_server/newsfragments/1101.deprecation new file mode 100644 index 00000000..922a2a0a --- /dev/null +++ b/changelogs/client_server/newsfragments/1101.deprecation @@ -0,0 +1 @@ +Deprecate the `sender_key` and `device_id` on `m.megolm.v1.aes-sha2` events, and the `sender_key` on `m.room_key_request` to-device messages, as per [MSC3700](https://github.com/matrix-org/matrix-spec-proposals/pull/3700). \ No newline at end of file diff --git a/content/client-server-api/modules/end_to_end_encryption.md b/content/client-server-api/modules/end_to_end_encryption.md index fb94f03f..c7db35aa 100644 --- a/content/client-server-api/modules/end_to_end_encryption.md +++ b/content/client-server-api/modules/end_to_end_encryption.md @@ -1531,8 +1531,19 @@ For example, Megolm sessions that were sent using the old session would have been lost. The client can attempt to retrieve the lost sessions through `m.room_key_request` messages. +{{% boxes/note %}} +Clients should send key requests for unknown sessions to all devices for +the user which used the session rather than just the `device_id` or +`sender_key` denoted on the event. + +This is due to a deprecation of the fields. See +[`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) for more information. +{{% /boxes/note %}} + ##### `m.megolm.v1.aes-sha2` +{{% changed-in v="1.3" %}} + The name `m.megolm.v1.aes-sha2` corresponds to version 1 of the Megolm ratchet, as defined by the [Megolm specification](http://matrix.org/docs/spec/megolm.html). This uses: @@ -1580,10 +1591,36 @@ ratchet index that they have already decrypted. Care should be taken in order to avoid false positives, as a client may decrypt the same event twice as part of its normal processing. -As with Olm events, clients must confirm that the `sender_key` belongs -to the user who sent the message. The same reasoning applies, but the -sender ed25519 key has to be inferred from the `keys.ed25519` property -of the event which established the Megolm session. +Similar to Olm events, clients should confirm that the user who sent the +message corresponds to the user the message was expected to come from. +For room events, this means ensuring the event's `sender`, `room_id`, and +the recorded `session_id` match a trusted session (eg: the `session_id` +is already known and validated to the client). + +{{% boxes/note %}} +As of `v1.3`, the `sender_key` and `device_id` keys are **deprecated**. They +SHOULD continue to be sent, however they MUST NOT be used to verify the +message's source. + +Clients MUST NOT store or lookup sessions using the `sender_key` or `device_id`. + +In a future version of the specification the keys can be removed completely, +including for sending new messages. +{{% /boxes/note %}} + +{{% boxes/rationale %}} +Removing the fields (eventually) improves privacy and security by masking the +device which sent the encrypted message as well as reducing the client's +dependence on untrusted data: a malicious server (or similar attacker) could +change these values, and other devices/users can simply lie about them too. + +We can remove the fields, particularly the `sender_key`, because the `session_id` +is already globally unique, therefore making storage and lookup possible without +the need for added context from the `sender_key` or `device_id`. + +Removing the dependence on the fields gives a privacy gain while also increasing +the security of messages transmitted over Matrix. +{{% /boxes/rationale %}} In order to enable end-to-end encryption in a room, clients can send an `m.room.encryption` state event specifying `m.megolm.v1.aes-sha2` as its @@ -1596,6 +1633,11 @@ that they can decrypt future messages encrypted using this session. An `m.room_key` events sent by other devices in order to decrypt their messages. +When a client is updating a Megolm session (room key) in its store, the client MUST ensure: + +* that the updated session data comes from a trusted source. +* that the new session key has a lower message index than the existing session key. + #### Protocol definitions ##### Events diff --git a/data/event-schemas/schema/m.room.encrypted.yaml b/data/event-schemas/schema/m.room.encrypted.yaml index 32e86902..51024f7e 100644 --- a/data/event-schemas/schema/m.room.encrypted.yaml +++ b/data/event-schemas/schema/m.room.encrypted.yaml @@ -40,10 +40,35 @@ properties: Olm event. For more details, see [Messaging Algorithms](/client-server-api/#messaging-algorithms). sender_key: type: string - description: The Curve25519 key of the sender. + x-changedInMatrixVersion: + 1.3: |- + Previously this field was required, however given it offers no additional + security or privacy benefit it has been deprecated for Megolm messages. + See [`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) for more information. + description: |- + The Curve25519 key of the sender. Required (not deprecated) if not using Megolm. + + **Deprecated**: This field provides no additional security or privacy benefit + for Megolm messages and must not be read from if the encrypted event is using + Megolm. It should still be included on outgoing messages, however must not be + used to find the corresponding session. See [`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) + for more information. device_id: type: string - description: The ID of the sending device. Required with Megolm. + x-changedInMatrixVersion: + 1.3: |- + Previously this field was required for Megolm messages, however given it + offers no additional security or privacy benefit it has been deprecated + for Megolm messages. See [`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) for + more information. + description: |- + The ID of the sending device. + + **Deprecated**: This field provides no additional security or privacy benefit + for Megolm messages and must not be read from if the encrypted event is using + Megolm. It should still be included on outgoing messages, however must not be + used to find the corresponding session. See [`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) + for more information. session_id: type: string description: |- @@ -51,7 +76,6 @@ properties: Megolm. required: - algorithm - - sender_key - ciphertext type: object type: diff --git a/data/event-schemas/schema/m.room_key_request.yaml b/data/event-schemas/schema/m.room_key_request.yaml index 99afdb19..acabe2d9 100644 --- a/data/event-schemas/schema/m.room_key_request.yaml +++ b/data/event-schemas/schema/m.room_key_request.yaml @@ -23,8 +23,19 @@ properties: description: The room where the key is used. sender_key: type: string + x-changedInMatrixVersion: + 1.3: |- + Previously this field was required, however given it offers no additional + security or privacy benefit it has been deprecated. See [`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) + for more information. description: |- The Curve25519 key of the device which initiated the session originally. + + **Deprecated**: This field provides no additional security or privacy benefit + and must not be read from. It should still be included on outgoing messages + (if the event for which keys are being requested for *also* has a `sender_key`), + however must not be used to find the corresponding session. See [`m.megolm.v1.aes-sha2`](#mmegolmv1aes-sha2) + for more information. session_id: type: string description: The ID of the session that the key is for. @@ -32,7 +43,6 @@ properties: - algorithm - room_id - session_id - - sender_key type: object title: RequestedKeyInfo action: From c665f7fcb64176cf9659e4910bc89f595acb15c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Sat, 11 Jun 2022 17:47:20 +0200 Subject: [PATCH 14/17] refresh_token is omitted if inhibit_login is true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- data/api/client-server/registration.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/api/client-server/registration.yaml b/data/api/client-server/registration.yaml index 21fc1b84..f48385e9 100644 --- a/data/api/client-server/registration.yaml +++ b/data/api/client-server/registration.yaml @@ -165,7 +165,7 @@ paths: obtain a new access token when it expires by calling the `/refresh` endpoint. - Omitted if the `inhibit_login` option is false. + Omitted if the `inhibit_login` option is true. x-addedInMatrixVersion: "1.3" expires_in_ms: type: integer @@ -177,7 +177,7 @@ paths: to obtain a new access token. If not given, the client can assume that the access token will not expire. - Omitted if the `inhibit_login` option is false. + Omitted if the `inhibit_login` option is true. x-addedInMatrixVersion: "1.3" home_server: type: string From 86f984b1b6b41ff9b7ec0313a9728c363445695c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Sat, 11 Jun 2022 17:52:48 +0200 Subject: [PATCH 15/17] Changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- changelogs/client_server/newsfragments/1113.clarification | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/client_server/newsfragments/1113.clarification diff --git a/changelogs/client_server/newsfragments/1113.clarification b/changelogs/client_server/newsfragments/1113.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/1113.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. From 3fbaced8be564c941a3e710c0f8eb70231ce3225 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 13 Jun 2022 05:27:17 -0400 Subject: [PATCH 16/17] Merge changelog with changelog for #1056 --- changelogs/client_server/newsfragments/1113.clarification | 1 - changelogs/client_server/newsfragments/1113.feature | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelogs/client_server/newsfragments/1113.clarification create mode 100644 changelogs/client_server/newsfragments/1113.feature diff --git a/changelogs/client_server/newsfragments/1113.clarification b/changelogs/client_server/newsfragments/1113.clarification deleted file mode 100644 index 3ccb2333..00000000 --- a/changelogs/client_server/newsfragments/1113.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/1113.feature b/changelogs/client_server/newsfragments/1113.feature new file mode 100644 index 00000000..2f8febb7 --- /dev/null +++ b/changelogs/client_server/newsfragments/1113.feature @@ -0,0 +1 @@ +Add refresh tokens, per [MSC2918](https://github.com/matrix-org/matrix-spec-proposals/pull/2918). From cbdd889fa8ab23e07bd1d3b5cac619efa2373871 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Tue, 14 Jun 2022 17:35:18 +0200 Subject: [PATCH 17/17] auth_data.yaml: formally allow it to be non-object (#1115) Closes #716. Signed-off-by: Alexey Rusakov Kitsune-Ral@users.sf.net --- changelogs/client_server/newsfragments/1115.clarification | 1 + data/api/client-server/definitions/auth_data.yaml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelogs/client_server/newsfragments/1115.clarification diff --git a/changelogs/client_server/newsfragments/1115.clarification b/changelogs/client_server/newsfragments/1115.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/1115.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/data/api/client-server/definitions/auth_data.yaml b/data/api/client-server/definitions/auth_data.yaml index 4bfbf62b..691cdc65 100644 --- a/data/api/client-server/definitions/auth_data.yaml +++ b/data/api/client-server/definitions/auth_data.yaml @@ -28,7 +28,6 @@ properties: type: string additionalProperties: description: Keys dependent on the login type - type: object example: type: "example.type.foo" session: "xxxxx"