354 lines
15 KiB
Markdown
354 lines
15 KiB
Markdown
# MSC3886: Simple client rendezvous capability
|
|
|
|
In [MSC3906](https://github.com/matrix-org/matrix-spec-proposals/pull/3906) a proposal is made to allow a user to login on a new device using an existing device by means of scanning a
|
|
QR code.
|
|
|
|
In order to facilitate this the two devices need some bi-directional communication channel which they can use to exchange
|
|
information such as:
|
|
|
|
- the homeserver being used
|
|
- the user ID
|
|
- facilitation of issuing a new access token
|
|
- device ID for end-to-end encryption
|
|
- data for establishing a secure communication channel (e.g. via
|
|
[MSC3903](https://github.com/matrix-org/matrix-spec-proposals/pull/3903))
|
|
|
|
To enable [MSC3906](https://github.com/matrix-org/matrix-spec-proposals/pull/3906) and support any future proposals this MSC proposes a simple HTTP based protocol that can be used to
|
|
establish a direct communication channel between two IP connected devices.
|
|
|
|
This channel [SHOULD be considered untrusted](#confidentiality-of-data)
|
|
by both devices, and SHOULD NOT be used to transmit sensitive data in cleartext.
|
|
|
|
It will work with devices behind NAT. It doesn't require homeserver administrators to deploy a separate server.
|
|
|
|
## Proposal
|
|
|
|
It is proposed that a general purpose HTTP based protocol be used to establish ephemeral bi-directional communication
|
|
channels over which arbitrary data can be exchanged.
|
|
|
|
Please note that it is intentional that this protocol does nothing to ensure the integrity of the data exchanged at a rendezvous.
|
|
|
|
### High-level description
|
|
|
|
Suppose that Device A wants to establish communications with Device B. A can do
|
|
so by creating a _rendezvous session_ via a `POST /_matrix/client/rendezvous`
|
|
call to an appropriate server. Its response includes an HTTP _rendezvous URL_
|
|
which should be shared out-of-band with device B. (This URL may be located on a
|
|
different domain to the initial `POST`.)
|
|
|
|
The rendezvous URL points to an arbitrary data resource ("the payload"), which
|
|
is initially populated using data from A's `POST` request. There are no
|
|
restrictions on the payload itself, but the rendezvous server SHOULD impose a
|
|
maximum size limit. The payload may be retrieved (`GET`) and updated (`PUT`) by
|
|
anyone—A, B, or a third party—who is able to reach the rendezvous URL.
|
|
|
|
In this way, A and B can communicate by repeatedly inspecting and updating the
|
|
payload pointed at the rendezvous URL.
|
|
|
|
#### The update mechanism
|
|
|
|
Every update request MUST include an `ETag` header, whose value is supplied
|
|
the `ETag` header in the last `GET` response seen by the requester. (The
|
|
initiating device may also use the `ETag` supplied in the initial `POST`
|
|
response to immediately update the payload.) Updates will succeed only if the
|
|
supplied `ETag` matches the server's current revision of the payload. This
|
|
prevents concurrent writes to the payload.
|
|
|
|
The `ETag` header is standard, described by
|
|
[RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-etag). In this
|
|
proposal we only accept strong, single-valued ETag values; anything else
|
|
constitutes a malformed request.
|
|
|
|
There is no mechanism to retrieve previous payloads after an update.
|
|
|
|
#### Expiry
|
|
|
|
The rendezvous session (i.e. the payload) SHOULD expire after a period of time
|
|
communicated to clients via the `Expires` header. After this point, any
|
|
further attempts to query or update the payload MUST fail. The expiry time
|
|
SHOULD be extended every time the payload is updated. The rendezvous session can
|
|
be manually expired with a `DELETE` call to the rendezvous URL.
|
|
|
|
### Example
|
|
|
|
A typical flow might look like this where device A is initiating the rendezvous with device B:
|
|
|
|
```mermaid
|
|
|
|
sequenceDiagram
|
|
participant A as Device A
|
|
participant R as Rendezvous Server
|
|
participant B as Device B
|
|
Note over A: Device A determines which rendezvous server to use
|
|
|
|
A->>+R: POST /rendezvous Hello from A
|
|
R->>-A: 201 Created Location: /abc-def-123-456
|
|
|
|
A-->>B: Rendezvous URI between clients, perhaps as QR code: e.g. https://rendzvous-server/abc-def-123-456
|
|
|
|
Note over A: Device A starts polling for contact at the rendezvous
|
|
|
|
B->>+R: GET <rendezvous URI>
|
|
R->>-B: 200 OK Hello from A
|
|
|
|
loop Device A polls for rendezvous updates
|
|
A->>+R: GET <rendezvous URI> If-None-Match: <ETag>
|
|
R->>-A: 304 Not Modified
|
|
end
|
|
|
|
B->>+R: PUT <rendezvous URI> Hello from B
|
|
R->>-B: 202 Accepted
|
|
|
|
Note over A,B: Rendezvous now established
|
|
```
|
|
|
|
### Protocol
|
|
|
|
#### Create a new rendezvous point: `POST /_matrix/client/rendezvous`
|
|
|
|
HTTP request headers:
|
|
|
|
- `Content-Length` - required
|
|
- `Content-Type` - optional, server should assume `application/octet-stream` if not specified
|
|
|
|
HTTP request body:
|
|
|
|
- any data up to maximum size allowed by the server
|
|
|
|
HTTP response codes, and Matrix error codes:
|
|
|
|
- `201 Created` - rendezvous created
|
|
- `400 Bad Request` (`M_MISSING_PARAM`) - no `Content-Length` was provided.
|
|
- `403 Forbidden` (`M_FORBIDDEN`) - forbidden by server policy
|
|
- `413 Payload Too Large` (`M_TOO_LARGE`) - the supplied payload is too large
|
|
- `429 Too Many Requests` (`M_UNKNOWN`) - the request has been rate limited
|
|
- `307 Temporary Redirect` - if the request should be served from somewhere else specified in the `Location` response header
|
|
|
|
n.b. the relatively unusual [`307 Temporary Redirect`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307) response
|
|
code has been chosen explicitly for the behaviour of ensuring that the method and body will not change whilst the user-agent
|
|
follows the redirect. For this reason, no other `30x` response codes are allowed.
|
|
|
|
HTTP response headers for `201 Created`:
|
|
|
|
- `Location` - required, the allocated rendezvous URI which can be on a different server
|
|
- `X-Max-Bytes` - required, the maximum allowed bytes for the payload
|
|
- `ETag` - required, ETag for the current payload at the rendezvous point as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.etag)
|
|
- `Expires` - required, the expiry time of the rendezvous as per [RFC7234](https://httpwg.org/specs/rfc7234.html#header.expires)
|
|
- `Last-Modified` - required, the last modified date of the payload as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.last-modified)
|
|
|
|
Example response headers:
|
|
|
|
```http
|
|
Location: /abcdEFG12345
|
|
X-Max-Bytes: 10240
|
|
ETag: VmbxF13QDusTgOCt8aoa0d2PQcnBOXeIxEqhw5aQ03o=
|
|
Expires: Wed, 07 Sep 2022 14:28:51 GMT
|
|
Last-Modified: Wed, 07 Sep 2022 14:27:51 GMT
|
|
```
|
|
|
|
#### Update payload at rendezvous point: `PUT <rendezvous URI>`
|
|
|
|
HTTP request headers:
|
|
|
|
- `Content-Length` - required
|
|
- `Content-Type` - optional, server should assume `application/octet-stream` if not specified
|
|
- `If-Match` - required. The ETag of the last payload seen by the requesting device.
|
|
|
|
if not specified
|
|
|
|
HTTP request body:
|
|
|
|
- any data up to maximum size allowed by the server
|
|
|
|
HTTP response codes, and Matrix error codes:
|
|
|
|
- `202 Accepted` - payload updated
|
|
- `400 Bad Request` (`M_MISSING_PARAM`) - a required header was not provided.
|
|
- `400 Bad Request` (`M_INVALID_PARAM`) - a malformed [`ETag`](#the-update-mechanism) header was provided.
|
|
- `404 Not Found` (`M_NOT_FOUND`) - rendezvous URI is not valid (it could have expired)
|
|
- `412 Precondition Failed` (`M_CONCURRENT_WRITE`, **a new error code**) - when the ETag does not match
|
|
- `413 Payload Too Large` (`M_TOO_LARGE`) - the supplied payload is too large
|
|
- `429 Too Many Requests` (`M_UNKNOWN`) - the request has been rate limited
|
|
|
|
HTTP response headers for `202 Accepted` and `412 Precondition Failed`:
|
|
|
|
- `ETag` - required, ETag for the current payload at the rendezvous point as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.etag)
|
|
- `Expires` - required, the expiry time of the rendezvous as per [RFC7233](https://httpwg.org/specs/rfc7234.html#header.expires)
|
|
- `Last-Modified` - required, the last modified date of the payload as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.last-modified)
|
|
|
|
#### Get payload from rendezvous point: `GET <rendezvous URI>`
|
|
|
|
HTTP request headers:
|
|
|
|
- `If-None-Match` - optional, as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.if-none-match) server will
|
|
only return data if given ETag does not match
|
|
|
|
HTTP response codes, and Matrix error codes:
|
|
|
|
- `200 OK` - payload returned
|
|
- `304 Not Modified` - when `If-None-Match` is supplied and the ETag does not match
|
|
- `404 Not Found` (`M_NOT_FOUND`) - rendezvous URI is not valid (it could have expired)
|
|
- `429 Too Many Requests` (`M_UNKNOWN`)- the request has been rate limited
|
|
|
|
HTTP response headers for `200 OK` and `304 Not Modified`:
|
|
|
|
- `ETag` - required, ETag for the current payload at the rendezvous point as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.etag)
|
|
- `Expires` - required, the expiry time of the rendezvous as per [RFC7233](https://httpwg.org/specs/rfc7234.html#header.expires)
|
|
- `Last-Modified` - required, the last modified date of the payload as per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.last-modified)
|
|
|
|
- `Content-Type` - required for `200 OK`
|
|
|
|
#### Cancel a rendezvous: `DELETE <rendezvous URI>`
|
|
|
|
HTTP response codes:
|
|
|
|
- `204 No Content` - rendezvous cancelled
|
|
- `404 Not Found` (`M_NOT_FOUND`) - rendezvous URI is not valid (it could have expired)
|
|
- `429 Too Many Requests` (`M_UNKNOWN`)- the request has been rate limited
|
|
|
|
### Authentication
|
|
|
|
These API endpoints do not require authentication. This is because the protocol is explicitly treated as untrusted,
|
|
with trust established at a higher level outside the scope of the present proposal.
|
|
|
|
### Maximum payload size
|
|
|
|
The server should enforce a maximum payload size for the payload size. It is recommended that this be no less than 10KB.
|
|
|
|
### Maximum duration of a rendezvous
|
|
|
|
The rendezvous only needs to persist for the duration of the handshake. So a timeout such as 30 seconds is adequate.
|
|
|
|
Clients should handle the case of the rendezvous being cancelled or timed out by the server.
|
|
|
|
### ETags
|
|
|
|
The ETag generated should be unique to the rendezvous point and the last modified time so that two clients can
|
|
distinguish between identical payloads sent by either client.
|
|
|
|
### CORS
|
|
|
|
To support usage from web browsers, the server should allow CORS requests to the `/rendezvous` endpoint from any
|
|
origin and expose the `ETag`, `Location` and `X-Max-Bytes` headers as:
|
|
|
|
```http
|
|
Access-Control-Allow-Headers: Content-Type,If-Match,If-None-Match
|
|
Access-Control-Allow-Methods: GET,PUT,POST,DELETE
|
|
Access-Control-Allow-Origin: *
|
|
Access-Control-Expose-Headers: ETag,Location,X-Max-Bytes
|
|
```
|
|
|
|
Currently the [spec](https://spec.matrix.org/v1.4/client-server-api/#web-browser-clients) specifies a single set of
|
|
CORS headers to be used. Therefore, care will be required to make it clear in the spec that the headers will
|
|
vary depending on the endpoint.
|
|
|
|
### Choice of server
|
|
|
|
Ultimately it will be up to the Matrix client implementation to decide which rendezvous server to use.
|
|
|
|
However, it is suggested that the following logic is used by the device/client to choose the rendezvous server in order
|
|
of preference:
|
|
|
|
1. If the client is already logged in: try and use current homeserver.
|
|
1. If the client is not logged in and it is known which homeserver the user wants to connect to: try and use that homeserver.
|
|
1. Otherwise use a default server.
|
|
|
|
## Potential issues
|
|
|
|
Because this is an entirely new set of functionality it should not cause issue with any existing Matrix functions or capabilities.
|
|
|
|
The proposed protocol requires the devices to have IP connectivity to the server which might not be the case in P2P scenarios.
|
|
|
|
## Alternatives
|
|
|
|
### Send-to-Device messaging
|
|
|
|
The combination of this proposal and [MSC3903](https://github.com/matrix-org/matrix-spec-proposals/pull/3903) look similar in
|
|
some regards to the existing [Send-to-device messaging](https://spec.matrix.org/v1.6/client-server-api/#send-to-device-messaging)
|
|
capability.
|
|
|
|
Whilst to-device messaging already provides a mechanism for secure communication
|
|
between two Matrix clients/devices, a key consideration for the anticipated
|
|
login with QR capability is that one of the clients is not yet authenticated with
|
|
a Homeserver.
|
|
|
|
Furthermore the client might not know which Homeserver the user wishes to
|
|
connect to.
|
|
|
|
Conceptually, one could create a new type of "guest" login that would allow the
|
|
unauthenticated client to connect to a Homeserver for the purposes of
|
|
communicating with an existing authenticated client via to-device messages.
|
|
|
|
Some considerations for this:
|
|
|
|
Where the "actual" Homeserver is not known then the "guest" Homeserver nominated
|
|
by the new client would need to be federated with the "actual" Homeserver.
|
|
|
|
The "guest" Homeserver would probably want to automatically clean up the "guest"
|
|
accounts after a short period of time.
|
|
|
|
The "actual" Homeserver operator might not want to open up full "guest" access
|
|
so a second type of "guest" account might be required.
|
|
|
|
Does the new device/client need to accept the T&Cs of the "guest" Homeserver?
|
|
|
|
### Other existing protocols
|
|
|
|
Try and do something with STUN or TURN or [COAP](http://coap.technology/).
|
|
|
|
### Implementation details
|
|
|
|
Rather than requiring the devices to poll for updates, "long-polling" could be used instead similar to `/sync`.
|
|
|
|
## Security considerations
|
|
|
|
### Confidentiality of data
|
|
|
|
Whilst the data transmitted can be encrypted in transit via HTTP/TLS the rendezvous server does have visibility over the
|
|
data and can also perform man in the middle attacks.
|
|
|
|
As such, for the purposes of authentication and end-to-end encryption the channel should be treated as untrusted and some
|
|
form of secure layer should be used on top of the channel such as a Diffie-Hellman key exchange.
|
|
|
|
### Denial of Service attack surface
|
|
|
|
Because the protocol allows for the creation of arbitrary channels and storage of arbitrary data, it is possible to use
|
|
it as a denial of service attack surface.
|
|
|
|
As such, the following standard mitigations such as the following may be deemed appropriate by homeserver implementations
|
|
and administrators:
|
|
|
|
- rate limiting of requests
|
|
- imposing a low maximum payload size (e.g. kilobytes not megabytes)
|
|
- limiting the number of concurrent channels
|
|
|
|
## Unstable prefix
|
|
|
|
While this feature is in development the new endpoint should be exposed using the following unstable prefix:
|
|
|
|
- `/_matrix/client/unstable/org.matrix.msc3886/rendezvous`
|
|
|
|
Additionally, the feature is to be advertised as unstable feature in the `GET /_matrix/client/versions`
|
|
response, with the key `org.matrix.msc3886` set to `true`. So, the response could look then as
|
|
following:
|
|
|
|
```json
|
|
{
|
|
"versions": ["r0.6.0"],
|
|
"unstable_features": {
|
|
"org.matrix.msc3886": true
|
|
}
|
|
}
|
|
```
|
|
|
|
## Dependencies
|
|
|
|
None, although it's intended to be used with
|
|
[MSC3903](https://github.com/matrix-org/matrix-spec-proposals/pull/3903) and
|
|
[MSC3906](https://github.com/matrix-org/matrix-spec-proposals/pull/3906).
|
|
|
|
## Credits
|
|
|
|
This proposal was influenced by https://wiki.mozilla.org/Services/KeyExchange which also has some helpful discussion
|
|
around DoS mitigation.
|