|
|
|
.. 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.
|
|
|
|
|
|
|
|
Client-Server API
|
|
|
|
=================
|
|
|
|
|
|
|
|
{{unstable_warning_block_CLIENT_RELEASE_LABEL}}
|
|
|
|
|
|
|
|
The client-server API provides a simple lightweight API to let clients send
|
|
|
|
messages, control rooms and synchronise conversation history. It is designed to
|
|
|
|
support both lightweight clients which store no state and lazy-load data from
|
|
|
|
the server as required - as well as heavyweight clients which maintain a full
|
|
|
|
local persistent copy of server state.
|
|
|
|
|
|
|
|
.. contents:: Table of Contents
|
|
|
|
.. sectnum::
|
|
|
|
|
|
|
|
Changelog
|
|
|
|
---------
|
|
|
|
|
|
|
|
.. topic:: Version: %CLIENT_RELEASE_LABEL%
|
|
|
|
{{client_server_changelog}}
|
|
|
|
|
|
|
|
This version of the specification is generated from
|
|
|
|
`matrix-doc <https://github.com/matrix-org/matrix-doc>`_ as of Git commit
|
|
|
|
`{{git_version}} <https://github.com/matrix-org/matrix-doc/tree/{{git_rev}}>`_.
|
|
|
|
|
|
|
|
For the full historical changelog, see
|
|
|
|
https://github.com/matrix-org/matrix-doc/blob/master/changelogs/client_server.rst
|
|
|
|
|
|
|
|
Other versions of this specification
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
The following other versions are also available, in reverse chronological order:
|
|
|
|
|
|
|
|
- `HEAD <https://matrix.org/docs/spec/client_server/unstable.html>`_: Includes all changes since the latest versioned release.
|
|
|
|
- `r0.4.0 <https://matrix.org/docs/spec/client_server/r0.4.0.html>`_
|
|
|
|
- `r0.3.0 <https://matrix.org/docs/spec/client_server/r0.3.0.html>`_
|
|
|
|
- `r0.2.0 <https://matrix.org/docs/spec/client_server/r0.2.0.html>`_
|
|
|
|
- `r0.1.0 <https://matrix.org/docs/spec/client_server/r0.1.0.html>`_
|
|
|
|
- `r0.0.1 <https://matrix.org/docs/spec/r0.0.1/client_server.html>`_
|
|
|
|
- `r0.0.0 <https://matrix.org/docs/spec/r0.0.0/client_server.html>`_
|
|
|
|
- `Legacy <https://matrix.org/docs/spec/legacy/#client-server-api>`_: The last draft before the spec was formally released in version r0.0.0.
|
|
|
|
|
|
|
|
|
|
|
|
API Standards
|
|
|
|
-------------
|
|
|
|
|
|
|
|
.. TODO
|
|
|
|
Need to specify any HMAC or access_token lifetime/ratcheting tricks
|
|
|
|
We need to specify capability negotiation for extensible transports
|
|
|
|
|
|
|
|
The mandatory baseline for client-server communication in Matrix is exchanging
|
|
|
|
JSON objects over HTTP APIs. HTTPS is recommended for communication, although
|
|
|
|
HTTP may be supported as a fallback to support basic
|
|
|
|
HTTP clients. More efficient optional transports
|
|
|
|
will in future be supported as optional extensions - e.g. a
|
|
|
|
packed binary encoding over stream-cipher encrypted TCP socket for
|
|
|
|
low-bandwidth/low-roundtrip mobile usage. For the default HTTP transport, all
|
|
|
|
API calls use a Content-Type of ``application/json``. In addition, all strings
|
|
|
|
MUST be encoded as UTF-8. Clients are authenticated using opaque
|
|
|
|
``access_token`` strings (see `Client Authentication`_ for details), passed as a
|
|
|
|
query string parameter on all requests.
|
|
|
|
|
|
|
|
The names of the API endpoints for the HTTP transport follow a convention of
|
|
|
|
using underscores to separate words (for example ``/delete_devices``). The key
|
|
|
|
names in JSON objects passed over the API also follow this convention.
|
|
|
|
|
|
|
|
.. NOTE::
|
|
|
|
There are a few historical exceptions to this rule, such as
|
|
|
|
``/createRoom``. A future version of this specification will address the
|
|
|
|
inconsistency.
|
|
|
|
|
|
|
|
|
|
|
|
Any errors which occur at the Matrix API level MUST return a "standard error
|
|
|
|
response". This is a JSON object which looks like:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"errcode": "<error code>",
|
|
|
|
"error": "<error message>"
|
|
|
|
}
|
|
|
|
|
|
|
|
The ``error`` string will be a human-readable error message, usually a sentence
|
|
|
|
explaining what went wrong. The ``errcode`` string will be a unique string
|
|
|
|
which can be used to handle an error message e.g. ``M_FORBIDDEN``. These error
|
|
|
|
codes should have their namespace first in ALL CAPS, followed by a single _ to
|
|
|
|
ease separating the namespace from the error code. For example, if there was a
|
|
|
|
custom namespace ``com.mydomain.here``, and a
|
|
|
|
``FORBIDDEN`` code, the error code should look like
|
|
|
|
``COM.MYDOMAIN.HERE_FORBIDDEN``. There may be additional keys depending on the
|
|
|
|
error, but the keys ``error`` and ``errcode`` MUST always be present.
|
|
|
|
|
|
|
|
Errors are generally best expressed by their error code rather than the HTTP
|
|
|
|
status code returned. When encountering the error code ``M_UNKNOWN``, clients
|
|
|
|
should prefer the HTTP status code as a more reliable reference for what the
|
|
|
|
issue was. For example, if the client receives an error code of ``M_NOT_FOUND``
|
|
|
|
but the request gave a 400 Bad Request status code, the client should treat
|
|
|
|
the error as if the resource was not found. However, if the client were to
|
|
|
|
receive an error code of ``M_UNKNOWN`` with a 400 Bad Request, the client
|
|
|
|
should assume that the request being made was invalid.
|
|
|
|
|
|
|
|
The common error codes are:
|
|
|
|
|
|
|
|
:``M_FORBIDDEN``:
|
|
|
|
Forbidden access, e.g. joining a room without permission, failed login.
|
|
|
|
|
|
|
|
:``M_UNKNOWN_TOKEN``:
|
|
|
|
The access token specified was not recognised.
|
|
|
|
|
|
|
|
:``M_MISSING_TOKEN``:
|
|
|
|
No access token was specified for the request.
|
|
|
|
|
|
|
|
:``M_BAD_JSON``:
|
|
|
|
Request contained valid JSON, but it was malformed in some way, e.g. missing
|
|
|
|
required keys, invalid values for keys.
|
|
|
|
|
|
|
|
:``M_NOT_JSON``:
|
|
|
|
Request did not contain valid JSON.
|
|
|
|
|
|
|
|
:``M_NOT_FOUND``:
|
|
|
|
No resource was found for this request.
|
|
|
|
|
|
|
|
:``M_LIMIT_EXCEEDED``:
|
|
|
|
Too many requests have been sent in a short period of time. Wait a while then
|
|
|
|
try again.
|
|
|
|
|
|
|
|
:``M_UNKNOWN``:
|
|
|
|
An unknown error has occurred.
|
|
|
|
|
|
|
|
Other error codes the client might encounter are:
|
|
|
|
|
|
|
|
:``M_UNRECOGNIZED``:
|
|
|
|
The server did not understand the request.
|
|
|
|
|
|
|
|
:``M_UNAUTHORIZED``:
|
|
|
|
The request was not correctly authorized. Usually due to login failures.
|
|
|
|
|
|
|
|
:``M_USER_IN_USE``:
|
|
|
|
Encountered when trying to register a user ID which has been taken.
|
|
|
|
|
|
|
|
:``M_INVALID_USERNAME``:
|
|
|
|
Encountered when trying to register a user ID which is not valid.
|
|
|
|
|
|
|
|
:``M_ROOM_IN_USE``:
|
|
|
|
Sent when the room alias given to the ``createRoom`` API is already in use.
|
|
|
|
|
|
|
|
:``M_INVALID_ROOM_STATE``:
|
|
|
|
Sent when the initial state given to the ``createRoom`` API is invalid.
|
|
|
|
|
|
|
|
:``M_THREEPID_IN_USE``:
|
|
|
|
Sent when a threepid given to an API cannot be used because the same threepid is already in use.
|
|
|
|
|
|
|
|
:``M_THREEPID_NOT_FOUND``:
|
|
|
|
Sent when a threepid given to an API cannot be used because no record matching the threepid was found.
|
|
|
|
|
|
|
|
:``M_THREEPID_AUTH_FAILED``:
|
|
|
|
Authentication could not be performed on the third party identifier.
|
|
|
|
|
|
|
|
:``M_THREEPID_DENIED``:
|
|
|
|
The server does not permit this third party identifier. This may happen if the server only
|
|
|
|
permits, for example, email addresses from a particular domain.
|
|
|
|
|
|
|
|
:``M_SERVER_NOT_TRUSTED``:
|
|
|
|
The client's request used a third party server, eg. identity server, that this server does not trust.
|
|
|
|
|
|
|
|
:``M_UNSUPPORTED_ROOM_VERSION``:
|
|
|
|
The client's request to create a room used a room version that the server does not support.
|
|
|
|
|
|
|
|
:``M_INCOMPATIBLE_ROOM_VERSION``:
|
|
|
|
The client attempted to join a room that has a version the server does not support. Inspect the
|
|
|
|
``room_version`` property of the error response for the room's version.
|
|
|
|
|
|
|
|
:``M_BAD_STATE``:
|
|
|
|
The state change requested cannot be performed, such as attempting to unban
|
|
|
|
a user who is not banned.
|
|
|
|
|
|
|
|
:``M_GUEST_ACCESS_FORBIDDEN``:
|
|
|
|
The room or resource does not permit guests to access it.
|
|
|
|
|
|
|
|
:``M_CAPTCHA_NEEDED``:
|
|
|
|
A Captcha is required to complete the request.
|
|
|
|
|
|
|
|
:``M_CAPTCHA_INVALID``:
|
|
|
|
The Captcha provided did not match what was expected.
|
|
|
|
|
|
|
|
:``M_MISSING_PARAM``:
|
|
|
|
A required parameter was missing from the request.
|
|
|
|
|
|
|
|
:``M_INVALID_PARAM``:
|
|
|
|
A parameter that was specified has the wrong value. For example, the server
|
|
|
|
expected an integer and instead received a string.
|
|
|
|
|
|
|
|
:``M_TOO_LARGE``:
|
|
|
|
The request or entity was too large.
|
|
|
|
|
|
|
|
:``M_EXCLUSIVE``:
|
|
|
|
The resource being requested is reserved by an application service, or the
|
|
|
|
application service making the request has not created the resource.
|
|
|
|
|
|
|
|
:``M_RESOURCE_LIMIT_EXCEEDED``:
|
|
|
|
The request cannot be completed because the homeserver has reached a resource
|
|
|
|
limit imposed on it. For example, a homeserver held in a shared hosting environment
|
|
|
|
may reach a resource limit if it starts using too much memory or disk space. The
|
|
|
|
error MUST have an ``admin_contact`` field to provide the user receiving the error
|
|
|
|
a place to reach out to. Typically, this error will appear on routes which attempt
|
|
|
|
to modify state (eg: sending messages, account data, etc) and not routes which only
|
|
|
|
read state (eg: ``/sync``, get account data, etc).
|
|
|
|
|
|
|
|
.. TODO: More error codes (covered by other issues)
|
|
|
|
.. * M_CONSENT_NOT_GIVEN - GDPR: https://github.com/matrix-org/matrix-doc/issues/1512
|
|
|
|
.. * M_CANNOT_LEAVE_SERVER_NOTICE_ROOM - GDPR: https://github.com/matrix-org/matrix-doc/issues/1254
|
|
|
|
|
|
|
|
.. _sect:txn_ids:
|
|
|
|
|
|
|
|
The client-server API typically uses ``HTTP PUT`` to submit requests with a
|
|
|
|
client-generated transaction identifier. This means that these requests are
|
|
|
|
idempotent. The scope of a transaction identifier is a particular access token.
|
|
|
|
It **only** serves to identify new
|
|
|
|
requests from retransmits. After the request has finished, the ``{txnId}``
|
|
|
|
value should be changed (how is not specified; a monotonically increasing
|
|
|
|
integer is recommended).
|
|
|
|
|
|
|
|
Some API endpoints may allow or require the use of ``POST`` requests without a
|
|
|
|
transaction ID. Where this is optional, the use of a ``PUT`` request is strongly
|
|
|
|
recommended.
|
|
|
|
|
|
|
|
{{versions_cs_http_api}}
|
|
|
|
|
|
|
|
Web Browser Clients
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
It is realistic to expect that some clients will be written to be run within a
|
|
|
|
web browser or similar environment. In these cases, the homeserver should respond
|
|
|
|
to pre-flight requests and supply Cross-Origin Resource Sharing (CORS) headers on
|
|
|
|
all requests.
|
|
|
|
|
|
|
|
When a client approaches the server with a pre-flight (``OPTIONS``) request, the
|
|
|
|
server should respond with the CORS headers for that route. The recommended CORS
|
|
|
|
headers to be returned by servers on all requests are:
|
|
|
|
|
|
|
|
.. code::
|
|
|
|
|
|
|
|
Access-Control-Allow-Origin: *
|
|
|
|
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
|
|
|
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
|
|
|
|
|
|
|
|
Server Discovery
|
|
|
|
----------------
|
|
|
|
|
|
|
|
In order to allow users to connect to a Matrix server without needing to
|
|
|
|
explicitly specify the homeserver's URL or other parameters, clients SHOULD use
|
|
|
|
an auto-discovery mechanism to determine the server's URL based on a user's
|
|
|
|
Matrix ID. Auto-discovery should only be done at login time.
|
|
|
|
|
|
|
|
In this section, the following terms are used with specific meanings:
|
|
|
|
|
|
|
|
``PROMPT``
|
|
|
|
Retrieve the specific piece of information from the user in a way which
|
|
|
|
fits within the existing client user experience, if the client is inclined to
|
|
|
|
do so. Failure can take place instead if no good user experience for this is
|
|
|
|
possible at this point.
|
|
|
|
|
|
|
|
``IGNORE``
|
|
|
|
Stop the current auto-discovery mechanism. If no more auto-discovery
|
|
|
|
mechanisms are available, then the client may use other methods of
|
|
|
|
determining the required parameters, such as prompting the user, or using
|
|
|
|
default values.
|
|
|
|
|
|
|
|
``FAIL_PROMPT``
|
|
|
|
Inform the user that auto-discovery failed due to invalid/empty data and
|
|
|
|
``PROMPT`` for the parameter.
|
|
|
|
|
|
|
|
``FAIL_ERROR``
|
|
|
|
Inform the user that auto-discovery did not return any usable URLs. Do not
|
|
|
|
continue further with the current login process. At this point, valid data
|
|
|
|
was obtained, but no homeserver is available to serve the client. No further
|
|
|
|
guess should be attempted and the user should make a conscientious decision
|
|
|
|
what to do next.
|
|
|
|
|
|
|
|
Well-known URI
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
The ``.well-known`` method uses a JSON file at a predetermined location to
|
|
|
|
specify parameter values. The flow for this method is as follows:
|
|
|
|
|
|
|
|
1. Extract the server name from the user's Matrix ID by splitting the Matrix ID
|
|
|
|
at the first colon.
|
|
|
|
2. Extract the hostname from the server name.
|
|
|
|
3. Make a GET request to ``https://hostname/.well-known/matrix/client``.
|
|
|
|
|
|
|
|
a. If the returned status code is 404, then ``IGNORE``.
|
|
|
|
b. If the returned status code is not 200, or the response body is empty,
|
|
|
|
then ``FAIL_PROMPT``.
|
|
|
|
c. Parse the response body as a JSON object
|
|
|
|
|
|
|
|
i. If the content cannot be parsed, then ``FAIL_PROMPT``.
|
|
|
|
|
|
|
|
d. Extract the ``base_url`` value from the ``m.homeserver`` property. This
|
|
|
|
value is to be used as the base URL of the homeserver.
|
|
|
|
|
|
|
|
i. If this value is not provided, then ``FAIL_PROMPT``.
|
|
|
|
|
|
|
|
e. Validate the homeserver base URL:
|
|
|
|
|
|
|
|
i. Parse it as a URL. If it is not a URL, then ``FAIL_ERROR``.
|
|
|
|
ii. Clients SHOULD validate that the URL points to a valid homeserver
|
|
|
|
before accepting it by connecting to the |/_matrix/client/versions|_
|
|
|
|
endpoint, ensuring that it does not return an error, and parsing and
|
|
|
|
validating that the data conforms with the expected response
|
|
|
|
format. If any step in the validation fails, then
|
|
|
|
``FAIL_ERROR``. Validation is done as a simple check against
|
|
|
|
configuration errors, in order to ensure that the discovered address
|
|
|
|
points to a valid homeserver.
|
|
|
|
|
|
|
|
f. If the ``m.identity_server`` property is present, extract the
|
|
|
|
``base_url`` value for use as the base URL of the identity server.
|
|
|
|
Validation for this URL is done as in the step above, but using
|
|
|
|
``/_matrix/identity/api/v1`` as the endpoint to connect to. If the
|
|
|
|
``m.identity_server`` property is present, but does not have a
|
|
|
|
``base_url`` value, then ``FAIL_ERROR``.
|
|
|
|
|
|
|
|
{{wellknown_cs_http_api}}
|
|
|
|
|
|
|
|
Client Authentication
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
Most API endpoints require the user to identify themselves by presenting
|
|
|
|
previously obtained credentials in the form of an ``access_token`` query
|
|
|
|
parameter or through an Authorization Header of ``Bearer $access_token``.
|
|
|
|
An access token is typically obtained via the `Login`_ or `Registration`_ processes.
|
|
|
|
|
|
|
|
.. NOTE::
|
|
|
|
|
|
|
|
This specification does not mandate a particular format for the access
|
|
|
|
token. Clients should treat it as an opaque byte sequence. Servers are free
|
|
|
|
to choose an appropriate format. Server implementors may like to investigate
|
|
|
|
`macaroons <macaroon_>`_.
|
|
|
|
|
|
|
|
Using access tokens
|
|
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Access tokens may be provided in two ways, both of which the homeserver MUST
|
|
|
|
support:
|
|
|
|
|
|
|
|
1. Via a query string parameter, ``access_token=TheTokenHere``.
|
|
|
|
#. Via a request header, ``Authorization: Bearer TheTokenHere``.
|
|
|
|
|
|
|
|
Clients are encouraged to use the ``Authorization`` header where possible
|
|
|
|
to prevent the access token being leaked in access/HTTP logs. The query
|
|
|
|
string should only be used in cases where the ``Authorization`` header is
|
|
|
|
inaccessible for the client.
|
|
|
|
|
|
|
|
When credentials are required but missing or invalid, the HTTP call will
|
|
|
|
return with a status of 401 and the error code, ``M_MISSING_TOKEN`` or
|
|
|
|
``M_UNKNOWN_TOKEN`` respectively.
|
|
|
|
|
|
|
|
Relationship between access tokens and devices
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Client `devices`_ are closely related to access tokens. Matrix servers should
|
|
|
|
record which device each access token is assigned to, so that subsequent
|
|
|
|
requests can be handled correctly.
|
|
|
|
|
|
|
|
By default, the `Login`_ and `Registration`_ processes auto-generate a new
|
|
|
|
``device_id``. A client is also free to generate its own ``device_id`` or,
|
|
|
|
provided the user remains the same, reuse a device: in either case the client
|
|
|
|
should pass the ``device_id`` in the request body. If the client sets the
|
|
|
|
``device_id``, the server will invalidate any access token previously assigned
|
|
|
|
to that device. There is therefore at most one active access token assigned to
|
|
|
|
each device at any one time.
|
|
|
|
|
|
|
|
User-Interactive Authentication API
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Overview
|
|
|
|
<<<<<<<<
|
|
|
|
|
|
|
|
Some API endpoints require authentication that
|
|
|
|
interacts with the user. The homeserver may provide many different ways of
|
|
|
|
authenticating, such as user/password auth, login via a social network (OAuth2),
|
|
|
|
login by confirming a token sent to their email address, etc. This specification
|
|
|
|
does not define how homeservers should authorise their users but instead
|
|
|
|
defines the standard interface which implementations should follow so that ANY
|
|
|
|
client can login to ANY homeserver.
|
|
|
|
|
|
|
|
The process takes the form of one or more 'stages'. At each stage the client
|
|
|
|
submits a set of data for a given authentication type and awaits a response
|
|
|
|
from the server, which will either be a final success or a request to perform
|
|
|
|
an additional stage. This exchange continues until the final success.
|
|
|
|
|
|
|
|
For each endpoint, a server offers one or more 'flows' that the client can use
|
|
|
|
to authenticate itself. Each flow comprises a series of stages, as described
|
|
|
|
above. The client is free to choose which flow it follows. When all stages in a
|
|
|
|
flow are complete, authentication is complete and the API call succeeds.
|
|
|
|
|
|
|
|
User-interactive API in the REST API
|
|
|
|
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
|
|
|
|
In the REST API described in this specification, authentication works by the
|
|
|
|
client and server exchanging JSON dictionaries. The server indicates what
|
|
|
|
authentication data it requires via the body of an HTTP 401 response, and the
|
|
|
|
client submits that authentication data via the ``auth`` request parameter.
|
|
|
|
|
|
|
|
A client should first make a request with no ``auth`` parameter [#]_. The
|
|
|
|
homeserver returns an HTTP 401 response, with a JSON body, as follows:
|
|
|
|
|
|
|
|
.. code::
|
|
|
|
|
|
|
|
HTTP/1.1 401 Unauthorized
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
{
|
|
|
|
"flows": [
|
|
|
|
{
|
|
|
|
"stages": [ "example.type.foo", "example.type.bar" ]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"stages": [ "example.type.foo", "example.type.baz" ]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"params": {
|
|
|
|
"example.type.baz": {
|
|
|
|
"example_key": "foobar"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"session": "xxxxxx"
|
|
|
|
}
|
|
|
|
|
|
|
|
In addition to the ``flows``, this object contains some extra
|
|
|
|
information:
|
|
|
|
|
|
|
|
params
|
|
|
|
This section contains any information that the client will need to know in
|
|
|
|
order to use a given type of authentication. For each authentication type
|
|
|
|
presented, that type may be present as a key in this dictionary. For example,
|
|
|
|
the public part of an OAuth client ID could be given here.
|
|
|
|
session
|
|
|
|
This is a session identifier that the client must pass back to the home
|
|
|
|
server, if one is provided, in subsequent attempts to authenticate in the same
|
|
|
|
API call.
|
|
|
|
|
|
|
|
The client then chooses a flow and attempts to complete one of the stages. It
|
|
|
|
does this by resubmitting the same request with the addition of an ``auth``
|
|
|
|
key in the object that it submits. This dictionary contains a ``type`` key whose
|
|
|
|
value is the name of the authentication type that the client is attempting to complete.
|
|
|
|
It must also contain a ``session`` key with the value of the session key given
|
|
|
|
by the homeserver, if one was given. It also contains other keys dependent on
|
|
|
|
the auth type being attempted. For example, if the client is attempting to
|
|
|
|
complete auth type ``example.type.foo``, it might submit something like this:
|
|
|
|
|
|
|
|
.. code::
|
|
|
|
|
|
|
|
POST /_matrix/client/r0/endpoint HTTP/1.1
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
{
|
|
|
|
"a_request_parameter": "something",
|
|
|
|
"another_request_parameter": "something else",
|
|
|
|
"auth": {
|
|
|
|
"type": "example.type.foo",
|
|
|
|
"session": "xxxxxx",
|
|
|
|
"example_credential": "verypoorsharedsecret"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
If the homeserver deems the authentication attempt to be successful but still
|
|
|
|
requires more stages to be completed, it returns HTTP status 401 along with the
|
|
|
|
same object as when no authentication was attempted, with the addition of the
|
|
|
|
``completed`` key which is an array of auth types the client has completed
|
|
|
|
successfully:
|
|
|
|
|
|
|
|
.. code::
|
|
|
|
|
|
|
|
HTTP/1.1 401 Unauthorized
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
{
|
|
|
|
"completed": [ "example.type.foo" ],
|
|
|
|
"flows": [
|
|
|
|
{
|
|
|
|
"stages": [ "example.type.foo", "example.type.bar" ]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"stages": [ "example.type.foo", "example.type.baz" ]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"params": {
|
|
|
|
"example.type.baz": {
|
|
|
|
"example_key": "foobar"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"session": "xxxxxx"
|
|
|
|
}
|
|
|
|
|
|
|
|
Individual stages may require more than one request to complete, in which case
|
|
|
|
the response will be as if the request was unauthenticated with the addition of
|
|
|
|
any other keys as defined by the auth type.
|
|
|
|
|
|
|
|
If the homeserver decides that an attempt on a stage was unsuccessful, but the
|
|
|
|
client may make a second attempt, it returns the same HTTP status 401 response
|
|
|
|
as above, with the addition of the standard ``errcode`` and ``error`` fields
|
|
|
|
describing the error. For example:
|
|
|
|
|
|
|
|
.. code::
|
|
|
|
|
|
|
|
HTTP/1.1 401 Unauthorized
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
{
|
|
|
|
"errcode": "M_FORBIDDEN",
|
|
|
|
"error": "Invalid password",
|
|
|
|
"completed": [ "example.type.foo" ],
|
|
|
|
"flows": [
|
|
|
|
{
|
|
|
|
"stages": [ "example.type.foo", "example.type.bar" ]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"stages": [ "example.type.foo", "example.type.baz" ]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"params": {
|
|
|
|
"example.type.baz": {
|
|
|
|
"example_key": "foobar"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"session": "xxxxxx"
|
|
|
|
}
|
|
|
|
|
|
|
|
If the request fails for a reason other than authentication, the server returns an error
|
|
|
|
message in the standard format. For example:
|
|
|
|
|
|
|
|
.. code::
|
|
|
|
|
|
|
|
HTTP/1.1 400 Bad request
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
{
|
|
|
|
"errcode": "M_EXAMPLE_ERROR",
|
|
|
|
"error": "Something was wrong"
|
|
|
|
}
|
|
|
|
|
|
|
|
If the client has completed all stages of a flow, the homeserver performs the
|
|
|
|
API call and returns the result as normal.
|
|
|
|
|
|
|
|
Some authentication types may be completed by means other than through the
|
|
|
|
Matrix client, for example, an email confirmation may be completed when the user
|
|
|
|
clicks on the link in the email. In this case, the client retries the request
|
|
|
|
with an auth dict containing only the session key. The response to this will be
|
|
|
|
the same as if the client were attempting to complete an auth state normally,
|
|
|
|
i.e. the request will either complete or request auth, with the presence or
|
|
|
|
absence of that auth type in the 'completed' array indicating whether
|
|
|
|
that stage is complete.
|
|
|
|
|
|
|
|
.. [#] A request to an endpoint that uses User-Interactive Authentication never
|
|
|
|
succeeds without auth. Homeservers may allow requests that don't require
|
|
|
|
auth by offering a stage with only the ``m.login.dummy`` auth type, but
|
|
|
|
they must still give a 401 response to requests with no auth data.
|
|
|
|
|
|
|
|
Example
|
|
|
|
+++++++
|
|
|
|
At a high level, the requests made for an API call completing an auth flow with
|
|
|
|
three stages will resemble the following diagram::
|
|
|
|
|
|
|
|
_______________________
|
|
|
|
| Stage 0 |
|
|
|
|
| No auth |
|
|
|
|
| ___________________ |
|
|
|
|
| |_Request_1_________| | <-- Returns "session" key which is used throughout.
|
|
|
|
|_______________________|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_________V_____________
|
|
|
|
| Stage 1 |
|
|
|
|
| type: "<auth type1>" |
|
|
|
|
| ___________________ |
|
|
|
|
| |_Request_1_________| |
|
|
|
|
|_______________________|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_________V_____________
|
|
|
|
| Stage 2 |
|
|
|
|
| type: "<auth type2>" |
|
|
|
|
| ___________________ |
|
|
|
|
| |_Request_1_________| |
|
|
|
|
| ___________________ |
|
|
|
|
| |_Request_2_________| |
|
|
|
|
| ___________________ |
|
|
|
|
| |_Request_3_________| |
|
|
|
|
|_______________________|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_________V_____________
|
|
|
|
| Stage 3 |
|
|
|
|
| type: "<auth type3>" |
|
|
|
|
| ___________________ |
|
|
|
|
| |_Request_1_________| | <-- Returns API response
|
|
|
|
|_______________________|
|
|
|
|
|
|
|
|
|
|
|
|
Authentication types
|
|
|
|
++++++++++++++++++++
|
|
|
|
|
|
|
|
This specification defines the following auth types:
|
|
|
|
- ``m.login.password``
|
|
|
|
- ``m.login.recaptcha``
|
|
|
|
- ``m.login.oauth2``
|
|
|
|
- ``m.login.email.identity``
|
|
|
|
- ``m.login.token``
|
|
|
|
- ``m.login.dummy``
|
|
|
|
|
|
|
|
Password-based
|
|
|
|
<<<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.login.password``
|
|
|
|
:Description:
|
|
|
|
The client submits an identifier and secret password, both sent in plain-text.
|
|
|
|
|
|
|
|
To use this authentication type, clients should submit an auth dict as follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.password",
|
|
|
|
"identifier": {
|
|
|
|
...
|
|
|
|
},
|
|
|
|
"password": "<password>",
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
where the ``identifier`` property is a user identifier object, as described in
|
|
|
|
`Identifier types`_.
|
|
|
|
|
|
|
|
For example, to authenticate using the user's Matrix ID, clients would submit:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.password",
|
|
|
|
"identifier": {
|
|
|
|
"type": "m.id.user",
|
|
|
|
"user": "<user_id or user localpart>"
|
|
|
|
},
|
|
|
|
"password": "<password>",
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Alternatively reply using a 3PID bound to the user's account on the homeserver
|
|
|
|
using the |/account/3pid|_ API rather then giving the ``user`` explicitly as
|
|
|
|
follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.password",
|
|
|
|
"identifier": {
|
|
|
|
"type": "m.id.thirdparty",
|
|
|
|
"medium": "<The medium of the third party identifier.>",
|
|
|
|
"address": "<The third party address of the user>"
|
|
|
|
},
|
|
|
|
"password": "<password>",
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
In the case that the homeserver does not know about the supplied 3PID, the
|
|
|
|
homeserver must respond with 403 Forbidden.
|
|
|
|
|
|
|
|
Google ReCaptcha
|
|
|
|
<<<<<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.login.recaptcha``
|
|
|
|
:Description:
|
|
|
|
The user completes a Google ReCaptcha 2.0 challenge
|
|
|
|
|
|
|
|
To use this authentication type, clients should submit an auth dict as follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.recaptcha",
|
|
|
|
"response": "<captcha response>",
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Token-based
|
|
|
|
<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.login.token``
|
|
|
|
:Description:
|
|
|
|
The client submits a login token.
|
|
|
|
|
|
|
|
To use this authentication type, clients should submit an auth dict as follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.token",
|
|
|
|
"token": "<token>",
|
|
|
|
"txn_id": "<client generated nonce>",
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
The ``nonce`` should be a random string generated by the client for the
|
|
|
|
request. The same ``nonce`` should be used if retrying the request.
|
|
|
|
|
|
|
|
A client may receive a login ``token`` via some external service, such as email
|
|
|
|
or SMS. Note that a login token is separate from an access token, the latter
|
|
|
|
providing general authentication to various API endpoints.
|
|
|
|
|
|
|
|
The ``txn_id`` may be used by the server to disallow other devices from using
|
|
|
|
the token, thus providing "single use" tokens while still allowing the device
|
|
|
|
to retry the request. This would be done by tying the token to the ``txn_id``
|
|
|
|
server side, as well as potentially invalidating the token completely once the
|
|
|
|
device has successfully logged in (e.g. when we receive a request from the
|
|
|
|
newly provisioned access_token).
|
|
|
|
|
|
|
|
The server must encode the user id in the ``token``. There is therefore no need
|
|
|
|
for the client to submit a separate username.
|
|
|
|
|
|
|
|
OAuth2-based
|
|
|
|
<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.login.oauth2``
|
|
|
|
:Description:
|
|
|
|
Authentication is supported via OAuth2 URLs. This login consists of multiple
|
|
|
|
requests.
|
|
|
|
:Parameters:
|
|
|
|
``uri``: Authorization Request URI OR service selection URI. Both contain an
|
|
|
|
encoded ``redirect URI``.
|
|
|
|
|
|
|
|
The homeserver acts as a 'confidential' client for the purposes of OAuth2. If
|
|
|
|
the uri is a ``service selection URI``, it MUST point to a webpage which prompts
|
|
|
|
the user to choose which service to authorize with. On selection of a service,
|
|
|
|
this MUST link through to an ``Authorization Request URI``. If there is only one
|
|
|
|
service which the homeserver accepts when logging in, this indirection can be
|
|
|
|
skipped and the "uri" key can be the ``Authorization Request URI``.
|
|
|
|
|
|
|
|
The client then visits the ``Authorization Request URI``, which then shows the
|
|
|
|
OAuth2 Allow/Deny prompt. Hitting 'Allow' redirects to the ``redirect URI`` with
|
|
|
|
the auth code. Homeservers can choose any path for the ``redirect URI``. Once
|
|
|
|
the OAuth flow has completed, the client retries the request with the session
|
|
|
|
only, as above.
|
|
|
|
|
|
|
|
Email-based (identity server)
|
|
|
|
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.login.email.identity``
|
|
|
|
:Description:
|
|
|
|
Authentication is supported by authorising an email address with an identity
|
|
|
|
server.
|
|
|
|
|
|
|
|
Prior to submitting this, the client should authenticate with an identity
|
|
|
|
server. After authenticating, the session information should be submitted to
|
|
|
|
the homeserver.
|
|
|
|
|
|
|
|
To use this authentication type, clients should submit an auth dict as follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.email.identity",
|
|
|
|
"threepidCreds": [
|
|
|
|
{
|
|
|
|
"sid": "<identity server session id>",
|
|
|
|
"client_secret": "<identity server client secret>",
|
|
|
|
"id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Dummy Auth
|
|
|
|
<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.login.dummy``
|
|
|
|
:Description:
|
|
|
|
Dummy authentication always succeeds and requires no extra parameters. Its
|
|
|
|
purpose is to allow servers to not require any form of User-Interactive
|
|
|
|
Authentication to perform a request. It can also be used to differentiate
|
|
|
|
flows where otherwise one flow would be a subset of another flow. eg. if
|
|
|
|
a server offers flows ``m.login.recaptcha`` and ``m.login.recaptcha,
|
|
|
|
m.login.email.identity`` and the client completes the recaptcha stage first,
|
|
|
|
the auth would succeed with the former flow, even if the client was intending
|
|
|
|
to then complete the email auth stage. A server can instead send flows
|
|
|
|
``m.login.recaptcha, m.login.dummy`` and ``m.login.recaptcha,
|
|
|
|
m.login.email.identity`` to fix the ambiguity.
|
|
|
|
|
|
|
|
To use this authentication type, clients should submit an auth dict with just
|
|
|
|
the type and session, if provided:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.dummy",
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Fallback
|
|
|
|
++++++++
|
|
|
|
Clients cannot be expected to be able to know how to process every single login
|
|
|
|
type. If a client does not know how to handle a given login type, it can direct
|
|
|
|
the user to a web browser with the URL of a fallback page which will allow the
|
|
|
|
user to complete that login step out-of-band in their web browser. The URL it
|
|
|
|
should open is::
|
|
|
|
|
|
|
|
/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/<auth type>/fallback/web?session=<session ID>
|
|
|
|
|
|
|
|
Where ``auth type`` is the type name of the stage it is attempting and
|
|
|
|
``session ID`` is the ID of the session given by the homeserver.
|
|
|
|
|
|
|
|
This MUST return an HTML page which can perform this authentication stage. This
|
|
|
|
page must use the following JavaScript when the authentication has been
|
|
|
|
completed:
|
|
|
|
|
|
|
|
.. code:: javascript
|
|
|
|
|
|
|
|
if (window.onAuthDone) {
|
|
|
|
window.onAuthDone();
|
|
|
|
} else if (window.opener && window.opener.postMessage) {
|
|
|
|
window.opener.postMessage("authDone", "*");
|
|
|
|
}
|
|
|
|
|
|
|
|
This allows the client to either arrange for the global function ``onAuthDone``
|
|
|
|
to be defined in an embedded browser, or to use the HTML5 `cross-document
|
|
|
|
messaging <https://www.w3.org/TR/webmessaging/#web-messaging>`_ API, to receive
|
|
|
|
a notification that the authentication stage has been completed.
|
|
|
|
|
|
|
|
Once a client receives the notificaton that the authentication stage has been
|
|
|
|
completed, it should resubmit the request with an auth dict with just the
|
|
|
|
session ID:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"session": "<session ID>"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Example
|
|
|
|
<<<<<<<
|
|
|
|
A client webapp might use the following javascript to open a popup window which will
|
|
|
|
handle unknown login types:
|
|
|
|
|
|
|
|
.. code:: javascript
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Arguments:
|
|
|
|
* homeserverUrl: the base url of the homeserver (eg "https://matrix.org")
|
|
|
|
*
|
|
|
|
* apiEndpoint: the API endpoint being used (eg
|
|
|
|
* "/_matrix/client/%CLIENT_MAJOR_VERSION%/account/password")
|
|
|
|
*
|
|
|
|
* loginType: the loginType being attempted (eg "m.login.recaptcha")
|
|
|
|
*
|
|
|
|
* sessionID: the session ID given by the homeserver in earlier requests
|
|
|
|
*
|
|
|
|
* onComplete: a callback which will be called with the results of the request
|
|
|
|
*/
|
|
|
|
function unknownLoginType(homeserverUrl, apiEndpoint, loginType, sessionID, onComplete) {
|
|
|
|
var popupWindow;
|
|
|
|
|
|
|
|
var eventListener = function(ev) {
|
|
|
|
// check it's the right message from the right place.
|
|
|
|
if (ev.data !== "authDone" || ev.origin !== homeserverUrl) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the popup
|
|
|
|
popupWindow.close();
|
|
|
|
window.removeEventListener("message", eventListener);
|
|
|
|
|
|
|
|
// repeat the request
|
|
|
|
var requestBody = {
|
|
|
|
auth: {
|
|
|
|
session: sessionID,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
request({
|
|
|
|
method:'POST', url:apiEndpint, json:requestBody,
|
|
|
|
}, onComplete);
|
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener("message", eventListener);
|
|
|
|
|
|
|
|
var url = homeserverUrl +
|
|
|
|
"/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/" +
|
|
|
|
encodeURIComponent(loginType) +
|
|
|
|
"/fallback/web?session=" +
|
|
|
|
encodeURIComponent(sessionID);
|
|
|
|
|
|
|
|
|
|
|
|
popupWindow = window.open(url);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Identifier types
|
|
|
|
++++++++++++++++
|
|
|
|
|
|
|
|
Some authentication mechanisms use a user identifier object to identify a
|
|
|
|
user. The user identifier object has a ``type`` field to indicate the type of
|
|
|
|
identifier being used, and depending on the type, has other fields giving the
|
|
|
|
information required to identify the user as described below.
|
|
|
|
|
|
|
|
This specification defines the following identifier types:
|
|
|
|
- ``m.id.user``
|
|
|
|
- ``m.id.thirdparty``
|
|
|
|
- ``m.id.phone``
|
|
|
|
|
|
|
|
Matrix User ID
|
|
|
|
<<<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.id.user``
|
|
|
|
:Description:
|
|
|
|
The user is identified by their Matrix ID.
|
|
|
|
|
|
|
|
A client can identify a user using their Matrix ID. This can either be the
|
|
|
|
fully qualified Matrix user ID, or just the localpart of the user ID.
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
"identifier": {
|
|
|
|
"type": "m.id.user",
|
|
|
|
"user": "<user_id or user localpart>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Third-party ID
|
|
|
|
<<<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.id.thirdparty``
|
|
|
|
:Description:
|
|
|
|
The user is identified by a third-party identifier in canonicalised form.
|
|
|
|
|
|
|
|
A client can identify a user using a 3PID associated with the user's account on
|
|
|
|
the homeserver, where the 3PID was previously associated using the
|
|
|
|
|/account/3pid|_ API. See the `3PID Types`_ Appendix for a list of Third-party
|
|
|
|
ID media.
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
"identifier": {
|
|
|
|
"type": "m.id.thirdparty",
|
|
|
|
"medium": "<The medium of the third party identifier>",
|
|
|
|
"address": "<The canonicalised third party address of the user>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Phone number
|
|
|
|
<<<<<<<<<<<<
|
|
|
|
:Type:
|
|
|
|
``m.id.phone``
|
|
|
|
:Description:
|
|
|
|
The user is identified by a phone number.
|
|
|
|
|
|
|
|
A client can identify a user using a phone number associated with the user's
|
|
|
|
account, where the phone number was previously associated using the
|
|
|
|
|/account/3pid|_ API. The phone number can be passed in as entered by the
|
|
|
|
user; the homeserver will be responsible for canonicalising it. If the client
|
|
|
|
wishes to canonicalise the phone number, then it can use the
|
|
|
|
``m.id.thirdparty`` identifier type with a ``medium`` of ``msisdn`` instead.
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
"identifier": {
|
|
|
|
"type": "m.id.phone",
|
|
|
|
"country": "<The country that the phone number is from>",
|
|
|
|
"phone": "<The phone number>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Login
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
A client can obtain access tokens using the ``/login`` API.
|
|
|
|
|
|
|
|
Note that this endpoint does `not` currently use the user-interactive
|
|
|
|
authentication API.
|
|
|
|
|
|
|
|
For a simple username/password login, clients should submit a ``/login``
|
|
|
|
request as follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.password",
|
|
|
|
"identifier": {
|
|
|
|
"type": "m.id.user",
|
|
|
|
"user": "<user_id or user localpart>"
|
|
|
|
},
|
|
|
|
"password": "<password>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Alternatively, a client can use a 3PID bound to the user's account on the
|
|
|
|
homeserver using the |/account/3pid|_ API rather then giving the ``user``
|
|
|
|
explicitly, as follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.password",
|
|
|
|
"identifier": {
|
|
|
|
"medium": "<The medium of the third party identifier>",
|
|
|
|
"address": "<The canonicalised third party address of the user>"
|
|
|
|
},
|
|
|
|
"password": "<password>"
|
|
|
|
}
|
|
|
|
|
|
|
|
In the case that the homeserver does not know about the supplied 3PID, the
|
|
|
|
homeserver must respond with ``403 Forbidden``.
|
|
|
|
|
|
|
|
To log in using a login token, clients should submit a ``/login`` request as
|
|
|
|
follows:
|
|
|
|
|
|
|
|
.. code:: json
|
|
|
|
|
|
|
|
{
|
|
|
|
"type": "m.login.token",
|
|
|
|
"token": "<login token>"
|
|
|
|
}
|
|
|
|
|
|
|
|
As with `token-based`_ interactive login, the ``token`` must encode the
|
|
|
|
user ID. In the case that the token is not valid, the homeserver must respond
|
|
|
|
with ``403 Forbidden`` and an error code of ``M_FORBIDDEN``.
|
|
|
|
|
|
|
|
If the homeserver advertises ``m.login.sso`` as a viable flow, and the client
|
|
|
|
supports it, the client should redirect the user to the ``/redirect`` endpoint
|
|
|
|
for `Single Sign-On <#sso-client-login>`_. After authentication is complete, the
|
|
|
|
client will need to submit a ``/login`` request matching ``m.login.token``.
|
|
|
|
|
|
|
|
{{login_cs_http_api}}
|
|
|
|
|
|
|
|
{{logout_cs_http_api}}
|
|
|
|
|
|
|
|
Login Fallback
|
|
|
|
<<<<<<<<<<<<<<
|
|
|
|
|
|
|
|
If a client does not recognize any or all login flows it can use the fallback
|
|
|
|
login API::
|
|
|
|
|
|
|
|
GET /_matrix/static/client/login/
|
|
|
|
|
|
|
|
This returns an HTML and JavaScript page which can perform the entire login
|
|
|
|
process. The page will attempt to call the JavaScript function
|
|
|
|
``window.onLogin`` when login has been successfully completed.
|
|
|
|
|
|
|
|
.. _Registration:
|
|
|
|
|
|
|
|
Account registration and management
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
{{registration_cs_http_api}}
|
|
|
|
|
|
|
|
Notes on password management
|
|
|
|
++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
.. WARNING::
|
|
|
|
Clients SHOULD enforce that the password provided is suitably complex. The
|
|
|
|
password SHOULD include a lower-case letter, an upper-case letter, a number
|
|
|
|
and a symbol and be at a minimum 8 characters in length. Servers MAY reject
|
|
|
|
weak passwords with an error code ``M_WEAK_PASSWORD``.
|
|
|
|
|
|
|
|
|
|
|
|
Adding Account Administrative Contact Information
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
A homeserver may keep some contact information for administrative use.
|
|
|
|
This is independent of any information kept by any identity servers.
|
|
|
|
|
|
|
|
{{administrative_contact_cs_http_api}}
|
|
|
|
|
|
|
|
Current account information
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
{{whoami_cs_http_api}}
|
|
|
|
|
|
|
|
Capabilities negotiation
|
|
|
|
------------------------
|
|
|
|
|
|
|
|
A homeserver may not support certain operations and clients must be able to
|
|
|
|
query for what the homeserver can and can't offer. For example, a homeserver
|
|
|
|
may not support users changing their password as it is configured to perform
|
|
|
|
authentication against an external system.
|
|
|
|
|
|
|
|
The capabilities advertised through this system are intended to advertise
|
|
|
|
functionality which is optional in the API, or which depend in some way on
|
|
|
|
the state of the user or server. This system should not be used to advertise
|
|
|
|
unstable or experimental features - this is better done by the ``/versions``
|
|
|
|
endpoint.
|
|
|
|
|
|
|
|
Some examples of what a reasonable capability could be are:
|
|
|
|
|
|
|
|
* Whether the server supports user presence.
|
|
|
|
|
|
|
|
* Whether the server supports optional features, such as the user or room
|
|
|
|
directories.
|
|
|
|
|
|
|
|
* The rate limits or file type restrictions imposed on clients by the server.
|
|
|
|
|
|
|
|
Some examples of what should **not** be a capability are:
|
|
|
|
|
|
|
|
* Whether the server supports a feature in the ``unstable`` specification.
|
|
|
|
|
|
|
|
* Media size limits - these are handled by the ``/media/%CLIENT_MAJOR_VERSION%/config``
|
|
|
|
API.
|
|
|
|
|
|
|
|
* Optional encodings or alternative transports for communicating with the
|
|
|
|
server.
|
|
|
|
|
|
|
|
Capabilities prefixed with ``m.`` are reserved for definition in the Matrix
|
|
|
|
specification while other values may be used by servers using the Java package
|
|
|
|
naming convention. The capabilities supported by the Matrix specification are
|
|
|
|
defined later in this section.
|
|
|
|
|
|
|
|
{{capabilities_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
``m.change_password`` capability
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
This capability has a single flag, ``enabled``, which indicates whether or not
|
|
|
|
the user can use the ``/account/password`` API to change their password. If not
|
|
|
|
present, the client should assume that password changes are possible via the
|
|
|
|
API. When present, clients SHOULD respect the capability's ``enabled`` flag
|
|
|
|
and indicate to the user if they are unable to change their password.
|
|
|
|
|
|
|
|
An example of the capability API's response for this capability is::
|
|
|
|
|
|
|
|
{
|
|
|
|
"capabilities": {
|
|
|
|
"m.change_password": {
|
|
|
|
"enabled": false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
``m.room_versions`` capability
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
This capability describes the default and available room versions a server
|
|
|
|
supports, and at what level of stability. Clients should make use of this
|
|
|
|
capability to determine if users need to be encouraged to upgrade their rooms.
|
|
|
|
|
|
|
|
An example of the capability API's response for this capability is::
|
|
|
|
|
|
|
|
{
|
|
|
|
"capabilities": {
|
|
|
|
"m.room_versions": {
|
|
|
|
"default": "1",
|
|
|
|
"available": {
|
|
|
|
"1": "stable",
|
|
|
|
"2": "stable",
|
|
|
|
"3": "unstable",
|
|
|
|
"custom-version": "unstable"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
This capability mirrors the same restrictions of `room versions`_ to describe
|
|
|
|
which versions are stable and unstable. Clients should assume that the ``default``
|
|
|
|
version is ``stable``. Any version not explicitly labelled as ``stable`` in the
|
|
|
|
``available`` versions is to be treated as ``unstable``. For example, a version
|
|
|
|
listed as ``future-stable`` should be treated as ``unstable``.
|
|
|
|
|
|
|
|
The ``default`` version is the version the server is using to create new rooms.
|
|
|
|
Clients should encourage users with sufficient permissions in a room to upgrade
|
|
|
|
their room to the ``default`` version when the room is using an ``unstable``
|
|
|
|
version.
|
|
|
|
|
|
|
|
When this capability is not listed, clients should use ``"1"`` as the default
|
|
|
|
and only stable ``available`` room version.
|
|
|
|
|
|
|
|
.. _`room versions`: ../index.html#room-versions
|
|
|
|
|
|
|
|
|
|
|
|
Pagination
|
|
|
|
----------
|
|
|
|
|
|
|
|
.. NOTE::
|
|
|
|
The paths referred to in this section are not actual endpoints. They only
|
|
|
|
serve as examples to explain how pagination functions.
|
|
|
|
|
|
|
|
Pagination is the process of dividing a dataset into multiple discrete pages.
|
|
|
|
Matrix makes use of pagination to allow clients to view extremely large datasets.
|
|
|
|
These datasets are not limited to events in a room (for example clients may want
|
|
|
|
to paginate a list of rooms in addition to events within those rooms). Regardless
|
|
|
|
of what is being paginated, there is a common approach which is used to give
|
|
|
|
clients an easy way of selecting subsets of a potentially changing dataset. Each
|
|
|
|
endpoint that uses pagination may use different parameters. However the theme
|
|
|
|
among them is that they take a ``from`` and ``to`` token, and occasionally
|
|
|
|
a ``limit`` and ``dir``. Together, these parameters describe the position in a
|
|
|
|
data set, where ``from`` and ``to`` are known as "stream tokens" matching the
|
|
|
|
regular expression ``[a-zA-Z0-9.=_-]+``. If supported, the ``dir`` defines the
|
|
|
|
direction of events to return: either forwards (``f``) or backwards (``b``).
|
|
|
|
The response may contain tokens that can be used for retrieving results before
|
|
|
|
or after the returned set. These tokens may be called `start` or `prev_batch`
|
|
|
|
for retrieving the previous result set, or `end`, `next_batch` or `next_token`
|
|
|
|
for retrieving the next result set.
|
|
|
|
|
|
|
|
In the following examples, 'START' and 'END' are placeholders to signify the
|
|
|
|
start and end of the data sets respectively.
|
|
|
|
|
|
|
|
For example, if an endpoint had events E1 -> E15. The client wants the last 5
|
|
|
|
events and doesn't know any previous events::
|
|
|
|
|
|
|
|
S E
|
|
|
|
|-E1-E2-E3-E4-E5-E6-E7-E8-E9-E10-E11-E12-E13-E14-E15-|
|
|
|
|
| | |
|
|
|
|
| _____| <--backwards-- |
|
|
|
|
|__________________ | | ________|
|
|
|
|
| | | |
|
|
|
|
GET /somepath?to=START&limit=5&dir=b&from=END
|
|
|
|
Returns:
|
|
|
|
E15,E14,E13,E12,E11
|
|
|
|
|
|
|
|
|
|
|
|
Another example: a public room list has rooms R1 -> R17. The client is showing 5
|
|
|
|
rooms at a time on screen, and is on page 2. They want to now show page 3 (rooms
|
|
|
|
R11 -> 15)::
|
|
|
|
|
|
|
|
S E
|
|
|
|
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | stream token
|
|
|
|
|-R1-R2-R3-R4-R5-R6-R7-R8-R9-R10-R11-R12-R13-R14-R15-R16-R17| room
|
|
|
|
|____________| |________________|
|
|
|
|
| |
|
|
|
|
Currently |
|
|
|
|
viewing |
|
|
|
|
|
|
|
|
|
GET /roomslist?from=9&to=END&limit=5
|
|
|
|
Returns: R11,R12,R13,R14,R15
|
|
|
|
|
|
|
|
Note that tokens are treated in an *exclusive*, not inclusive, manner. The end
|
|
|
|
token from the initial request was '9' which corresponded to R10. When the 2nd
|
|
|
|
request was made, R10 did not appear again, even though from=9 was specified. If
|
|
|
|
you know the token, you already have the data.
|
|
|
|
|
|
|
|
Responses for pagination-capable endpoints SHOULD have a ``chunk`` array alongside
|
|
|
|
the applicable stream tokens to represent the result set.
|
|
|
|
|
|
|
|
In general, when the end of a result set is reached the applicable stream token
|
|
|
|
will be excluded from the response. For example, if a user was backwards-paginating
|
|
|
|
events in a room they'd eventually reach the first event in the room. In this scenario,
|
|
|
|
the ``prev_batch`` token would be excluded from the response. Some paginated
|
|
|
|
endpoints are open-ended in one direction, such as endpoints which expose an event
|
|
|
|
stream for an active room. In this case, it is not possible for the client to reach
|
|
|
|
the true "end" of the data set and therefore should always be presented with a token
|
|
|
|
to keep moving forwards.
|
|
|
|
|
|
|
|
.. _`filter`:
|
|
|
|
|
|
|
|
Filtering
|
|
|
|
---------
|
|
|
|
|
|
|
|
Filters can be created on the server and can be passed as as a parameter to APIs
|
|
|
|
which return events. These filters alter the data returned from those APIs.
|
|
|
|
Not all APIs accept filters.
|
|
|
|
|
|
|
|
Lazy-loading room members
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Membership events often take significant resources for clients to track. In an
|
|
|
|
effort to reduce the number of resources used, clients can enable "lazy-loading"
|
|
|
|
for room members. By doing this, servers will only ever send membership events
|
|
|
|
which are relevant to the client.
|
|
|
|
|
|
|
|
In terms of filters, this means enabling ``lazy_load_members`` on a ``RoomEventFilter``
|
|
|
|
(or a ``StateFilter`` in the case of ``/sync`` only). When enabled, lazy-loading
|
|
|
|
aware endpoints (see below) will only include membership events for the ``sender``
|
|
|
|
of events being included in the response. For example, if a client makes a ``/sync``
|
|
|
|
request with lazy-loading enabled, the server will only return membership events
|
|
|
|
for the ``sender`` of events in the timeline, not all members of a room.
|
|
|
|
|
|
|
|
Repeated calls to lazy-loading aware endpoints will result in redundant membership
|
|
|
|
events being excluded by default. Clients often track which membership events they
|
|
|
|
already have, therefore making the extra information not as useful to the client.
|
|
|
|
Clients can always request redundant members by setting ``include_redundant_members``
|
|
|
|
to true in the filter.
|
|
|
|
|
|
|
|
Servers should be cautious about which events they consider redundant. Membership
|
|
|
|
events can change over time, and should be included as relevant to maintain the
|
|
|
|
historical record. Likewise, clients should be cautious about treating an older event
|
|
|
|
as the current membership event for a user.
|
|
|
|
|
|
|
|
.. Note::
|
|
|
|
Repeated calls using the same filter to *any* lazy-loading aware endpoint may
|
|
|
|
result in redundant members being excluded from future calls. For example, a
|
|
|
|
request to ``/sync`` followed by a request to ``/messages`` may result in a
|
|
|
|
future call to ``/sync`` excluding members included by the ``/messages`` call.
|
|
|
|
|
|
|
|
The current endpoints which support lazy-loading room members are:
|
|
|
|
|
|
|
|
* |/sync|_
|
|
|
|
* |/rooms/<room_id>/messages|_
|
|
|
|
* |/rooms/{roomId}/context/{eventId}|_
|
|
|
|
|
|
|
|
API endpoints
|
|
|
|
~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
{{filter_cs_http_api}}
|
|
|
|
|
|
|
|
Events
|
|
|
|
------
|
|
|
|
|
|
|
|
.. _sect:events:
|
|
|
|
|
|
|
|
The model of conversation history exposed by the client-server API can be
|
|
|
|
considered as a list of events. The server 'linearises' the
|
|
|
|
eventually-consistent event graph of events into an 'event stream' at any given
|
|
|
|
point in time::
|
|
|
|
|
|
|
|
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]
|
|
|
|
|
|
|
|
.. WARNING::
|
|
|
|
|
|
|
|
The format of events can change depending on room version. Check the
|
|
|
|
`room version specification`_ for specific details on what to expect for
|
|
|
|
event formats. Examples contained within the client-server specification
|
|
|
|
are expected to be compatible with all specified room versions, however
|
|
|
|
some differences may still apply.
|
|
|
|
|
|
|
|
For this version of the specification, clients only need to worry about
|
|
|
|
the event ID format being different depending on room version. Clients
|
|
|
|
should not be parsing the event ID, and instead be treating it as an
|
|
|
|
opaque string. No changes should be required to support the currently
|
|
|
|
available room versions.
|
|
|
|
|
|
|
|
.. _`room version specification`: ../index.html#room-versions
|
|
|
|
|
|
|
|
Types of room events
|
|
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Room events are split into two categories:
|
|
|
|
|
|
|
|
:State Events:
|
|
|
|
These are events which update the metadata state of the room (e.g. room topic,
|
|
|
|
room membership etc). State is keyed by a tuple of event ``type`` and a
|
|
|
|
``state_key``. State in the room with the same key-tuple will be overwritten.
|
|
|
|
|
|
|
|
:Message events:
|
|
|
|
These are events which describe transient "once-off" activity in a room:
|
|
|
|
typically communication such as sending an instant message or setting up a
|
|
|
|
VoIP call.
|
|
|
|
|
|
|
|
This specification outlines several events, all with the event type prefix
|
|
|
|
``m.``. (See `Room Events`_ for the m. event specification.) However,
|
|
|
|
applications may wish to add their own type of event, and this can be achieved
|
|
|
|
using the REST API detailed in the following sections. If new events are added,
|
|
|
|
the event ``type`` key SHOULD follow the Java package naming convention,
|
|
|
|
e.g. ``com.example.myapp.event``. This ensures event types are suitably
|
|
|
|
namespaced for each application and reduces the risk of clashes.
|
|
|
|
|
|
|
|
|
|
|
|
Syncing
|
|
|
|
~~~~~~~
|
|
|
|
|
|
|
|
To read events, the intended flow of operation is for clients to first call the
|
|
|
|
|/sync|_ API without a ``since`` parameter. This returns the most recent
|
|
|
|
message events for each room, as well as the state of the room at the start of
|
|
|
|
the returned timeline. The response also includes a ``next_batch`` field, which
|
|
|
|
should be used as the value of the ``since`` parameter in the next call to
|
|
|
|
``/sync``. Finally, the response includes, for each room, a ``prev_batch``
|
|
|
|
field, which can be passed as a ``start`` parameter to the
|
|
|
|
|/rooms/<room_id>/messages|_ API to retrieve earlier messages.
|
|
|
|
|
|
|
|
You can visualise the range of events being returned as::
|
|
|
|
|
|
|
|
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]
|
|
|
|
^ ^
|
|
|
|
| |
|
|
|
|
prev_batch: '1-2-3' next_batch: 'a-b-c'
|
|
|
|
|
|
|
|
|
|
|
|
Clients then receive new events by "long-polling" the homeserver via the
|
|
|
|
``/sync`` API, passing the value of the ``next_batch`` field from the response
|
|
|
|
to the previous call as the ``since`` parameter. The client should also pass a
|
|
|
|
``timeout`` parameter. The server will then hold open the HTTP connection for a
|
|
|
|
short period of time waiting for new events, returning early if an event
|
|
|
|
occurs. Only the ``/sync`` API (and the deprecated ``/events`` API) support
|
|
|
|
long-polling in this way.
|
|
|
|
|
|
|
|
The response for such an incremental sync can be visualised as::
|
|
|
|
|
|
|
|
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]
|
|
|
|
^ ^
|
|
|
|
| |
|
|
|
|
| next_batch: 'x-y-z'
|
|
|
|
prev_batch: 'a-b-c'
|
|
|
|
|
|
|
|
|
|
|
|
Normally, all new events which are visible to the client will appear in the
|
|
|
|
response to the ``/sync`` API. However, if a large number of events arrive
|
|
|
|
between calls to ``/sync``, a "limited" timeline is returned, containing only
|
|
|
|
the most recent message events. A state "delta" is also returned, summarising
|
|
|
|
any state changes in the omitted part of the timeline. The client may therefore
|
|
|
|
end up with "gaps" in its knowledge of the message timeline. The client can
|
|
|
|
fill these gaps using the |/rooms/<room_id>/messages|_ API. This situation
|
|
|
|
looks like this::
|
|
|
|
|
|
|
|
| gap |
|
|
|
|
| <-> |
|
|
|
|
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]->[E7]->[E8]->[E9]->[E10]
|
|
|
|
^ ^
|
|
|
|
| |
|
|
|
|
prev_batch: 'd-e-f' next_batch: 'u-v-w'
|
|
|
|
|
|
|
|
|
|
|
|
.. Warning::
|
|
|
|
Events are ordered in this API according to the arrival time of the event on
|
|
|
|
the homeserver. This can conflict with other APIs which order events based on
|
|
|
|
their partial ordering in the event graph. This can result in duplicate events
|
|
|
|
being received (once per distinct API called). Clients SHOULD de-duplicate
|
|
|
|
events based on the event ID when this happens.
|
|
|
|
|
|
|
|
.. NOTE::
|
|
|
|
|
|
|
|
The ``/sync`` API returns a ``state`` list which is separate from the
|
|
|
|
``timeline``. This ``state`` list allows clients to keep their model of the
|
|
|
|
room state in sync with that on the server. In the case of an initial
|
|
|
|
(``since``-less) sync, the ``state`` list represents the complete state of
|
|
|
|
the room at the **start** of the returned timeline (so in the case of a
|
|
|
|
recently-created room whose state fits entirely in the ``timeline``, the
|
|
|
|
``state`` list will be empty).
|
|
|
|
|
|
|
|
In the case of an incremental sync, the ``state`` list gives a delta
|
|
|
|
between the state of the room at the ``since`` parameter and that at the
|
|
|
|
start of the returned ``timeline``. (It will therefore be empty
|
|
|
|
unless the timeline was ``limited``.)
|
|
|
|
|
|
|
|
In both cases, it should be noted that the events returned in the ``state``
|
|
|
|
list did **not** necessarily take place just before the returned
|
|
|
|
``timeline``, so clients should not display them to the user in the timeline.
|
|
|
|
|
|
|
|
.. admonition:: Rationale
|
|
|
|
|
|
|
|
An early design of this specification made the ``state`` list represent the
|
|
|
|
room state at the end of the returned timeline, instead of the start. This
|
|
|
|
was unsatisfactory because it led to duplication of events between the
|
|
|
|
``state`` list and the ``timeline``, but more importantly, it made it
|
|
|
|
difficult for clients to show the timeline correctly.
|
|
|
|
|
|
|
|
In particular, consider a returned timeline [M0, S1, M2], where M0 and M2 are
|
|
|
|
both messages sent by the same user, and S1 is a state event where that user
|
|
|
|
changes their displayname. If the ``state`` list represents the room state at
|
|
|
|
the end of the timeline, the client must take a copy of the state dictionary,
|
|
|
|
and *rewind* S1, in order to correctly calculate the display name for M0.
|
|
|
|
|
|
|
|
.. TODO-spec
|
|
|
|
Do we ever support streaming requests? Why not websockets?
|
|
|
|
|
|
|
|
{{sync_cs_http_api}}
|
|
|
|
|
|
|
|
{{old_sync_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
Getting events for a room
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
There are several APIs provided to ``GET`` events for a room:
|
|
|
|
|
|
|
|
{{rooms_cs_http_api}}
|
|
|
|
|
|
|
|
{{message_pagination_cs_http_api}}
|
|
|
|
|
|
|
|
{{room_initial_sync_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
Sending events to a room
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
{{room_state_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
**Examples**
|
|
|
|
|
|
|
|
Valid requests look like::
|
|
|
|
|
|
|
|
PUT /rooms/!roomid:domain/state/m.example.event
|
|
|
|
{ "key" : "without a state key" }
|
|
|
|
|
|
|
|
PUT /rooms/!roomid:domain/state/m.another.example.event/foo
|
|
|
|
{ "key" : "with 'foo' as the state key" }
|
|
|
|
|
|
|
|
In contrast, these requests are invalid::
|
|
|
|
|
|
|
|
POST /rooms/!roomid:domain/state/m.example.event/
|
|
|
|
{ "key" : "cannot use POST here" }
|
|
|
|
|
|
|
|
PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
|
|
|
|
{ "key" : "txnIds are not supported" }
|
|
|
|
|
|
|
|
Care should be taken to avoid setting the wrong ``state key``::
|
|
|
|
|
|
|
|
PUT /rooms/!roomid:domain/state/m.another.example.event/11
|
|
|
|
{ "key" : "with '11' as the state key, but was probably intended to be a txnId" }
|
|
|
|
|
|
|
|
The ``state_key`` is often used to store state about individual users, by using
|
|
|
|
the user ID as the ``state_key`` value. For example::
|
|
|
|
|
|
|
|
PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Aexample.org
|
|
|
|
{ "animal" : "cat", "reason": "fluffy" }
|
|
|
|
|
|
|
|
In some cases, there may be no need for a ``state_key``, so it can be omitted::
|
|
|
|
|
|
|
|
PUT /rooms/!roomid:domain/state/m.room.bgd.color
|
|
|
|
{ "color": "red", "hex": "#ff0000" }
|
|
|
|
|
|
|
|
{{room_send_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
Redactions
|
|
|
|
~~~~~~~~~~
|
|
|
|
Since events are extensible it is possible for malicious users and/or servers
|
|
|
|
to add keys that are, for example offensive or illegal. Since some events
|
|
|
|
cannot be simply deleted, e.g. membership events, we instead 'redact' events.
|
|
|
|
This involves removing all keys from an event that are not required by the
|
|
|
|
protocol. This stripped down event is thereafter returned anytime a client or
|
|
|
|
remote server requests it. Redacting an event cannot be undone, allowing server
|
|
|
|
owners to delete the offending content from the databases. Events that have been
|
|
|
|
redacted include a ``redacted_because`` key whose value is the event that caused
|
|
|
|
it to be redacted, which may include a reason.
|
|
|
|
|
|
|
|
|
|
|
|
Upon receipt of a redaction event, the server should strip off any keys not in
|
|
|
|
the following list:
|
|
|
|
|
|
|
|
- ``event_id``
|
|
|
|
- ``type``
|
|
|
|
- ``room_id``
|
|
|
|
- ``sender``
|
|
|
|
- ``state_key``
|
|
|
|
- ``content``
|
|
|
|
- ``hashes``
|
|
|
|
- ``signatures``
|
|
|
|
- ``depth``
|
|
|
|
- ``prev_events``
|
|
|
|
- ``prev_state``
|
|
|
|
- ``auth_events``
|
|
|
|
- ``origin``
|
|
|
|
- ``origin_server_ts``
|
|
|
|
- ``membership``
|
|
|
|
|
|
|
|
.. Note:
|
|
|
|
Some of the keys, such as ``hashes``, will appear on the federation-formatted
|
|
|
|
event and therefore the client may not be aware of them.
|
|
|
|
|
|
|
|
The content object should also be stripped of all keys, unless it is one of
|
|
|
|
one of the following event types:
|
|
|
|
|
|
|
|
- ``m.room.member`` allows key ``membership``.
|
|
|
|
- ``m.room.create`` allows key ``creator``.
|
|
|
|
- ``m.room.join_rules`` allows key ``join_rule``.
|
|
|
|
- ``m.room.power_levels`` allows keys ``ban``, ``events``, ``events_default``,
|
|
|
|
``kick``, ``redact``, ``state_default``, ``users``, ``users_default``.
|
|
|
|
- ``m.room.aliases`` allows key ``aliases``.
|
|
|
|
- ``m.room.history_visibility`` allows key ``history_visibility``.
|
|
|
|
|
|
|
|
The server should add the event causing the redaction to the ``unsigned``
|
|
|
|
property of the redacted event, under the ``redacted_because`` key. When a
|
|
|
|
client receives a redaction event it should change the redacted event in the
|
|
|
|
same way a server does.
|
|
|
|
|
|
|
|
Events
|
|
|
|
++++++
|
|
|
|
|
|
|
|
{{m_room_redaction_event}}
|
|
|
|
|
|
|
|
Client behaviour
|
|
|
|
++++++++++++++++
|
|
|
|
|
|
|
|
{{redaction_cs_http_api}}
|
|
|
|
|
|
|
|
Rooms
|
|
|
|
-----
|
|
|
|
|
|
|
|
Creation
|
|
|
|
~~~~~~~~
|
|
|
|
The homeserver will create an ``m.room.create`` event when a room is created,
|
|
|
|
which serves as the root of the event graph for this room. This event also has a
|
|
|
|
``creator`` key which contains the user ID of the room creator. It will also
|
|
|
|
generate several other events in order to manage permissions in this room. This
|
|
|
|
includes:
|
|
|
|
|
|
|
|
- ``m.room.power_levels`` : Sets the power levels of users and required power
|
|
|
|
levels for various actions within the room such as sending events.
|
|
|
|
- ``m.room.join_rules`` : Whether the room is "invite-only" or not.
|
|
|
|
|
|
|
|
See `Room Events`_ for more information on these events. To create a room, a
|
|
|
|
client has to use the following API.
|
|
|
|
|
|
|
|
{{create_room_cs_http_api}}
|
|
|
|
|
|
|
|
Room aliases
|
|
|
|
~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Servers may host aliases for rooms with human-friendly names. Aliases take the
|
|
|
|
form ``#friendlyname:server.name``.
|
|
|
|
|
|
|
|
As room aliases are scoped to a particular homeserver domain name, it is
|
|
|
|
likely that a homeserver will reject attempts to maintain aliases on other
|
|
|
|
domain names. This specification does not provide a way for homeservers to
|
|
|
|
send update requests to other servers. However, homeservers MUST handle
|
|
|
|
``GET`` requests to resolve aliases on other servers; they should do this using
|
|
|
|
the federation API if necessary.
|
|
|
|
|
|
|
|
Rooms store a *partial* list of room aliases via the ``m.room.aliases`` state
|
|
|
|
event. This alias list is partial because it cannot guarantee that the alias
|
|
|
|
list is in any way accurate or up-to-date, as room aliases can point to
|
|
|
|
different room IDs over time. Crucially, the aliases in this event are
|
|
|
|
**purely informational** and SHOULD NOT be treated as accurate. They SHOULD
|
|
|
|
be checked before they are used or shared with another user. If a room
|
|
|
|
appears to have a room alias of ``#alias:example.com``, this SHOULD be checked
|
|
|
|
to make sure that the room's ID matches the ``room_id`` returned from the
|
|
|
|
request.
|
|
|
|
|
|
|
|
{{directory_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
Permissions
|
|
|
|
~~~~~~~~~~~
|
|
|
|
.. NOTE::
|
|
|
|
This section is a work in progress.
|
|
|
|
|
|
|
|
Permissions for rooms are done via the concept of power levels - to do any
|
|
|
|
action in a room a user must have a suitable power level. Power levels are
|
|
|
|
stored as state events in a given room. The power levels required for operations
|
|
|
|
and the power levels for users are defined in ``m.room.power_levels``, where
|
|
|
|
both a default and specific users' power levels can be set.
|
|
|
|
By default all users have a power level of 0, other than the room creator whose
|
|
|
|
power level defaults to 100. Users can grant other users increased power levels
|
|
|
|
up to their own power level. For example, user A with a power level of 50 could
|
|
|
|
increase the power level of user B to a maximum of level 50. Power levels for
|
|
|
|
users are tracked per-room even if the user is not present in the room.
|
|
|
|
The keys contained in ``m.room.power_levels`` determine the levels required for
|
|
|
|
certain operations such as kicking, banning and sending state events. See
|
|
|
|
`m.room.power_levels`_ for more information.
|
|
|
|
|
|
|
|
Clients may wish to assign names to particular power levels. A suggested mapping is as follows:
|
|
|
|
- 0 User
|
|
|
|
- 50 Moderator
|
|
|
|
- 100 Admin
|
|
|
|
|
|
|
|
|
|
|
|
Room membership
|
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Users need to be a member of a room in order to send and receive events in that
|
|
|
|
room. There are several states in which a user may be, in relation to a room:
|
|
|
|
|
|
|
|
- Unrelated (the user cannot send or receive events in the room)
|
|
|
|
- Invited (the user has been invited to participate in the room, but is not
|
|
|
|
yet participating)
|
|
|
|
- Joined (the user can send and receive events in the room)
|
|
|
|
- Banned (the user is not allowed to join the room)
|
|
|
|
|
|
|
|
There is an exception to the requirement that a user join a room before sending
|
|
|
|
events to it: users may send an ``m.room.member`` event to a room with
|
|
|
|
``content.membership`` set to ``leave`` to reject an invitation if they have
|
|
|
|
currently been invited to a room but have not joined it.
|
|
|
|
|
|
|
|
Some rooms require that users be invited to it before they can join; others
|
|
|
|
allow anyone to join. Whether a given room is an "invite-only" room is
|
|
|
|
determined by the room config key ``m.room.join_rules``. It can have one of the
|
|
|
|
following values:
|
|
|
|
|
|
|
|
``public``
|
|
|
|
This room is free for anyone to join without an invite.
|
|
|
|
|
|
|
|
``invite``
|
|
|
|
This room can only be joined if you were invited.
|
|
|
|
|
|
|
|
The allowable state transitions of membership are::
|
|
|
|
|
|
|
|
/ban
|
|
|
|
+------------------------------------------------------+
|
|
|
|
| |
|
|
|
|
| +----------------+ +----------------+ |
|
|
|
|
| | /leave | | | |
|
|
|
|
| | v v | |
|
|
|
|
/invite +--------+ +-------+ | |
|
|
|
|
------------>| invite |<----------| leave |----+ | |
|
|
|
|
+--------+ /invite +-------+ | | |
|
|
|
|
| | ^ | | |
|
|
|
|
| | | | | |
|
|
|
|
/join | +---------------+ | | | |
|
|
|
|
| | /join if | | | |
|
|
|
|
| | join_rules | | /ban | /unban |
|
|
|
|
| | public /leave | | | |
|
|
|
|
v v or | | | |
|
|
|
|
+------+ /kick | | | |
|
|
|
|
------------>| join |-------------------+ | | |
|
|
|
|
/join +------+ v | |
|
|
|
|
if | +-----+ | |
|
|
|
|
join_rules +-------------------------->| ban |-----+ |
|
|
|
|
public /ban +-----+ |
|
|
|
|
^ ^ |
|
|
|
|
| | |
|
|
|
|
----------------------------------------------+ +----------------------+
|
|
|
|
/ban
|
|
|
|
|
|
|
|
{{list_joined_rooms_cs_http_api}}
|
|
|
|
|
|
|
|
Joining rooms
|
|
|
|
+++++++++++++
|
|
|
|
|
|
|
|
{{inviting_cs_http_api}}
|
|
|
|
|
|
|
|
{{joining_cs_http_api}}
|
|
|
|
|
|
|
|
Leaving rooms
|
|
|
|
+++++++++++++
|
|
|
|
A user can leave a room to stop receiving events for that room. A user must
|
|
|
|
have been invited to or have joined the room before they are eligible to leave
|
|
|
|
the room. Leaving a room to which the user has been invited rejects the invite.
|
|
|
|
Once a user leaves a room, it will no longer appear in the response to the
|
|
|
|
|/sync|_ API unless it is explicitly requested via a filter with the
|
|
|
|
``include_leave`` field set to ``true``.
|
|
|
|
|
|
|
|
Whether or not they actually joined the room, if the room is an "invite-only"
|
|
|
|
room the user will need to be re-invited before they can re-join the room.
|
|
|
|
|
|
|
|
A user can also forget a room which they have left. Rooms which have been
|
|
|
|
forgotten will never appear the response to the |/sync|_ API, until the user
|
|
|
|
re-joins or is re-invited.
|
|
|
|
|
|
|
|
A user may wish to force another user to leave a room. This can be done by
|
|
|
|
'kicking' the other user. To do so, the user performing the kick MUST have the
|
|
|
|
required power level. Once a user has been kicked, the behaviour is the same as
|
|
|
|
if they had left of their own accord. In particular, the user is free to
|
|
|
|
re-join if the room is not "invite-only".
|
|
|
|
|
|
|
|
{{leaving_cs_http_api}}
|
|
|
|
|
|
|
|
{{kicking_cs_http_api}}
|
|
|
|
|
|
|
|
Banning users in a room
|
|
|
|
+++++++++++++++++++++++
|
|
|
|
A user may decide to ban another user in a room. 'Banning' forces the target
|
|
|
|
user to leave the room and prevents them from re-joining the room. A banned
|
|
|
|
user will not be treated as a joined user, and so will not be able to send or
|
|
|
|
receive events in the room. In order to ban someone, the user performing the
|
|
|
|
ban MUST have the required power level. To ban a user, a request should be made
|
|
|
|
to |/rooms/<room_id>/ban|_ with::
|
|
|
|
|
|
|
|
{
|
|
|
|
"user_id": "<user id to ban>"
|
|
|
|
"reason": "string: <reason for the ban>"
|
|
|
|
}
|
|
|
|
|
|
|
|
Banning a user adjusts the banned member's membership state to ``ban``.
|
|
|
|
Like with other membership changes, a user can directly adjust the target
|
|
|
|
member's state, by making a request to
|
|
|
|
``/rooms/<room id>/state/m.room.member/<user id>``::
|
|
|
|
|
|
|
|
{
|
|
|
|
"membership": "ban"
|
|
|
|
}
|
|
|
|
|
|
|
|
A user must be explicitly unbanned with a request to |/rooms/<room_id>/unban|_
|
|
|
|
before they can re-join the room or be re-invited.
|
|
|
|
|
|
|
|
{{banning_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Listing rooms
|
|
|
|
~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
{{list_public_rooms_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
User Data
|
|
|
|
---------
|
|
|
|
|
|
|
|
User Directory
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
{{users_cs_http_api}}
|
|
|
|
|
|
|
|
|
|
|
|
Profiles
|
|
|
|
~~~~~~~~
|
|
|
|
|
|
|
|
{{profile_cs_http_api}}
|
|
|
|
|
|
|
|
Events on Change of Profile Information
|
|
|
|
+++++++++++++++++++++++++++++++++++++++
|
|
|
|
Because the profile display name and avatar information are likely to be used in
|
|
|
|
many places of a client's display, changes to these fields cause an automatic
|
|
|
|
propagation event to occur, informing likely-interested parties of the new
|
|
|
|
values. This change is conveyed using two separate mechanisms:
|
|
|
|
|
|
|
|
- a ``m.room.member`` event (with a ``join`` membership) is sent to every room
|
|
|
|
the user is a member of, to update the ``displayname`` and ``avatar_url``.
|
|
|
|
- a ``m.presence`` presence status update is sent, again containing the new
|
|
|
|
values of the ``displayname`` and ``avatar_url`` keys, in addition to the
|
|
|
|
required ``presence`` key containing the current presence state of the user.
|
|
|
|
|
|
|
|
Both of these should be done automatically by the homeserver when a user
|
|
|
|
successfully changes their display name or avatar URL fields.
|
|
|
|
|
|
|
|
Additionally, when homeservers emit room membership events for their own
|
|
|
|
users, they should include the display name and avatar URL fields in these
|
|
|
|
events so that clients already have these details to hand, and do not have to
|
|
|
|
perform extra round trips to query it.
|
|
|
|
|
|
|
|
Security
|
|
|
|
--------
|
|
|
|
|
|
|
|
Rate limiting
|
|
|
|
~~~~~~~~~~~~~
|
|
|
|
Homeservers SHOULD implement rate limiting to reduce the risk of being
|
|
|
|
overloaded. If a request is refused due to rate limiting, it should return a
|
|
|
|
standard error response of the form::
|
|
|
|
|
|
|
|
{
|
|
|
|
"errcode": "M_LIMIT_EXCEEDED",
|
|
|
|
"error": "string",
|
|
|
|
"retry_after_ms": integer (optional)
|
|
|
|
}
|
|
|
|
|
|
|
|
The ``retry_after_ms`` key SHOULD be included to tell the client how long they
|
|
|
|
have to wait in milliseconds before they can try again.
|
|
|
|
|
|
|
|
.. TODO-spec
|
|
|
|
- Surely we should recommend an algorithm for the rate limiting, rather than letting every
|
|
|
|
homeserver come up with their own idea, causing totally unpredictable performance over
|
|
|
|
federated rooms?
|
|
|
|
|
|
|
|
.. References
|
|
|
|
|
|
|
|
.. _`macaroon`: http://research.google.com/pubs/pub41892.html
|
|
|
|
.. _`devices`: ../index.html#devices
|
|
|
|
|
|
|
|
.. Links through the external API docs are below
|
|
|
|
.. =============================================
|
|
|
|
|
|
|
|
.. |/initialSync| replace:: ``/initialSync``
|
|
|
|
.. _/initialSync: #get-matrix-client-%CLIENT_MAJOR_VERSION%-initialsync
|
|
|
|
|
|
|
|
.. |/sync| replace:: ``/sync``
|
|
|
|
.. _/sync: #get-matrix-client-%CLIENT_MAJOR_VERSION%-sync
|
|
|
|
|
|
|
|
.. |/events| replace:: ``/events``
|
|
|
|
.. _/events: #get-matrix-client-%CLIENT_MAJOR_VERSION%-events
|
|
|
|
|
|
|
|
.. |/createRoom| replace:: ``/createRoom``
|
|
|
|
.. _/createRoom: #post-matrix-client-%CLIENT_MAJOR_VERSION%-createroom
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/initialSync| replace:: ``/rooms/<room_id>/initialSync``
|
|
|
|
.. _/rooms/<room_id>/initialSync: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/messages| replace:: ``/rooms/<room_id>/messages``
|
|
|
|
.. _/rooms/<room_id>/messages: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/members| replace:: ``/rooms/<room_id>/members``
|
|
|
|
.. _/rooms/<room_id>/members: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-members
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/state| replace:: ``/rooms/<room_id>/state``
|
|
|
|
.. _/rooms/<room_id>/state: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/send| replace:: ``/rooms/<room_id>/send``
|
|
|
|
.. _/rooms/<room_id>/send: #put-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-send-eventtype-txnid
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/invite| replace:: ``/rooms/<room_id>/invite``
|
|
|
|
.. _/rooms/<room_id>/invite: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-invite
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/join| replace:: ``/rooms/<room_id>/join``
|
|
|
|
.. _/rooms/<room_id>/join: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-join
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/leave| replace:: ``/rooms/<room_id>/leave``
|
|
|
|
.. _/rooms/<room_id>/leave: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-leave
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/ban| replace:: ``/rooms/<room_id>/ban``
|
|
|
|
.. _/rooms/<room_id>/ban: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-ban
|
|
|
|
|
|
|
|
.. |/rooms/<room_id>/unban| replace:: ``/rooms/<room_id>/unban``
|
|
|
|
.. _/rooms/<room_id>/unban: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-unban
|
|
|
|
|
|
|
|
.. |/rooms/{roomId}/context/{eventId}| replace:: ``/rooms/{roomId}/context/{eventId}``
|
|
|
|
.. _/rooms/{roomId}/context/{eventId}: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-context-eventid
|
|
|
|
|
|
|
|
.. |/rooms/{roomId}/event/{eventId}| replace:: ``/rooms/{roomId}/event/{eventId}``
|
|
|
|
.. _/rooms/{roomId}/event/{eventId}: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-event-eventid
|
|
|
|
|
|
|
|
.. |/account/3pid| replace:: ``/account/3pid``
|
|
|
|
.. _/account/3pid: #post-matrix-client-%CLIENT_MAJOR_VERSION%-account-3pid
|
|
|
|
|
|
|
|
.. |/user/<user_id>/account_data/<type>| replace:: ``/user/<user_id>/account_data/<type>``
|
|
|
|
.. _/user/<user_id>/account_data/<type>: #put-matrix-client-%CLIENT_MAJOR_VERSION%-user-userid-account-data-type
|
|
|
|
|
|
|
|
.. |/_matrix/client/versions| replace:: ``/_matrix/client/versions``
|
|
|
|
.. _/_matrix/client/versions: #get-matrix-client-versions
|
|
|
|
|
|
|
|
.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
|
|
|
|
.. _`3PID Types`: ../appendices.html#pid-types
|