|
|
|
@ -40,6 +40,7 @@ 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
|
|
|
|
|
------
|
|
|
|
|
|
|
|
|
@ -55,41 +56,79 @@ A client asks a server to invite a user by their third party identifier.
|
|
|
|
|
Server behaviour
|
|
|
|
|
----------------
|
|
|
|
|
|
|
|
|
|
All homeservers MUST verify the signature in the event's
|
|
|
|
|
``content.third_party_invite.signed`` object.
|
|
|
|
|
|
|
|
|
|
When a homeserver inserts an ``m.room.member`` ``invite`` event into the graph
|
|
|
|
|
because of an ``m.room.third_party_invite`` event,
|
|
|
|
|
that homesever MUST validate that the public
|
|
|
|
|
key used for signing is still valid, by checking ``key_validity_url`` from the ``m.room.third_party_invite``. It does
|
|
|
|
|
this by making an HTTP GET request to ``key_validity_url``:
|
|
|
|
|
|
|
|
|
|
.. TODO: Link to identity server spec when it exists
|
|
|
|
|
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 | |
|
|
|
|
|
|<------------------------------------| |
|
|
|
|
|
| | |
|
|
|
|
|
|
|
|
|
|
Schema::
|
|
|
|
|
|
|
|
|
|
=> GET $key_validity_url?public_key=$public_key
|
|
|
|
|
<= HTTP/1.1 200 OK
|
|
|
|
|
{
|
|
|
|
|
"valid": true|false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Example::
|
|
|
|
|
|
|
|
|
|
key_validity_url = https://identity.server/is_valid
|
|
|
|
|
public_key = ALJWLAFQfqffQHFqFfeqFUOEHf4AIHfefh4
|
|
|
|
|
=> GET https://identity.server/is_valid?public_key=ALJWLAFQfqffQHFqFfeqFUOEHf4AIHfefh4
|
|
|
|
|
<= HTTP/1.1 200 OK
|
|
|
|
|
{
|
|
|
|
|
"valid": true
|
|
|
|
|
}
|
|
|
|
|
All homeservers MUST verify the signature in the event's
|
|
|
|
|
``content.third_party_invite.signed`` object.
|
|
|
|
|
|
|
|
|
|
with the querystring
|
|
|
|
|
?public_key=``public_key``. A JSON object will be returned.
|
|
|
|
|
The invitation is valid if the object contains a key named ``valid`` which is
|
|
|
|
|
``true``. Otherwise, the invitation MUST be rejected. This request is
|
|
|
|
|
idempotent and may be retried by the homeserver.
|
|
|
|
|
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
|
|
|
|
@ -99,29 +138,84 @@ validate that the public key used for signing is still valid, by checking
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
the room. They may, however, indicate to their clients that a member's
|
|
|
|
|
membership is questionable.
|
|
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
|
|
#. Room R has two participating homeservers, H1, H2
|
|
|
|
|
|
|
|
|
|
#. User A on H1 invites a third party identifier to room R
|
|
|
|
|
|
|
|
|
|
#. H1 asks the identity server for a binding to a Matrix user ID, and has none,
|
|
|
|
|
so issues an ``m.room.third_party_invite`` event to the room.
|
|
|
|
|
|
|
|
|
|
#. When the third party user validates their identity, their homeserver H3
|
|
|
|
|
is notified and attempts to issue an ``m.room.member`` event to participate
|
|
|
|
|
in the room.
|
|
|
|
|
|
|
|
|
|
#. H3 validates the signature given to it by the identity server.
|
|
|
|
|
|
|
|
|
|
#. H3 then asks H1 to join it to the room. H1 *must* validate the ``signed``
|
|
|
|
|
property *and* check ``key_validity_url``.
|
|
|
|
|
|
|
|
|
|
#. Having validated these things, H1 writes the invite event to the room, and H3
|
|
|
|
|
begins participating in the room. H2 *must* accept this event.
|
|
|
|
|
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 Server 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.
|
|
|
|
@ -158,3 +252,6 @@ 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
|
|
|
|
|