You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
matrix-spec-proposals/specification/modules/third_party_invites.rst

259 lines
19 KiB
ReStructuredText

.. Copyright 2016 OpenMarket Ltd
..
.. 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.
Third party invites
===================
.. _module:third-party-invites:
This module adds in support for inviting new members to a room where their
Matrix user ID is not known, instead addressing them by a third party identifier
such as an email address.
There are two flows here; one if a Matrix user ID is known for the third party
identifier, and one if not. Either way, the client calls ``/invite`` with the
details of the third party identifier.
The homeserver asks the identity server whether a Matrix user ID is known for
that identifier:
- If it is, an invite is simply issued for that user.
- If it is not, the homeserver asks the identity server to record the details of
the invitation, and to notify the invitee's homeserver of this pending invitation if it gets
a binding for this identifier in the future. The identity server returns a token
and public key to the inviting homeserver.
When the invitee's homeserver receives the notification of the binding, it
should insert an ``m.room.member`` event into the room's graph for that user,
with ``content.membership`` = ``invite``, as well as a
``content.third_party_invite`` property which contains proof that the invitee
does indeed own that third party identifier. See the `m.room.member <#m-room-member>`_
schema for more information.
Events
------
{{m_room_third_party_invite_event}}
Client behaviour
----------------
A client asks a server to invite a user by their third party identifier.
{{third_party_membership_cs_http_api}}
Server behaviour
----------------
Upon receipt of an ``/invite``, the server is expected to look up the third party
identifier with the provided identity server. If the lookup yields a result for
a Matrix User ID then the normal invite process can be initiated. This process
ends up looking like this:
::
+---------+ +-------------+ +-----------------+
| Client | | Homeserver | | IdentityServer |
+---------+ +-------------+ +-----------------+
| | |
| POST /invite | |
|------------------------------------>| |
| | |
| | GET /lookup |
| |--------------------------------------------------->|
| | |
| | User ID result |
| |<---------------------------------------------------|
| | |
| | Invite process for the discovered User ID |
| |------------------------------------------ |
| | | |
| |<----------------------------------------- |
| | |
| Complete the /invite request | |
|<------------------------------------| |
| | |
However, if the lookup does not yield a bound User ID, the homeserver must store
the invite on the identity server and emit a valid ``m.room.third_party_invite``
event to the room. This process ends up looking like this:
::
+---------+ +-------------+ +-----------------+
| Client | | Homeserver | | IdentityServer |
+---------+ +-------------+ +-----------------+
| | |
| POST /invite | |
|------------------------------------>| |
| | |
| | GET /lookup |
| |-------------------------------------------------------------->|
| | |
| | "no users" result |
| |<--------------------------------------------------------------|
| | |
| | POST /store-invite |
| |-------------------------------------------------------------->|
| | |
| | Information needed for the m.room.third_party_invite |
| |<--------------------------------------------------------------|
| | |
| | Emit m.room.third_party_invite to the room |
| |------------------------------------------- |
| | | |
| |<------------------------------------------ |
| | |
| Complete the /invite request | |
|<------------------------------------| |
| | |
All homeservers MUST verify the signature in the event's
``content.third_party_invite.signed`` object.
The third party user will then need to verify their identity, which results in
a call from the identity server to the homeserver that bound the third party
identifier to a user. The homeserver then exchanges the ``m.room.third_party_invite``
event in the room for a complete ``m.room.member`` event for ``membership: invite``
for the user that has bound the third party identifier.
If a homeserver is joining a room for the first time because of an
``m.room.third_party_invite``, the server which is already participating in the
room (which is chosen as per the standard server-server specification) MUST
validate that the public key used for signing is still valid, by checking
``key_validity_url`` in the above described way.
No other homeservers may reject the joining of the room on the basis of
``key_validity_url``, this is so that all homeservers have a consistent view of
the room. They may, however, indicate to their clients that a member's
membership is questionable.
For example, given H1, H2, and H3 as homeservers, UserA as a user of H1, and an
identity server IS, the full sequence for a third party invite would look like
the following. This diagram assumes H1 and H2 are residents of the room while
H3 is attempting to join.
::
+-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+
| UserA | | ThirdPartyUser | | H1 | | H2 | | H3 | | IS |
+-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+
| | | | | |
| POST /invite for ThirdPartyUser | | | |
|----------------------------------->| | | |
| | | | | |
| | | GET /lookup | | |
| | |---------------------------------------------------------------------------------------------->|
| | | | | |
| | | | Lookup results (empty object) |
| | |<----------------------------------------------------------------------------------------------|
| | | | | |
| | | POST /store-invite | | |
| | |---------------------------------------------------------------------------------------------->|
| | | | | |
| | | | Token, keys, etc for third party invite |
| | |<----------------------------------------------------------------------------------------------|
| | | | | |
| | | (Federation) Emit m.room.third_party_invite | | |
| | |----------------------------------------------->| | |
| | | | | |
| Complete /invite request | | | |
|<-----------------------------------| | | |
| | | | | |
| | Verify identity | | | |
| |-------------------------------------------------------------------------------------------------------------------->|
| | | | | |
| | | | | POST /3pid/onbind |
| | | | |<---------------------------|
| | | | | |
| | | PUT /exchange_third_party_invite/:roomId | |
| | |<-----------------------------------------------------------------| |
| | | | | |
| | | Verify the request | | |
| | |------------------- | | |
| | | | | | |
| | |<------------------ | | |
| | | | | |
| | | (Federation) Emit m.room.member for invite | | |
| | |----------------------------------------------->| | |
| | | | | |
| | | | | |
| | | (Federation) Emit the m.room.member event sent to H2 | |
| | |----------------------------------------------------------------->| |
| | | | | |
| | | Complete /exchange_third_party_invite/:roomId request | |
| | |----------------------------------------------------------------->| |
| | | | | |
| | | | | Participate in the room |
| | | | |------------------------ |
| | | | | | |
| | | | |<----------------------- |
| | | | | |
Note that when H1 sends the ``m.room.member`` event to H2 and H3 it does not
have to block on either server's receipt of the event. Likewise, H1 may complete
the ``/exchange_third_party_invite/:roomId`` request at the same time as sending
the ``m.room.member`` event to H2 and H3. Additionally, H3 may complete the
``/3pid/onbind`` request it got from IS at any time - the completion is not shown
in the diagram.
H1 MUST verify the request from H3 to ensure the ``signed`` property is correct
as well as the ``key_validity_url`` as still being valid. This is done by making
a request to the `identity server /isvalid`_ endpoint, using the provided URL
rather than constructing a new one. The query string and response for the provided
URL must match the Identity Service Specification.
The reason that no other homeserver may reject the event based on checking
``key_validity_url`` is that we must ensure event acceptance is deterministic.
If some other participating server doesn't have a network path to the keyserver,
or if the keyserver were to go offline, or revoke its keys, that other server
would reject the event and cause the participating servers' graphs to diverge.
This relies on participating servers trusting each other, but that trust is
already implied by the server-server protocol. Also, the public key signature
verification must still be performed, so the attack surface here is minimized.
Security considerations
-----------------------
There are a number of privacy and trust implications to this module.
It is important for user privacy that leaking the mapping between a matrix user
ID and a third party identifier is hard. In particular, being able to look up
all third party identifiers from a matrix user ID (and accordingly, being able
to link each third party identifier) should be avoided wherever possible.
To this end, the third party identifier is not put in any event, rather an
opaque display name provided by the identity server is put into the events.
Clients should not remember or display third party identifiers from invites,
other than for the use of the inviter themself.
Homeservers are not required to trust any particular identity server(s). It is
generally a client's responsibility to decide which identity servers it trusts,
not a homeserver's. Accordingly, this API takes identity servers as input from
end users, and doesn't have any specific trusted set. It is possible some
homeservers may want to supply defaults, or reject some identity servers for
*its* users, but no homeserver is allowed to dictate which identity servers
*other* homeservers' users trust.
There is some risk of denial of service attacks by flooding homeservers or
identity servers with many requests, or much state to store. Defending against
these is left to the implementer's discretion.
.. _`identity server /isvalid`: ../identity_service/%IDENTITY_RELEASE_LABEL%.html#get-matrix-identity-v2-pubkey-isvalid