Merge branch 'master' into travis/msc/widgets-send-receive-events
commit
b67124622b
@ -0,0 +1 @@
|
||||
Deprecate starting verifications that don't start with `m.key.verification.request` as per [MSC3122](https://github.com/matrix-org/matrix-doc/pull/3122).
|
||||
@ -0,0 +1 @@
|
||||
Update `Access-Control-Allow-Headers` recommendation to fit CORS specification.
|
||||
@ -0,0 +1 @@
|
||||
Explicitly state that `replacment_room` is a room ID in `m.room.tombstone` events.
|
||||
@ -0,0 +1 @@
|
||||
Clarify that all request bodies are required.
|
||||
@ -0,0 +1 @@
|
||||
Add support for knocking, as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
||||
@ -0,0 +1 @@
|
||||
Fix key_backup operation IDs in OpenAPI spec.
|
||||
@ -0,0 +1 @@
|
||||
Add titles for OpenAPI objects.
|
||||
@ -0,0 +1 @@
|
||||
Add auth property to UIA endpoint uploadCrossSigningKeys.
|
||||
@ -0,0 +1 @@
|
||||
Clarify that all request bodies are required.
|
||||
@ -0,0 +1 @@
|
||||
Disambiguate getEvents and peekEvents, and include both in swagger.
|
||||
@ -0,0 +1 @@
|
||||
Mention that a canonical alias event should be added when a room is created with an alias.
|
||||
@ -0,0 +1 @@
|
||||
Fix various typos throughout the specification.
|
||||
@ -0,0 +1 @@
|
||||
Add an 'API conventions' section to the Appendices.
|
||||
@ -1 +1 @@
|
||||
Clarify that some identifiers may be lowercase prior to processing, as per [MSC2265](https://github.com/matrix-org/matrix-doc/pull/2265).
|
||||
Clarify that some identifiers must be case folded prior to processing, as per [MSC2265](https://github.com/matrix-org/matrix-doc/pull/2265).
|
||||
|
||||
@ -0,0 +1 @@
|
||||
Clarify that some identifiers must be case folded prior to processing, as per [MSC2265](https://github.com/matrix-org/matrix-doc/pull/2265).
|
||||
@ -0,0 +1 @@
|
||||
Fix various typos throughout the specification.
|
||||
@ -0,0 +1 @@
|
||||
Correct the `/_matrix/federation/v1/user/devices/{userId}` response which actually returns `"self_signing_key"` instead of `"self_signing_keys"`.
|
||||
@ -0,0 +1 @@
|
||||
Explain the reasons why `<hostname>` TLS certificate is needed rather than `<delegated_hostname>` for SRV delegation.
|
||||
@ -0,0 +1,97 @@
|
||||
# MSC2732: Olm fallback keys
|
||||
|
||||
Olm uses a set of one-time keys when initializing a session between two
|
||||
devices: Alice uploads one-time keys to her homeserver, and Bob claims one of
|
||||
them to perform a Diffie-Hellman to generate a shared key. As implied by the
|
||||
name, a one-time key is only to be used once. However, if all of Alice's
|
||||
one-time keys are claimed, Bob will not be able to create a session with Alice.
|
||||
|
||||
This can be addressed by Alice uploading a fallback key that is used in place
|
||||
of a one-time key when no one-time keys are available.
|
||||
|
||||
## Proposal
|
||||
|
||||
A new request parameter, `fallback_keys`, is added to the body of the
|
||||
[`/keys/upload` client-server API](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-upload), which is in the same format as the
|
||||
`one_time_keys` parameter with the exception that there must be at most one key
|
||||
per key algorithm. If the user had previously uploaded a fallback key for a
|
||||
given algorithm, it is replaced -- the server will only keep one fallback key
|
||||
per algorithm for each user.
|
||||
|
||||
When uploading fallback keys for algorithms whose key format is a signed JSON
|
||||
object, client should include a property named `fallback` with a value of
|
||||
`true`.
|
||||
|
||||
Example:
|
||||
|
||||
`POST /keys/upload`
|
||||
|
||||
```json
|
||||
{
|
||||
"fallback_keys": {
|
||||
"signed_curve25519:AAAAAA": {
|
||||
"key": "base64+public+key",
|
||||
"fallback": true,
|
||||
"signatures": {
|
||||
"@alice:example.org": {
|
||||
"ed25519:DEVICEID": "base64+signature"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When Bob calls `/keys/claim` to claim one of Alice's one-time keys, but Alice
|
||||
has no one-time keys left, the homeserver will return the fallback key instead,
|
||||
if Alice had previously uploaded one. Unlike with one-time keys, fallback keys
|
||||
are not deleted when they are returned by `/keys/claim`. However, the server
|
||||
marks that they have been used.
|
||||
|
||||
A new response parameter, `device_unused_fallback_key_types`, is added to
|
||||
`/sync`. This is an array listing the key algorithms for which the server has
|
||||
an unused fallback key for the device. If the client wants the server to have a
|
||||
fallback key for a given key algorithm, but that algorithm is not listed in
|
||||
`device_unused_fallback_key_types`, the client will upload a new key as above.
|
||||
|
||||
The `device_unused_fallback_key_types` parameter must be present if the server
|
||||
supports fallback keys. Clients can thus treat this field as an indication
|
||||
that the server supports fallback keys, and so only upload fallback keys to
|
||||
servers that support them.
|
||||
|
||||
Example:
|
||||
|
||||
`GET /sync`
|
||||
|
||||
Response:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
// other fields...
|
||||
"device_unused_fallback_key_types": ["signed_curve25519"]
|
||||
}
|
||||
```
|
||||
|
||||
## Security considerations
|
||||
|
||||
Using a fallback key rather than a one-time key has security implications. An
|
||||
attacker can replay a message that was originally sent with a fallback key, and
|
||||
the receiving client will accept it as a new message if the fallback key is
|
||||
still active. Also, an attacker that compromises a client may be able to
|
||||
retrieve the private part of the fallback key to decrypt past messages if the
|
||||
client has still retained the private part of the fallback key.
|
||||
|
||||
For this reason, clients should not store the private part of the fallback key
|
||||
indefinitely. For example, client should only store at most two fallback keys:
|
||||
the current fallback key (that it has not yet received any messages for) and
|
||||
the previous fallback key, and should remove the previous fallback key once it
|
||||
is reasonably certain that it has received all the messages that use it (for
|
||||
example, one hour after receiving the first message that used it).
|
||||
|
||||
For addressing replay attacks, clients can also keep track of inbound sessions
|
||||
to detect replays.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
The `fallback_keys` request parameter and the `device_unused_fallback_key_types`
|
||||
response parameter will be prefixed by `org.matrix.msc2732.`.
|
||||
@ -0,0 +1,135 @@
|
||||
# MSC2778: Providing authentication method for appservice users
|
||||
|
||||
Appservices within Matrix are increasingly attempting to support End-to-End Encryption. As such, they
|
||||
need a way to generate devices for their users so that they can participate in E2E rooms. In order to
|
||||
do so, this proposal suggests implementing an appservice extension to the
|
||||
[`POST /login` endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login).
|
||||
|
||||
Appservice users do not usually need to log in as they do not need their own access token, and do not
|
||||
traditionally need a "device". However, E2E encryption demands that at least one user in a room has a
|
||||
Matrix device which means bridge users need to be able to generate a device on demand. In the past,
|
||||
bridge developers have used the bridge bot's device for all bridge users in the room, but this causes
|
||||
problems should the bridge wish to only join ghosts to a room (e.g. for DMs).
|
||||
|
||||
Another advantage this provides is that an appservice can now be used to generate access tokens for
|
||||
any user in its namespace without having to set a password for that user, which may be useful where
|
||||
maintaining password(s) in the configuration is undesirable.
|
||||
|
||||
## Proposal
|
||||
|
||||
A new `type` is to be added to `POST /login`: `m.login.application_service`
|
||||
|
||||
The `/login` endpoint may now take an `access_token` in the same way that other
|
||||
authenticated endpoints do. No additional parameters should be specified in the request body.
|
||||
|
||||
Example request
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": {
|
||||
"type": "m.id.user",
|
||||
"user": "_bridge_alice"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: Implementations MUST use the `identifier.type`=`m.id.user` method of specifying the
|
||||
localpart. The deprecated top-level `user` field **cannot** use this login flow type. This
|
||||
is deliberate so as to coax developers into using the new identifier format when implementing
|
||||
new flows.
|
||||
|
||||
The response body should be unchanged from the existing `/login` specification.
|
||||
|
||||
If one of the following conditions are true:
|
||||
|
||||
- The access token is not provided
|
||||
- The access token does not correspond to an appservice
|
||||
- Or the user has not previously been registered
|
||||
|
||||
Then the servers MUST reject with HTTP 403, with an `errcode` of `"M_FORBIDDEN"`.
|
||||
|
||||
If the access token DOES correspond to an appservice but the user is not inside its namespace,
|
||||
then the `errcode` must be `"M_EXCLUSIVE"`.
|
||||
|
||||
Homeservers should ignore the `access_token` parameter if a type other than
|
||||
`m.login.application_service` has been provided.
|
||||
|
||||
Appservices creating **new** users can still use the `/register` endpoint to generate an `access_token` / `device_id`
|
||||
but for existing users, the `/login` endpoint can be used instead.
|
||||
|
||||
## Potential issues
|
||||
|
||||
This proposal means that there will be more calls to make when setting up a appservice user, when
|
||||
using encryption. While this could be done during the registration step, this would prohibit creating
|
||||
new devices should the appservice intentionally or inadvertently have lost the client-side device data.
|
||||
|
||||
## Alternatives
|
||||
|
||||
### 1. Include the token in the `/login` request body
|
||||
|
||||
One minor tweak to the current proposal could be to include the token as part of the auth data, rather than
|
||||
being part of the header/params to the request. An argument could be made for either, but since the specification
|
||||
expects the appservice to pass the token this way in all requests, including `/register`, it seems wise to keep
|
||||
it that way.
|
||||
|
||||
### 2. Use implementation specific "shared secret" authentication
|
||||
|
||||
Some community members have used homeserver implementation details such as a "shared secret" authentication method to
|
||||
log into the accounts without having to use the /login process at all. Synapse provides such a function,
|
||||
but also means the appservice can now authenticate as any user on the homeserver. This is undesirable from a
|
||||
security standpoint.
|
||||
|
||||
### 3. Keep using `/register` solely
|
||||
|
||||
A third option could be to create a new endpoint that simply creates a new device for an appservice user on demand.
|
||||
Given the rest of the matrix eco-system does this with /login, and /login is already extensible with `type`, it would
|
||||
create more work for all parties involved for little benefit.
|
||||
|
||||
Finally, `POST /register` does already return a `device_id` and `access_token` so appservices
|
||||
could store this information rather than calling `POST /login` at all. This does however present a few problems:
|
||||
|
||||
- Quite a few appservices which only support unencrypted messaging do not use/store the `device_id`/`access_token` from a register call.
|
||||
In the event that an appservice eventually gains the ability to support encryption, they would be unable to fetch a new `device_id`/
|
||||
`access_token` for any existing users (as `/register` would fail for an existing user).
|
||||
- If user tokens were lost or exposed, there is no way to programattically create new access tokens for these users.
|
||||
- Finally, if a user was registered externally and the appservice would like to masquerade as it, it would be unable to fetch
|
||||
an access token for that user.
|
||||
|
||||
While `POST /register` does work, it is impactical as the sole method of fetching an access token.
|
||||
|
||||
## Security considerations
|
||||
|
||||
Appservices could use this new functionality to generate devices for any userId that are within its namespace e.g. setting the
|
||||
user namespace regex to `@.*:example.com` would allow appservice to control anyone on the homeserver. While this sounds scary, in practice
|
||||
this is not a problem because:
|
||||
|
||||
- Appservice namespaces are maintained by the homeserver admin. If the namespace were to change, then it's reasonable
|
||||
to assume that the server admin is aware. There is no defense mechanism to stop a malicious server admin from creating new
|
||||
devices for a given user's account as they could also do so by simply modifying the database.
|
||||
|
||||
- While an appservice *could* try to masquerade as a user maliciously without the server admin expecting it, it would still
|
||||
be bound by the restrictions of the namespace. Server admins are expected to be aware of the implications of adding new
|
||||
appservices to their server so the burden of responsibility lies with the server admin.
|
||||
|
||||
- Appservices already can /sync as any user using the `as_token` and send any messages as any user in the namespace, the only
|
||||
difference is that without a dedicated access token they are unable to receive device messages. While in theory this
|
||||
does make them unable to see encrypted messages, this is not designed to be a security mechanism.
|
||||
|
||||
In conclusion this MSC only automates the creation of new devices for users inside an AS namespace, which is something
|
||||
a server admin could already do. Appservices should always be treated with care and so with these facts in mind the MSC should
|
||||
be considered secure.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
Implementations should use `uk.half-shot.msc2778.login.application_service` for `type` given in the
|
||||
`POST /login` until this lands in a released version of the specification.
|
||||
|
||||
## Implementations
|
||||
|
||||
The proposal has been implemented by a homeserver, a bridge SDK and two bridges:
|
||||
|
||||
- [synapse](https://github.com/matrix-org/synapse/pull/8320)
|
||||
- [mautrix-python](https://github.com/tulir/mautrix-python/commit/12d7c48ca7c15fd3ff61608369af1cf69e289aeb)
|
||||
- [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp/commit/ead8a869c84d07fadc7cfcf3d522452c99faaa36)
|
||||
- [matrix-appservice-bridge](https://github.com/matrix-org/matrix-appservice-bridge/pull/231/files#diff-5e93f1b51d50a44fcf0ca46ea1793c1cR851-R864)
|
||||
@ -0,0 +1,291 @@
|
||||
# Restricting room membership based on membership in other rooms
|
||||
|
||||
A desirable feature is to give room admins the power to restrict membership of
|
||||
their room based on the membership of one or more rooms.
|
||||
|
||||
Potential usecases include:
|
||||
|
||||
* Private spaces (allowing any member of a [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772)
|
||||
space to join child rooms in that space), for example:
|
||||
|
||||
> members of the #doglovers:example.com space can join this room without an invitation<sup id="a1">[1](#f1)</sup>
|
||||
* Room upgrades for private rooms (instead of issuing invites to each user).
|
||||
* Allowing all users in a private room to be able to join a private breakout room.
|
||||
|
||||
This does not preclude members from being directly invited to the room, which is
|
||||
still a useful discovery feature.
|
||||
|
||||
## Proposal
|
||||
|
||||
In a future room version a new `join_rule` (`restricted`) will be used to reflect
|
||||
a cross between `invite` and `public` join rules. The content of the join rules
|
||||
would include the rooms to trust for membership. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.join_rules",
|
||||
"state_key": "",
|
||||
"content": {
|
||||
"join_rule": "restricted",
|
||||
"allow": [
|
||||
{
|
||||
"type": "m.room_membership",
|
||||
"room_id": "!mods:example.org"
|
||||
},
|
||||
{
|
||||
"type": "m.room_membership",
|
||||
"room_id": "!users:example.org"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This means that a user must be a member of the `!mods:example.org` room or
|
||||
`!users:example.org` room in order to join without an invite<sup id="a2">[2](#f2)</sup>.
|
||||
Membership in a single allowed room is enough.
|
||||
|
||||
If the `allow` key is an empty list (or not a list at all), then no users are
|
||||
allowed to join without an invite. Each entry is expected to be an object with the
|
||||
following keys:
|
||||
|
||||
* `type`: `"m.room_membership"` to describe that we are allowing access via room
|
||||
membership. Future MSCs may define other types.
|
||||
* `room_id`: The room ID to check the membership of.
|
||||
|
||||
Any entries in the list which do not match the expected format are ignored. Thus,
|
||||
if all entries are invalid, the list behaves as if empty and all users without
|
||||
an invite are rejected.
|
||||
|
||||
The `allow` key is to be protected when redacting an event.
|
||||
|
||||
When a homeserver receives a `/join` request from a client or a `/make_join` /
|
||||
`/send_join` request from another homeserver, the request should only be permitted
|
||||
if the user is invited to this room, or is joined to one of the listed rooms. If
|
||||
the user is not a member of at least one of the rooms, the homeserver should return
|
||||
an error response with HTTP status code of 403 and an `errcode` of `M_FORBIDDEN`.
|
||||
|
||||
It is possible for a resident homeserver (one which receives a `/make_join` /
|
||||
`/send_join` request) to not know if the user is in some of the allowed rooms (due
|
||||
to not participating in them). If the user is not in any of the allowed rooms that
|
||||
are known to the homeserver, and the homeserver is not participating in all listed
|
||||
rooms, then it should return an error response with HTTP status code of 400 with an `errcode` of `M_UNABLE_TO_AUTHORISE_JOIN`. The joining server should
|
||||
attempt to join via another resident homeserver. If the resident homeserver knows
|
||||
that the user is not in *any* of the allowed rooms it should return an error response
|
||||
with HTTP status code of 403 and an `errcode` of `M_FORBIDDEN`. Note that it is a
|
||||
configuration error if there are allowed rooms with no participating authorised
|
||||
servers.
|
||||
|
||||
A chosen resident homeserver might also be unable to issue invites (which, as below,
|
||||
is a pre-requisite for generating a correctly-signed join event). In this case
|
||||
it should return an error response with HTTP status code of 400 and an `errcode`
|
||||
of `M_UNABLE_TO_GRANT_JOIN`. The joining server should attempt to join via another
|
||||
resident homeserver.
|
||||
|
||||
From the perspective of the [auth rules](https://spec.matrix.org/unstable/rooms/v1/#authorization-rules),
|
||||
the `restricted` join rule has the same behavior as `public`, with the additional
|
||||
caveat that servers must ensure that, for `m.room.member` events with a `membership` of `join`:
|
||||
|
||||
* The user's previous membership was `invite` or `join`, or
|
||||
* The join event has a valid signature from a homeserver whose users have the
|
||||
power to issue invites.
|
||||
|
||||
When generating a join event for `/join` or `/make_join`, the server should
|
||||
include the MXID of a local user who could issue an invite in the content with
|
||||
the key `join_authorised_via_users_server`. The actual user chosen is arbitrary.
|
||||
|
||||
The changes to the auth rules imply that:
|
||||
|
||||
* A join event issued via `/send_join` is signed by not just the requesting
|
||||
server, but also the resident server.<sup id="a3">[3](#f3)</sup>
|
||||
|
||||
In order for the joining server to receive the proper signatures the join
|
||||
event will be returned via `/send_join` in the `event` field.
|
||||
* The auth chain of the join event needs to include events which prove
|
||||
the homeserver can be issuing the join. This can be done by including:
|
||||
|
||||
* The `m.room.power_levels` event.
|
||||
* The join event of the user specified in `join_authorised_via_users_server`.
|
||||
|
||||
It should be confirmed that the authorising user is in the room. (This
|
||||
prevents situations where any homeserver could process the join, even if
|
||||
they weren't in the room, under certain power level conditions.)
|
||||
|
||||
The above creates a new restriction on the relationship between the resident
|
||||
servers used for `/make_join` and `/send_join` -- they must now both go to
|
||||
the same server (since the `join_authorised_via_users_server` is added in
|
||||
the call to `/make_join`, while the final signature is added during
|
||||
the call to `/send_join`). If a request to `/send_join` is received that includes
|
||||
an event from a different resident server it should return an error response with
|
||||
HTTP status code of 400.
|
||||
|
||||
Note that the homeservers whose users can issue invites are trusted to confirm
|
||||
that the `allow` rules were properly checked (since this cannot easily be
|
||||
enforced over federation by event authorisation).<sup id="a4">[4](#f4)</sup>
|
||||
|
||||
To better cope with joining via aliases, homeservers should use the list of
|
||||
authorised servers (not the list of candidate servers) when a user attempts to
|
||||
join a room.
|
||||
|
||||
## Summary of the behaviour of join rules
|
||||
|
||||
See the [join rules](https://matrix.org/docs/spec/client_server/r0.6.1#m-room-join-rules)
|
||||
specification for full details; the summary below is meant to highlight the differences
|
||||
between `public`, `invite`, and `restricted` from a user perspective. Note that
|
||||
all join rules are subject to `ban` and `server_acls`.
|
||||
|
||||
* `public`: anyone can join, as today.
|
||||
* `invite`: only people with membership `invite` can join, as today.
|
||||
* `knock`: the same as `invite`, except anyone can knock. See
|
||||
[MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
||||
* `private`: This is reserved, but unspecified.
|
||||
* `restricted`: the same as `invite`, except users may also join if they are a
|
||||
member of a room listed in the `allow` rules.
|
||||
|
||||
## Security considerations
|
||||
|
||||
Increased trust to enforce the join rules during calls to `/join`, `/make_join`,
|
||||
and `/send_join` is placed in the homeservers whose users can issue invites.
|
||||
Although it is possible for those homeservers to issue a join event in bad faith,
|
||||
there is no real-world benefit to doing this as those homeservers could easily
|
||||
side-step the restriction by issuing an invite first anyway.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
The `restricted` join rule will be included in a future room version to allow
|
||||
servers and clients to opt-into the new functionality.
|
||||
|
||||
During development, an unstable room version of `org.matrix.msc3083.v2` will be used.
|
||||
Since the room version namespaces the behaviour, the `allow` key and value, as well
|
||||
as the `restricted` join rule value do not need unstable prefixes.
|
||||
|
||||
An unstable key of `org.matrix.msc3083.v2.event` will be used in the response
|
||||
from `/send_join` in place of `event` during development.
|
||||
|
||||
## Alternatives
|
||||
|
||||
It may seem that just having the `allow` key with `public` join rules is enough
|
||||
(as originally suggested in [MSC2962](https://github.com/matrix-org/matrix-doc/pull/2962)),
|
||||
but there are concerns that changing the behaviour of a pre-existing `public`
|
||||
join rule may cause security issues in older implementations (that do not yet
|
||||
understand the new behaviour). This could be solved by introducing a new room
|
||||
version, thus it seems clearer to introduce a new join rule -- `restricted`.
|
||||
|
||||
Using an `allow` key with the `invite` join rules to broaden who can join was rejected
|
||||
as an option since it requires weakening the [auth rules](https://spec.matrix.org/unstable/rooms/v1/#authorization-rules).
|
||||
From the perspective of the auth rules, the `restricted` join rule is identical
|
||||
to `public` with additional checks on the signature of the event.
|
||||
|
||||
## Future extensions
|
||||
|
||||
### Checking room membership over federation
|
||||
|
||||
If a homeserver is not in an allowed room (and thus doesn't know the
|
||||
membership of it) then the server cannot enforce the membership checks while
|
||||
generating a join event. Peeking over federation, as described in
|
||||
[MSC2444](https://github.com/matrix-org/matrix-doc/pull/2444),
|
||||
could be used to establish if the user is in any of the proper rooms.
|
||||
|
||||
This would then delegate power out to a (potentially) untrusted server, giving that
|
||||
peek server significant power. For example, a poorly chosen peek
|
||||
server could lie about the room membership and add an `@evil_user:example.org`
|
||||
to an allowed room to gain membership to a room.
|
||||
|
||||
As iterated above, this MSC recommends rejecting the join, potentially allowing
|
||||
the requesting homeserver to retry via another homeserver.
|
||||
|
||||
### Kicking users out when they leave the allowed room
|
||||
|
||||
In the above example, suppose `@bob:server.example` leaves `!users:example.org`:
|
||||
should they be removed from the room? Likely not, by analogy with what happens
|
||||
when you switch the join rules from `public` to `invite`. Join rules currently govern
|
||||
joins, not existing room membership.
|
||||
|
||||
It is left to a future MSC to consider this, but some potential thoughts are
|
||||
given below.
|
||||
|
||||
If you assume that a user *should* be removed in this case, one option is to
|
||||
leave the departure up to Bob's server `server.example`, but this places a
|
||||
relatively high level of trust in that server. Additionally, if `server.example`
|
||||
were offline, other users in the room would still see Bob in the room (and their
|
||||
servers would attempt to send message traffic to it).
|
||||
|
||||
Another consideration is that users may have joined via a direct invite, not via
|
||||
access through a room.
|
||||
|
||||
Fixing this is thorny. Some sort of annotation on the membership events might
|
||||
help, but it's unclear what the desired semantics are:
|
||||
|
||||
* Assuming that users in an allowed room are *not* kicked when that room is
|
||||
removed from `allow`, are those users then given a pass to remain
|
||||
in the room indefinitely? What happens if the room is added back to
|
||||
`allow` and *then* the user leaves it?
|
||||
* Suppose a user joins a room via an allowed room (RoomA). Later, RoomB is added
|
||||
to the `allow` list and RoomA is removed. What should happen when the
|
||||
user leaves RoomB? Are they exempt from the kick?
|
||||
|
||||
It is possible that completely different state should be kept, or a different
|
||||
`m.room.member` state could be used in a more reasonable way to track this.
|
||||
|
||||
### Inheriting join rules
|
||||
|
||||
If an allowed room is a space and you make a parent space invite-only, should that
|
||||
(optionally?) cascade into child rooms? This would have some of the same problems
|
||||
as inheriting power levels, as discussed in [MSC2962](https://github.com/matrix-org/matrix-doc/pull/2962).
|
||||
|
||||
### Additional allow types
|
||||
|
||||
Future MSCs may wish to define additional values for the `type` argument, potentially
|
||||
restricting access via:
|
||||
|
||||
* MXIDs or servers.
|
||||
* A shared secret (room password).
|
||||
|
||||
These are just examples and are not fully thought through for this MSC, but it should
|
||||
be possible to add these behaviors in the future.
|
||||
|
||||
### Client considerations
|
||||
|
||||
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772) defines a `via`
|
||||
key in the content of `m.space.child` events:
|
||||
|
||||
> the content must contain a via `key` which gives a list of candidate servers
|
||||
> that can be used to join the room.
|
||||
|
||||
It is possible for the list of candidate servers and the list of authorised
|
||||
servers to diverge. It may not be possible for a user to join a room if there's
|
||||
no overlap between these lists.
|
||||
|
||||
If there is some overlap between the lists of servers the join request should
|
||||
complete successfully.
|
||||
|
||||
Clients should also consider the authorised servers when generating candidate
|
||||
servers to embed in links to the room, e.g. via matrix.to.
|
||||
|
||||
A future MSC may define a way to override or update the `via` key in a coherent
|
||||
manner.
|
||||
|
||||
## Footnotes
|
||||
|
||||
<a id="f1"/>[1]: The converse restriction, "anybody can join, provided they are not members
|
||||
of the #catlovers:example.com space" is less useful since:
|
||||
|
||||
1. Users in the banned room could simply leave it at any time
|
||||
2. This functionality is already partially provided by
|
||||
[Moderation policy lists](https://matrix.org/docs/spec/client_server/r0.6.1#moderation-policy-lists). [↩](#a1)
|
||||
|
||||
<a id="f2"/>[2]: Note that there is nothing stopping users sending and
|
||||
receiving invites in `public` rooms today, and they work as you might expect.
|
||||
The only difference is that you are not *required* to hold an invite when
|
||||
joining the room. [↩](#a2)
|
||||
|
||||
<a id="f3"/>[3]: This seems like an improvement regardless since the resident server
|
||||
is accepting the event on behalf of the joining server and ideally this should be
|
||||
verifiable after the fact, even for current room versions. Requiring all events
|
||||
to be signed and verified in this way is left to a future MSC. [↩](#a3)
|
||||
|
||||
<a id="f4"/>[4]: This has the downside of increased centralisation, as some
|
||||
homeservers that are already in the room may not issue a join event for another
|
||||
user on that server. (It must go through the `/make_join` / `/send_join` flow of
|
||||
a server whose users may issue invites.) This is considered a reasonable
|
||||
trade-off. [↩](#a4)
|
||||
@ -0,0 +1,195 @@
|
||||
# MSC3173: Expose stripped state events to any potential joiner
|
||||
|
||||
It can be useful to view the partial state of a room before joining to allow a user
|
||||
to know *what* they're joining. For example, it improves the user experience to
|
||||
show the user the room name and avatar before joining.
|
||||
|
||||
It is already allowed to partially view the room state without being joined to
|
||||
the room in some situations:
|
||||
|
||||
* If the room has `history_visibility: world_readable`, then anyone can inspect
|
||||
it (by calling `/state` on it).
|
||||
* Rooms in the [room directory](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms)
|
||||
expose some of their state publicly.
|
||||
* [Invited users](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
|
||||
and [knocking users](https://github.com/matrix-org/matrix-doc/pull/2403)
|
||||
receive stripped state events to display metadata to users.
|
||||
|
||||
This MSC proposes formalizing that the stripped state that is currently available
|
||||
to invited and knocking users is available to any user who could potentially join
|
||||
a room. It also defines "stripped state" and consolidates the recommendation on
|
||||
which events to include in the stripped state.
|
||||
|
||||
## Background
|
||||
|
||||
When creating an invite it is [currently recommended](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
|
||||
to include stripped state events which are useful for displaying the invite to a user:
|
||||
|
||||
> An optional list of simplified events to help the receiver of the invite identify
|
||||
> the room. The recommended events to include are the join rules, canonical alias,
|
||||
> avatar, and name of the room.
|
||||
|
||||
The invited user receives these [stripped state events](https://spec.matrix.org/unstable/client-server-api/#get_matrixclientr0sync)
|
||||
as part of the `/sync` response:
|
||||
|
||||
> The state of a room that the user has been invited to. These state events may
|
||||
> only have the `sender`, `type`, `state_key` and `content` keys present. These
|
||||
> events do not replace any state that the client already has for the room, for
|
||||
> example if the client has archived the room.
|
||||
|
||||
These are sent as part of the [`unsigned` content of the `m.room.member` event](https://spec.matrix.org/unstable/client-server-api/#mroommember)
|
||||
containing the invite.
|
||||
|
||||
[MSC2403: Add "knock" feature](https://github.com/matrix-org/matrix-doc/pull/2403)
|
||||
extends this concept to also include the stripped state events in the `/sync` response
|
||||
for knocking users:
|
||||
|
||||
> It is proposed to add a fourth possible key to rooms, called `knock`. Its value
|
||||
> is a mapping from room ID to room information. The room information is a mapping
|
||||
> from a key `knock_state` to another mapping with key events being a list of
|
||||
> `StrippedStateEvent`.
|
||||
|
||||
It is also provides an extended rationale of why this is useful:
|
||||
|
||||
> These stripped state events contain information about the room, most notably the
|
||||
> room's name and avatar. A client will need this information to show a nice
|
||||
> representation of pending knocked rooms. The recommended events to include are the
|
||||
> join rules, canonical alias, avatar, name and encryption state of the room, rather
|
||||
> than all room state. This behaviour matches the information sent to remote
|
||||
> homeservers when remote users are invited to a room.
|
||||
|
||||
[MSC1772: Spaces](https://github.com/matrix-org/matrix-doc/pull/1772) additionally
|
||||
recommends including the `m.room.create` event as one of the stripped state events:
|
||||
|
||||
> Join rules, invites and 3PID invites work as for a normal room, with the exception
|
||||
> that `invite_state` sent along with invites should be amended to include the
|
||||
> `m.room.create` event, to allow clients to discern whether an invite is to a
|
||||
> space-room or not.
|
||||
|
||||
## Proposal
|
||||
|
||||
The specification does not currently define what "stripped state" is or formally
|
||||
describe who can access it, instead it is specified that certain situations (e.g.
|
||||
an invite or knock) provide a potential joiner with the stripped state of a room.
|
||||
|
||||
This MSC clarifies what "stripped state" is and formalizes who can access the
|
||||
stripped state of a room in future cases.
|
||||
|
||||
Potential ways that a user might be able to join a room include, but are not
|
||||
limited to, the following mechanisms:
|
||||
|
||||
* A room that has `join_rules` set to `public` or `knock`.
|
||||
* A room that the user is in possession of an invite to (regardless of the `join_rules`).
|
||||
|
||||
This MSC proposes a formal definition for the stripped state of a room<sup id="a1">[1](#f1)</sup>:
|
||||
|
||||
> The stripped state of a room is a list of simplified state events to help a
|
||||
> potential joiner identify the room. These state events may only have the
|
||||
> `sender`, `type`, `state_key` and `content` keys present.
|
||||
|
||||
### Client behavior
|
||||
|
||||
These events do not replace any state that the client already has for the room,
|
||||
for example if the client has archived the room. Instead the client should keep
|
||||
two separate copies of the state: the one from the stripped state and one from the
|
||||
archived state. If the client joins the room then the current state will be given
|
||||
as a delta against the archived state not the stripped state.
|
||||
|
||||
### Server behavior
|
||||
|
||||
It is recommended (although not required<sup id="a2">[2](#f2)</sup>) that
|
||||
homeserver implementations include the following events as part of the stripped
|
||||
state of a room:
|
||||
|
||||
* Create event (`m.room.create`)<sup id="a3">[3](#f3)</sup>
|
||||
* Join rules (`m.room.join_rules`)
|
||||
* Canonical alias (`m.room.canonical_alias`)
|
||||
* Room avatar (`m.room.avatar`)
|
||||
* Room name (`m.room.name`)
|
||||
* Encryption information (`m.room.encryption`)<sup id="a4">[4](#f4)</sup>
|
||||
* Room topic (`m.room.topic`)<sup id="a5">[5](#f5)</sup>
|
||||
|
||||
## Potential issues
|
||||
|
||||
This is a formalization of current behavior and should not introduce new issues.
|
||||
|
||||
## Alternatives
|
||||
|
||||
A different approach would be to continue with what is done today for invites,
|
||||
knocking, the room directory: separately specify that a user is allowed to see
|
||||
the stripped state (and what events the stripped state should contain).
|
||||
|
||||
## Security considerations
|
||||
|
||||
This would allow for invisibly accessing the stripped state of a room with `public`
|
||||
or `knock` join rules.
|
||||
|
||||
In the case of a public room, if the room has `history_visibility` set to `world_readable`
|
||||
then this is no change. Additionally, this information is already provided by the
|
||||
room directory (if the room is listed there). Otherwise, it is trivial to access
|
||||
the state of the room by joining, but currently users in the room would know
|
||||
that the join occurred.
|
||||
|
||||
Similarly, in the case of knocking, a user is able to trivially access the
|
||||
stripped state of the room by knocking, but users in the room would know that
|
||||
the knock occurred.
|
||||
|
||||
This does not seem to weaken the security expectations of either join rule.
|
||||
|
||||
## Future extensions
|
||||
|
||||
### Revisions to the room directory
|
||||
|
||||
A future MSC could include additional information from the stripped state events
|
||||
in the [room directory](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms).
|
||||
The main missing piece seems to be the encryption information, but there may also
|
||||
be other pieces of information to include.
|
||||
|
||||
### Additional ways to access the stripped state
|
||||
|
||||
[MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946) proposes including
|
||||
the stripped state in the spaces summary. Not needing to rationalize what state
|
||||
can be included for a potential joiner would simplify this (and future) MSCs.
|
||||
|
||||
### Additional ways to join a room
|
||||
|
||||
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083) proposes a new
|
||||
join rule due to membership in a space. This MSC would clarify that the stripped
|
||||
state of a room is available to those joiners.
|
||||
|
||||
### Dedicated API for accessing the stripped state
|
||||
|
||||
Dedicated client-server and server-server APIs could be added to request the
|
||||
stripped state events, but that is considered out-of-scope for the current
|
||||
proposal.
|
||||
|
||||
This API would allow any potential joiner to query for the stripped state. If
|
||||
the server does not know the room's state it would need to query other servers
|
||||
for it.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
N/A
|
||||
|
||||
## Footnotes
|
||||
|
||||
<a id="f1"/>[1]: No changes are proposed to
|
||||
[the definition of `history_visibility`](https://matrix.org/docs/spec/client_server/latest#room-history-visibility).
|
||||
The state of a room which is `world_readable` is available to anyone. This somewhat
|
||||
implies that the stripped state is also available to anyone, regardless of the join
|
||||
rules, but having a `world_readable`, `invite` room does not seem valuable. [↩](#a1)
|
||||
|
||||
<a id="f2"/>[2]: Privacy conscious deployments may wish to limit the metadata
|
||||
available to users who are not in a room as the trade-off against user experience.
|
||||
There seems to be no reason to not allow this. [↩](#a2)
|
||||
|
||||
<a id="f3"/>[3]: As updated in [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772). [↩](#a3)
|
||||
|
||||
<a id="f4"/>[4]: The encryption information (`m.room.encryption`) is already sent
|
||||
from Synapse and generally seems useful for a user to know before joining a room.
|
||||
[↩](#a4)
|
||||
|
||||
<a id="f5"/>[5]: The room topic (`m.room.topic`) is included as part of the
|
||||
[room directory](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms)
|
||||
response for public rooms. It is also planned to be included as part of [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946)
|
||||
in the spaces summary response. [↩](#a5)
|
||||
@ -0,0 +1,15 @@
|
||||
# MSC3289: Room Version 8
|
||||
|
||||
A new room version, `8`, is proposed using [room version 7](https://spec.matrix.org/unstable/rooms/v7/)
|
||||
as a base and incorporating the following MSCs:
|
||||
|
||||
* [MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083) - Restricting room
|
||||
membership based on membership in other rooms
|
||||
|
||||
Though other MSCs are capable of being included in this version, they do not have
|
||||
sufficient implementation to be considered for this room version. A future room
|
||||
version may include them.
|
||||
|
||||
Room version `8` upon being added to the specification shall be considered stable.
|
||||
No other room versions are affected by this MSC. Before room version `8` can enter
|
||||
the specification, MSC3083 needs to complete its final comment period.
|
||||
Loading…
Reference in New Issue