534 lines
47 KiB
ReStructuredText
534 lines
47 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.
|
|
|
|
|
|
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 privary 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/unstable.html#get-matrix-identity-api-v1-pubkey-isvalid
|
|
|
|
|
|
Sample Scenarios
|
|
----------------
|
|
|
|
Third party invites are complicated to get right, and have security considerations
|
|
that must be taken to ensure data is not accidentally leaked. These sample
|
|
scenarios are not exhaustive, however they may be of use to assist in understanding
|
|
the requirements for this particular module.
|
|
|
|
These scenarios all describe what happens in the context of a single room.
|
|
Additionally, each scenario uses the following shorthand for readability:
|
|
|
|
* HS = Homeserver (sometimes suffixed with a number to represent unique/different
|
|
homeservers in the same example)
|
|
|
|
* IS = Identity Server (sometimes also suffixed with a number)
|
|
|
|
* User = A Matrix user
|
|
|
|
* 3PUser = A third party user (email address, etc)
|
|
|
|
|
|
All of these scenarios assume that the invited third party identifier is not
|
|
bound to a matrix user.
|
|
|
|
|
|
Simple scenario: 1 homeserver, 1 identity server
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This is most likely to happen when a user invites a third party user to a private
|
|
chat, where the receiving user signs up on the same homeserver as the sender.
|
|
|
|
::
|
|
|
|
+-------+ +---------+ +-----+ +-----+
|
|
| User | | 3PUser | | HS | | IS |
|
|
+-------+ +---------+ +-----+ +-----+
|
|
| | | |
|
|
| POST /invite | |
|
|
|---------------------------->| |
|
|
| | | |
|
|
| | | GET /lookup |
|
|
| | |--------------------------------------------------------------->|
|
|
| | | |
|
|
| | | "no users" result |
|
|
| | |<---------------------------------------------------------------|
|
|
| | | |
|
|
| | | POST /store-invite |
|
|
| | |--------------------------------------------------------------->|
|
|
| | | |
|
|
| | | Information for a m.room.third_party_invite |
|
|
| | |<---------------------------------------------------------------|
|
|
| | | |
|
|
| | | Send m.room.third_party_invite to room |
|
|
| | |--------------------------------------- |
|
|
| | | | |
|
|
| | |<-------------------------------------- |
|
|
| | | |
|
|
| Complete /invite request | |
|
|
|<----------------------------| |
|
|
| | | |
|
|
| | Verify identity | |
|
|
| |---------------------------------------------------------------------------------->|
|
|
| | | |
|
|
| | | POST /3pid/onbind |
|
|
| | |<---------------------------------------------------------------|
|
|
| | | |
|
|
| | | Exchange m.room.third_party_invite for m.room.member invite |
|
|
| | |------------------------------------------------------------ |
|
|
| | | | |
|
|
| | |<----------------------------------------------------------- |
|
|
| | | |
|
|
| | | Send m.room.member to room |
|
|
| | |--------------------------- |
|
|
| | | | |
|
|
| | |<-------------------------- |
|
|
| | | |
|
|
| | | Complete /3pid/onbind request |
|
|
| | |--------------------------------------------------------------->|
|
|
| | | |
|
|
| | | Complete identity verification request |
|
|
| |<----------------------------------------------------------------------------------|
|
|
| | | |
|
|
|
|
|
|
Simple scenario: 2 homeservers (both residents of the room), 1 identity server
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This happens when a user invites a third party user to a room where two homeservers
|
|
reside. The process is similar for 3, 4, and more homeservers residing in the room.
|
|
|
|
Note: Homeserver 2 is able to exchange the third party invite for an ``m.room.member``
|
|
invite event for itself because it is a resident of the room.
|
|
|
|
::
|
|
|
|
+-------+ +---------+ +-----+ +-----+ +-----+
|
|
| User1 | | 3PUser | | HS1 | | HS2 | | IS |
|
|
+-------+ +---------+ +-----+ +-----+ +-----+
|
|
| | | | |
|
|
| POST /invite | | |
|
|
|---------------------------->| | |
|
|
| | | | |
|
|
| | | GET /lookup | |
|
|
| | |----------------------------------------------------------------------------------------------------------->|
|
|
| | | | |
|
|
| | | | "no users" result |
|
|
| | |<-----------------------------------------------------------------------------------------------------------|
|
|
| | | | |
|
|
| | | POST /store-invite | |
|
|
| | |----------------------------------------------------------------------------------------------------------->|
|
|
| | | | |
|
|
| | | | Information for a m.room.third_party_invite |
|
|
| | |<-----------------------------------------------------------------------------------------------------------|
|
|
| | | | |
|
|
| | | Send m.room.third_party_invite to room | |
|
|
| | |------------------------------------------>| |
|
|
| | | | |
|
|
| Complete /invite request | | |
|
|
|<----------------------------| | |
|
|
| | | | |
|
|
| | Verify identity | | |
|
|
| |------------------------------------------------------------------------------------------------------------------------------>|
|
|
| | | | |
|
|
| | | | POST /3pid/onbind |
|
|
| | | |<---------------------------------------------------------------|
|
|
| | | | |
|
|
| | | | Exchange m.room.third_party_invite for m.room.member invite |
|
|
| | | |------------------------------------------------------------ |
|
|
| | | | | |
|
|
| | | |<----------------------------------------------------------- |
|
|
| | | | |
|
|
| | | Send m.room.member to room | |
|
|
| | |<------------------------------------------| |
|
|
| | | | |
|
|
| | | | Complete /3pid/onbind request |
|
|
| | | |--------------------------------------------------------------->|
|
|
| | | | |
|
|
| | | | Complete identity verification request |
|
|
| |<------------------------------------------------------------------------------------------------------------------------------|
|
|
| | | | |
|
|
|
|
|
|
Scenario: 2 homeservers (1 not in the room), 1 identity server
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This is similar to the previous scenario however HS2 is not a resident of the room
|
|
and must contact HS1 to get into the room.
|
|
|
|
::
|
|
|
|
+-------+ +---------+ +-----+ +-----+ +-----+
|
|
| User1 | | 3PUser | | HS1 | | HS2 | | IS |
|
|
+-------+ +---------+ +-----+ +-----+ +-----+
|
|
| | | | |
|
|
| POST /invite | | |
|
|
|---------------------------->| | |
|
|
| | | | |
|
|
| | | GET /lookup | |
|
|
| | |----------------------------------------------------------------------------------->|
|
|
| | | | |
|
|
| | | | "no users" result |
|
|
| | |<-----------------------------------------------------------------------------------|
|
|
| | | | |
|
|
| | | POST /store-invite | |
|
|
| | |----------------------------------------------------------------------------------->|
|
|
| | | | |
|
|
| | | Information for a m.room.third_party_invite |
|
|
| | |<-----------------------------------------------------------------------------------|
|
|
| | | | |
|
|
| | | Send m.room.third_party_invite to room | |
|
|
| | |------------------------------------------------>| |
|
|
| | | | |
|
|
| Complete /invite request | | |
|
|
|<----------------------------| | |
|
|
| | | | |
|
|
| | Verify identity | | |
|
|
| |------------------------------------------------------------------------------------------------------>|
|
|
| | | | |
|
|
| | | | POST /3pid/onbind |
|
|
| | | |<---------------------------------|
|
|
| | | | |
|
|
| | | POST /exchange_third_party_invite/:roomId | |
|
|
| | |<------------------------------------------------| |
|
|
| | | | |
|
|
| | | Verify the request | |
|
|
| | |------------------- | |
|
|
| | | | | |
|
|
| | |<------------------ | |
|
|
| | | | |
|
|
| | | Send m.room.member to room | |
|
|
| | |------------------------------------------------>| |
|
|
| | | | |
|
|
| | | Complete /exchange_third_party_invite request | |
|
|
| | |------------------------------------------------>| |
|
|
| | | | |
|
|
| | | | Complete /3pid/onbind request |
|
|
| | | |--------------------------------->|
|
|
| | | | |
|
|
| | | Complete identity verification request |
|
|
| |<------------------------------------------------------------------------------------------------------|
|
|
| | | | |
|
|
|
|
|
|
Theoretical identity server replication: 2 homeservers (1 residing in the room), 2 identity servers
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The identity server specification does not currently define a way for an invite to
|
|
replicate to another identity server, however this may occur in the wider world. This
|
|
scenario defines a case where a user invites a third party user using one identity
|
|
server, however the third party user verifies their identity with a different identity
|
|
server. The identity servers are assumed to be compatible with each other.
|
|
|
|
::
|
|
|
|
+-------+ +---------+ +-----+ +-----+ +-----+ +-----+
|
|
| User1 | | 3PUser | | HS1 | | HS2 | | IS1 | | IS2 |
|
|
+-------+ +---------+ +-----+ +-----+ +-----+ +-----+
|
|
| | | | | |
|
|
| POST /invite | | | |
|
|
|---------------------------->| | | |
|
|
| | | | | |
|
|
| | | GET /lookup | | |
|
|
| | |-------------------------------------------------------->| |
|
|
| | | | | |
|
|
| | | "no users" result | |
|
|
| | |<--------------------------------------------------------| |
|
|
| | | | | |
|
|
| | | POST /store-invite | | |
|
|
| | |-------------------------------------------------------->| |
|
|
| | | | | |
|
|
| | | | | Replicate invite |
|
|
| | | | |-------------------------->|
|
|
| | | | | |
|
|
| | | Information for a m.room.third_party_invite | |
|
|
| | |<--------------------------------------------------------| |
|
|
| | | | | |
|
|
| | | Send m.room.third_party_invite to room | | |
|
|
| | |------------------------------------------------>| | |
|
|
| | | | | |
|
|
| Complete /invite request | | | |
|
|
|<----------------------------| | | |
|
|
| | | | | |
|
|
| | Verify identity | | | |
|
|
| |------------------------------------------------------------------------------------------------------->|
|
|
| | | | | |
|
|
| | | | | POST /3pid/onbind |
|
|
| | | |<----------------------------------|
|
|
| | | | | |
|
|
| | | POST /exchange_third_party_invite/:roomId | | |
|
|
| | |<------------------------------------------------| | |
|
|
| | | | | |
|
|
| | | Verify the request | | |
|
|
| | |------------------- | | |
|
|
| | | | | | |
|
|
| | |<------------------ | | |
|
|
| | | | | |
|
|
| | | Send m.room.member to room | | |
|
|
| | |------------------------------------------------>| | |
|
|
| | | | | |
|
|
| | | Complete /exchange_third_party_invite request | | |
|
|
| | |------------------------------------------------>| | |
|
|
| | | | | |
|
|
| | | | Complete /3pid/onbind request |
|
|
| | | |---------------------------------->|
|
|
| | | | | |
|
|
| | | | | Replicate verification |
|
|
| | | | |<--------------------------|
|
|
| | | | | |
|
|
| | | Complete identity verification request |
|
|
| |<-------------------------------------------------------------------------------------------------------|
|
|
| | | | | |
|
|
|
|
|
|
|
|
.. _`Identity Server /isvalid`: ../identity_service/unstable.html#get-matrix-identity-api-v1-pubkey-isvalid
|