Update 3pid spec based on new implementation

This commit is contained in:
Daniel Wagner-Hall 2015-11-05 18:11:20 +00:00
parent 232b10b0f6
commit 161441fa3a
6 changed files with 246 additions and 163 deletions

View file

@ -73,9 +73,12 @@ paths:
post:
summary: Invite a user to participate in a particular room.
description: |-
.. _invite-by-user-id-endpoint:
*Note that there are two forms of this API, which are documented separately.
This version of the API requires that the inviter knows the Matrix
identifier of the invitee.*
identifier of the invitee. The other is documented in the*
`third party invites section`_.
This API invites a user to participate in a particular room.
They do not start participating in the room until they actually join the
@ -86,6 +89,8 @@ paths:
If the user was invited to the room, the home server will append a
``m.room.member`` event to the room.
.. _third party invites section: `invite-by-third-party-id-endpoint`_
security:
- accessToken: []
parameters:
@ -132,106 +137,3 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
"/rooms/{roomId}/invite":
post:
summary: Invite a user to participate in a particular room.
description: |-
*Note that there are two forms of this API, which are documented separately.
This version of the API does not require that the inviter know the Matrix
identifier of the invitee, and instead relies on third party identifiers.
The homeserver uses an identity server to perform the mapping from
third party identifier to a Matrix identifier.*
This API invites a user to participate in a particular room.
They do not start participating in the room until they actually join the
room.
Only users currently in a particular room can invite other users to
join that room.
If the identity server did know the Matrix user identifier for the
third party identifier, the home server will append a ``m.room.member``
event to the room.
If the identity server does not know a Matrix user identifier for the
passed third party identifier, the homeserver will issue an invitation
which can be accepted upon providing proof of ownership of the third
party identifier. This is achieved by the identity server generating a
token, which it gives to the inviting homeserver. The homeserver will
add an ``m.room.third_party_invite`` event into the graph for the room,
containing that token.
When the invitee binds the invited third party identifier to a Matrix
user ID, the identity server will give the user a list of pending
invitations, each containing:
- The room ID to which they were invited
- The token given to the homeserver
- A signature of the token, signed with the identity server's private key
- The matrix user ID who invited them to the room
If a token is requested from the identity server, the home server will
append a ``m.room.third_party_invite`` event to the room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier (not alias) to which to invite the user.
required: true
x-example: "!d41d8cd:matrix.org"
- in: body
name: body
required: true
schema:
type: object
example: |-
{
"id_server": "matrix.org",
"medium": "email",
"address": "cheeky@monkey.com",
"display_name": "A very cheeky monkey"
}
properties:
id_server:
type: string
description: The hostname+port of the identity server which should be used for third party identifier lookups.
medium:
type: string
# TODO: Link to identity service spec when it eixsts
description: The kind of address being passed in the address field, for example ``email``.
address:
type: string
description: The invitee's third party identifier.
display_name:
type: string
description: A user-friendly string describing who has been invited. It should not contain the address of the invitee, to avoid leaking mappings between third party identities and matrix user IDs.
required: ["id_server", "medium", "address", "display_name"]
responses:
200:
description: The user has been invited to join the room.
examples:
application/json: |-
{}
schema:
type: object
403:
description: |-
You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:
- The invitee has been banned from the room.
- The invitee is already a member of the room.
- The inviter is not currently in the room.
- The inviter's power level is insufficient to invite users to the room.
examples:
application/json: |-
{"errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

View file

@ -0,0 +1,127 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Room Membership API for third party identifiers"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/rooms/{roomId}/invite":
post:
summary: Invite a user to participate in a particular room.
description: |-
.. _invite-by-third-party-id-endpoint:
*Note that there are two forms of this API, which are documented separately.
This version of the API does not require that the inviter know the Matrix
identifier of the invitee, and instead relies on third party identifiers.
The homeserver uses an identity server to perform the mapping from
third party identifier to a Matrix identifier. The other is documented in the*
`joining rooms section`_.
This API invites a user to participate in a particular room.
They do not start participating in the room until they actually join the
room.
Only users currently in a particular room can invite other users to
join that room.
If the identity server did know the Matrix user identifier for the
third party identifier, the home server will append a ``m.room.member``
event to the room.
If the identity server does not know a Matrix user identifier for the
passed third party identifier, the homeserver will issue an invitation
which can be accepted upon providing proof of ownership of the third
party identifier. This is achieved by the identity server generating a
token, which it gives to the inviting homeserver. The homeserver will
add an ``m.room.third_party_invite`` event into the graph for the room,
containing that token.
When the invitee binds the invited third party identifier to a Matrix
user ID, the identity server will give the user a list of pending
invitations, each containing:
- The room ID to which they were invited
- The token given to the homeserver
- A signature of the token, signed with the identity server's private key
- The matrix user ID who invited them to the room
If a token is requested from the identity server, the home server will
append a ``m.room.third_party_invite`` event to the room.
.. _joining rooms section: `invite-by-user-id-endpoint`_
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier (not alias) to which to invite the user.
required: true
x-example: "!d41d8cd:matrix.org"
- in: body
name: body
required: true
schema:
type: object
example: |-
{
"id_server": "matrix.org",
"medium": "email",
"address": "cheeky@monkey.com",
"display_name": "A very cheeky monkey"
}
properties:
id_server:
type: string
description: The hostname+port of the identity server which should be used for third party identifier lookups.
medium:
type: string
# TODO: Link to identity service spec when it eixsts
description: The kind of address being passed in the address field, for example ``email``.
address:
type: string
description: The invitee's third party identifier.
display_name:
type: string
description: A user-friendly string describing who has been invited. It should not contain the address of the invitee, to avoid leaking mappings between third party identities and matrix user IDs.
required: ["id_server", "medium", "address", "display_name"]
responses:
200:
description: The user has been invited to join the room.
examples:
application/json: |-
{}
schema:
type: object
403:
description: |-
You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:
- The invitee has been banned from the room.
- The invitee is already a member of the room.
- The inviter is not currently in the room.
- The inviter's power level is insufficient to invite users to the room.
examples:
application/json: |-
{"errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

View file

@ -3,22 +3,7 @@
"content": {
"membership": "join",
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid",
"third_party_invite": {
"token": "pc98",
"public_key": "abc123",
"key_validity_url": "https://magic.forest/verifykey",
"signed": {
"mxid": "@alice:localhost",
"token": "pc98",
"signatures": {
"magic.forest": {
"ed25519:0": "poi098"
}
}
},
"sender": "@zun:zun.soft"
}
"displayname": "Alice Margatroid"
},
"invite_room_state": [
{

View file

@ -1,7 +1,7 @@
{
"type": "object",
"title": "The current membership state of a user in the room.",
"description": "Adjusts the membership state for a user in a room. It is preferable to use the membership APIs (``/rooms/<room id>/invite`` etc) when performing membership actions rather than adjusting the state directly as there are a restricted set of valid transformations. For example, user A cannot force user B to join a room, and trying to force this state change directly will fail. \n\nThe ``third_party_invite`` property will be set if the invite was an ``m.room.third_party_invite`` event, and absent if the invite was an ``m.room.member`` event.\n\nThis event also includes an ``invite_room_state`` key **outside the** ``content`` **key**. This contains an array of ``StrippedState`` Events. These events provide information on a few select state events such as the room name.",
"description": "Adjusts the membership state for a user in a room. It is preferable to use the membership APIs (``/rooms/<room id>/invite`` etc) when performing membership actions rather than adjusting the state directly as there are a restricted set of valid transformations. For example, user A cannot force user B to join a room, and trying to force this state change directly will fail. \n\nThe ``third_party_invite`` property will be set if this invite is an ``invite`` event and is the successor of an ``m.room.third_party_invite`` event, and absent otherwise.\n\nThis event also includes an ``invite_room_state`` key **outside the** ``content`` **key**. This contains an array of ``StrippedState`` Events. These events provide information on a few select state events such as the room name.",
"allOf": [{
"$ref": "core-event-schema/state_event.json"
}],
@ -27,18 +27,6 @@
"type": "object",
"title": "Invite",
"properties": {
"token": {
"type": "string",
"description": "A token which must be correctly signed, in order to join the room."
},
"key_validity_url": {
"type": "string",
"description": "A URL which can be fetched, with querystring ``public_key=public_key``, to validate whether the key has been revoked. The URL must return a JSON object containing a boolean property named 'valid'."
},
"public_key": {
"type": "string",
"description": "A base64-encoded ed25519 key with which token must be signed."
},
"signed": {
"type": "object",
"title": "signed",
@ -58,13 +46,9 @@
}
},
"required": ["mxid", "signatures", "token"]
},
"sender": {
"type": "string",
"description": "The matrix user ID of the user who send the invite which is being used."
}
},
"required": ["token", "key_validity_url", "public_key", "sender", "signed"]
"required": ["signed"]
}
},
"required": ["membership"]

View file

@ -0,0 +1,50 @@
Guest access
================
.. _module:guest-access:
It may be desirable to allow users without a fully registered user account to
ephemerally access Matrix rooms. This module specifies limited ways of doing so.
Note that this is not currently a complete anonymous access solution; in
particular, it only allows servers to provided anonymous access to rooms in
which they are already participating, and relies on individual homeservers to
adhere to the conventions which this module sets, rather than allowing all
participating homeservers to enforce them.
Events
------
{{m_room_guest_accessibility}}
Client behaviour
----------------
A client can register for guest access using the FOO endpoint. From that point
on, they can interact with a limited subset of the existing client-server API,
as if they were a fully registered user, using the access token granted to them
by the server.
These users are only allowed to make calls in relation to rooms which have the
``m.room.history_visibility`` event set to ``world_readable``.
The APIs they are allowed to hit are:
/rooms/{roomId}/messages
/rooms/{roomId}/state
/rooms/{roomId}/state/{eventType}/{stateKey}
/events
Server behaviour
----------------
Does the server need to handle any of the new events in a special way (e.g.
typing timeouts, presence). Advice on how to persist events and/or requests are
recommended to aid implementation. Federation-specific logic should be included
here.
Security considerations
-----------------------
This includes privacy leaks: for example leaking presence info. How do
misbehaving clients or servers impact this module? This section should always be
included, if only to say "we've thought about it but there isn't anything to do
here".

View file

@ -1,27 +1,30 @@
Third party invites
===================
.. _module: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.
that identifier:
If it is not, the homeserver asks the identity server to record the details of
the invitation, and to notify the client 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 homeserver.
- If it is, an invite is simply issued for that user.
If a client then tries to join the room in the future, it will be allowed to if
it presents both the token, and a signature of that token from the identity
server which can be verified with the public key.
- 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
------
@ -33,15 +36,18 @@ Client behaviour
A client asks a server to invite a user by their third party identifier.
{{third_party_membership_http_api}}
Server behaviour
----------------
All homeservers MUST verify the signature in the event's
``content.third_party_invite.signed`` object.
If a client of the current homeserver is joining by an
``m.room.third_party_invite``, that homesever MUST validate that the public
key used for signing is still valid, by checking ``key_validity_url``. It does
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
@ -84,24 +90,24 @@ membership is questionable.
For example:
If room R has two participating homeservers, H1, H2
#. Room R has two participating homeservers, H1, H2
And user A on H1 invites a third party identifier to room R
#. 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.
#. 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, they are told about the
invite, and ask their homeserver, H3, to join 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 in the event's
``content.third_party_invite.signed`` object.
#. 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``.
#. 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 join event to the room, and H3
begins participating in the room. H2 *must* accept this event.
#. Having validated these things, H1 writes the invite event to the room, and H3
begins participating in the room. H2 *must* accept this event.
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.
@ -112,3 +118,32 @@ 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, when implementing this API care should be taken to avoid
adding links between these two identifiers as room events. This mapping can be
unintentionally created by specifying the third party identifier in the
``display_name`` field of the ``m.room.third_party_invite`` event, and then
observing which matrix user ID joins the room using that invite. Clients SHOULD
set ``display_name`` to a value other than the third party identifier, e.g. the
invitee's common name.
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.