In MSC3906 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)
To enable MSC3906 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 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.
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.
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.
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. 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.
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.
A typical flow might look like this where device A is initiating the rendezvous with device B:
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
HTTP request headers:
Content-Length
- requiredContent-Type
- optional, server should assumeapplication/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 created400 Bad Request
(M_MISSING_PARAM
) - noContent-Length
was provided.403 Forbidden
(M_FORBIDDEN
) - forbidden by server policy413 Payload Too Large
(M_TOO_LARGE
) - the supplied payload is too large429 Too Many Requests
(M_UNKNOWN
) - the request has been rate limited307 Temporary Redirect
- if the request should be served from somewhere else specified in theLocation
response header
n.b. the relatively unusual 307 Temporary Redirect
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 serverX-Max-Bytes
- required, the maximum allowed bytes for the payloadETag
- required, ETag for the current payload at the rendezvous point as per RFC7232Expires
- required, the expiry time of the rendezvous as per RFC7234Last-Modified
- required, the last modified date of the payload as per RFC7232
Example response headers:
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
HTTP request headers:
Content-Length
- requiredContent-Type
- optional, server should assumeapplication/octet-stream
if not specifiedIf-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 updated400 Bad Request
(M_MISSING_PARAM
) - a required header was not provided.400 Bad Request
(M_INVALID_PARAM
) - a malformedETag
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 match413 Payload Too Large
(M_TOO_LARGE
) - the supplied payload is too large429 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 RFC7232Expires
- required, the expiry time of the rendezvous as per RFC7233Last-Modified
- required, the last modified date of the payload as per RFC7232
HTTP request headers:
If-None-Match
- optional, as per RFC7232 server will only return data if given ETag does not match
HTTP response codes, and Matrix error codes:
200 OK
- payload returned304 Not Modified
- whenIf-None-Match
is supplied and the ETag does not match404 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 -
Expires
- required, the expiry time of the rendezvous as per RFC7233 -
Last-Modified
- required, the last modified date of the payload as per RFC7232 -
Content-Type
- required for200 OK
HTTP response codes:
204 No Content
- rendezvous cancelled404 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
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.
The server should enforce a maximum payload size for the payload size. It is recommended that this be no less than 10KB.
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.
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.
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:
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 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.
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:
- If the client is already logged in: try and use current homeserver.
- If the client is not logged in and it is known which homeserver the user wants to connect to: try and use that homeserver.
- Otherwise use a default server.
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.
The combination of this proposal and MSC3903 look similar in some regards to the existing 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?
Try and do something with STUN or TURN or COAP.
Rather than requiring the devices to poll for updates, "long-polling" could be used instead similar to /sync
.
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.
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
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:
{
"versions": ["r0.6.0"],
"unstable_features": {
"org.matrix.msc3886": true
}
}
None, although it's intended to be used with MSC3903 and MSC3906.
This proposal was influenced by https://wiki.mozilla.org/Services/KeyExchange which also has some helpful discussion around DoS mitigation.