Merge remote-tracking branch 'origin/master' into kitsune/cleanup

pull/977/head
Kitsune Ral 5 years ago
commit 8e7c0dfb93

@ -13,7 +13,7 @@ https://github.com/matrix-org/matrix-doc/blob/master/meta/documentation_style.rs
Python code within the ``matrix-doc`` project should follow the same style as Python code within the ``matrix-doc`` project should follow the same style as
synapse, which is documented at synapse, which is documented at
https://github.com/matrix-org/synapse/tree/master/docs/code_style.rst. https://github.com/matrix-org/synapse/tree/master/docs/code_style.md.
Matrix-doc workflows Matrix-doc workflows
-------------------- --------------------

@ -82,7 +82,7 @@ paths:
devices: devices:
type: object type: object
description: |- description: |-
Each key is an identitfier for one of the user's devices. Each key is an identifier for one of the user's devices.
additionalProperties: additionalProperties:
type: object type: object
title: DeviceInfo title: DeviceInfo

@ -96,7 +96,6 @@ paths:
example: "1" example: "1"
available: available:
type: object type: object
title: AvailableRoomVersions
description: |- description: |-
A detailed description of the room versions the server supports. A detailed description of the room versions the server supports.
additionalProperties: additionalProperties:

@ -38,20 +38,26 @@ paths:
the new room, including checking power levels for each event. It MUST the new room, including checking power levels for each event. It MUST
apply the events implied by the request in the following order: apply the events implied by the request in the following order:
0. A default ``m.room.power_levels`` event, giving the room creator 1. The ``m.room.create`` event itself. Must be the first event in the
room.
2. An ``m.room.member`` event for the creator to join the room. This is
needed so the remaining events can be sent.
3. A default ``m.room.power_levels`` event, giving the room creator
(and not other members) permission to send state events. Overridden (and not other members) permission to send state events. Overridden
by the ``power_level_content_override`` parameter. by the ``power_level_content_override`` parameter.
1. Events set by the ``preset``. Currently these are the ``m.room.join_rules``, 4. Events set by the ``preset``. Currently these are the ``m.room.join_rules``,
``m.room.history_visibility``, and ``m.room.guest_access`` state events. ``m.room.history_visibility``, and ``m.room.guest_access`` state events.
2. Events listed in ``initial_state``, in the order that they are 5. Events listed in ``initial_state``, in the order that they are
listed. listed.
3. Events implied by ``name`` and ``topic`` (``m.room.name`` and ``m.room.topic`` 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and ``m.room.topic``
state events). state events).
4. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` with 7. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` with
``membership: invite`` and ``m.room.third_party_invite``). ``membership: invite`` and ``m.room.third_party_invite``).
The available presets do the following with respect to room state: The available presets do the following with respect to room state:

@ -47,6 +47,7 @@ properties:
"ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI" "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
signatures: signatures:
type: object type: object
title: Signatures
description: |- description: |-
Signatures for the device key object. A map from user ID, to a map from Signatures for the device key object. A map from user ID, to a map from
``<algorithm>:<device_id>`` to the signature. ``<algorithm>:<device_id>`` to the signature.

@ -0,0 +1,50 @@
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# 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.
type: object
title: KeyBackupData
description: "The key data"
properties:
first_message_index:
description: |-
The index of the first message in the session that the key can decrypt.
type: integer
example: 1
forwarded_count:
description: |-
The number of times this key has been forwarded via key-sharing between devices.
type: integer
example: 0
is_verified:
description: |-
Whether the device backing up the key verified the device that the key
is from.
type: boolean
example: false
session_data:
description: |-
Algorithm-dependent data. See the documentation for the backup
algorithms in `Server-side key backups`_ for more information on the
expected format of the data.
type: object
example: {
"ephemeral": "base64+ephemeral+key",
"ciphertext": "base64+ciphertext+of+JSON+data",
"mac": "base64+mac+of+ciphertext"
}
required:
- first_message_index
- forwarded_count
- is_verified
- session_data

@ -33,6 +33,10 @@ properties:
type: object type: object
description: A signatures object containing a signature of the entire signed object. description: A signatures object containing a signature of the entire signed object.
title: Signatures title: Signatures
additionalProperties:
type: object
additionalProperties:
type: string
example: { example: {
"example.org": { "example.org": {
"ed25519:0": "some9signature" "ed25519:0": "some9signature"

@ -19,7 +19,7 @@ host: localhost:8008
schemes: schemes:
- https - https
- http - http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%/directory basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
consumes: consumes:
- application/json - application/json
produces: produces:
@ -27,7 +27,7 @@ produces:
securityDefinitions: securityDefinitions:
$ref: definitions/security.yaml $ref: definitions/security.yaml
paths: paths:
"/room/{roomAlias}": "/directory/room/{roomAlias}":
put: put:
summary: Create a new mapping from room alias to room ID. summary: Create a new mapping from room alias to room ID.
operationId: setRoomAlias operationId: setRoomAlias
@ -129,7 +129,16 @@ paths:
description: |- description: |-
Remove a mapping of room alias to room ID. Remove a mapping of room alias to room ID.
Servers may choose to implement additional access control checks here, for instance that room aliases can only be deleted by their creator or a server administrator. Servers may choose to implement additional access control checks here, for instance that
room aliases can only be deleted by their creator or a server administrator.
.. Note::
Servers may choose to update the ``alt_aliases`` for the ``m.room.canonical_alias``
state event in the room when an alias is removed. Servers which choose to update the
canonical alias event are recommended to, in addition to their other relevant permission
checks, delete the alias and return a successful response even if the user does not
have permission to update the ``m.room.canonical_alias`` event.
operationId: deleteRoomAlias operationId: deleteRoomAlias
security: security:
- accessToken: [] - accessToken: []
@ -159,3 +168,70 @@ paths:
"$ref": "definitions/errors/error.yaml" "$ref": "definitions/errors/error.yaml"
tags: tags:
- Room directory - Room directory
"/rooms/{roomId}/aliases":
get:
summary: Get a list of local aliases on a given room.
description: |-
Get a list of aliases maintained by the local server for the
given room.
This endpoint can be called by users who are in the room (external
users receive an ``M_FORBIDDEN`` error response). If the room's
``m.room.history_visibility`` maps to ``world_readable``, any
user can call this endpoint.
Servers may choose to implement additional access control checks here,
such as allowing server administrators to view aliases regardless of
membership.
.. Note::
Clients are recommended not to display this list of aliases prominently
as they are not curated, unlike those listed in the ``m.room.canonical_alias``
state event.
operationId: getLocalAliases
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room ID to find local aliases of.
required: true
x-example: "!abc123:example.org"
responses:
200:
description: |-
The list of local aliases for the room.
examples:
application/json: {
"aliases": [
"#somewhere:example.com",
"#another:example.com",
"#hat_trick:example.com"
]
}
schema:
type: object
properties:
aliases:
type: array
description: The server's local aliases on the room. Can be empty.
items:
type: string
required: ['aliases']
403:
description: The user is not permitted to retrieve the list of local aliases for the room.
examples:
application/json: {
"errcode": "M_FORBIDDEN",
"error": "You are not a member of the room."
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room directory

@ -0,0 +1,940 @@
# Copyright 2019-2020 The Matrix.org Foundation C.I.C.
#
# 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.
swagger: '2.0'
info:
title: "Matrix Client-Server Key Backup API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/room_keys/version":
post:
summary: Create a new backup.
description: |-
Creates a new backup.
operationId: postRoomKeysVersion
security:
- accessToken: []
parameters:
- in: body
name: version
description: "The backup configuration."
schema:
type: object
properties:
algorithm:
description: The algorithm used for storing backups.
type: string
enum: ["m.megolm_backup.v1.curve25519-aes-sha2"]
example: "m.megolm_backup.v1.curve25519-aes-sha2"
auth_data:
description: |-
Algorithm-dependent data. See the documentation for the backup
algorithms in `Server-side key backups`_ for more information on the
expected format of the data.
type: object
example: {
"public_key": "abcdefg",
"signatures": {
"@alice:example.org": {
"ed25519:deviceid": "signature"
}
}
}
required:
- algorithm
- auth_data
responses:
200:
description:
The version id of the new backup.
schema:
type: object
properties:
version:
type: string
description: The backup version. This is an opaque string.
example: "1"
required:
- version
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
get:
summary: Get information about the latest backup version.
description: |-
Get information about the latest backup version.
operationId: getRoomKeysVersionCurrent
security:
- accessToken: []
responses:
200:
description:
The information about the backup.
schema:
type: object
properties:
algorithm:
type: string
description: The algorithm used for storing backups.
enum: ["m.megolm_backup.v1.curve25519-aes-sha2"]
example: "m.megolm_backup.v1.curve25519-aes-sha2"
auth_data:
description: |-
Algorithm-dependent data. See the documentation for the backup
algorithms in `Server-side key backups`_ for more information on the
expected format of the data.
type: object
example: {
"public_key": "abcdefg",
"signatures": {
"@alice:example.org": {
"ed25519:deviceid": "signature"
}
}
}
count:
description: The number of keys stored in the backup.
type: integer
example: 42
etag:
description: |-
An opaque string representing stored keys in the backup.
Clients can compare it with the ``etag`` value they received
in the request of their last key storage request. If not
equal, another client has modified the backup.
type: string
example: "anopaquestring"
version:
type: string
description: The backup version
example: "1"
required:
- algorithm
- auth_data
- count
- etag
- version
404:
description:
No backup exists.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "No current backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
"/room_keys/version/{version}":
get:
summary: Get information about an existing backup.
description: |-
Get information about an existing backup.
operationId: getRoomKeysVersion
security:
- accessToken: []
parameters:
- in: path
type: string
name: version
description: |-
The backup version to get, as returned in the ``version`` parameter
of the response in `POST /_matrix/client/r0/room_keys/version`_ or
this endpoint.
required: true
x-example: "1"
responses:
200:
description:
The information about the requested backup.
schema:
type: object
properties:
algorithm:
type: string
description: The algorithm used for storing backups.
enum: ["m.megolm_backup.v1.curve25519-aes-sha2"]
example: "m.megolm_backup.v1.curve25519-aes-sha2"
auth_data:
description: |-
Algorithm-dependent data. See the documentation for the backup
algorithms in `Server-side key backups`_ for more information on the
expected format of the data.
type: object
example: {
"public_key": "abcdefg",
"signatures": {
"@alice:example.org": {
"ed25519:deviceid": "signature"
}
}
}
count:
description: The number of keys stored in the backup.
type: integer
example: 42
etag:
description: |-
An opaque string representing stored keys in the backup.
Clients can compare it with the ``etag`` value they received
in the request of their last key storage request. If not
equal, another client has modified the backup.
type: string
example: "anopaquestring"
version:
type: string
description: The backup version
example: "1"
required:
- algorithm
- auth_data
- count
- etag
- version
404:
description:
The backup specified does not exist.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
put:
summary: Update information about an existing backup.
description: |-
Update information about an existing backup. Only ``auth_data`` can be modified.
operationId: putRoomKeysVersion
security:
- accessToken: []
parameters:
- in: path
type: string
name: version
description: |-
The backup version to update, as returned in the ``version``
parameter in the response of `POST
/_matrix/client/r0/room_keys/version`_ or `GET
/_matrix/client/r0/room_keys/version/{version}`_.
required: true
x-example: "1"
- in: body
name: version
description: "The backup configuration"
schema:
type: object
properties:
algorithm:
description: |-
The algorithm used for storing backups. Must be the same as
the algorithm currently used by the backup.
type: string
enum: ["m.megolm_backup.v1.curve25519-aes-sha2"]
example: "m.megolm_backup.v1.curve25519-aes-sha2"
auth_data:
description: |-
Algorithm-dependent data. See the documentation for the backup
algorithms in `Server-side key backups`_ for more information on the
expected format of the data.
type: object
example: {
"public_key": "abcdefg",
"signatures": {
"@alice:example.org": {
"ed25519:deviceid": "signature"
}
}
}
version:
description: |-
The backup version. If present, must be the same as the
version in the path parameter.
type: string
example: "1"
required:
- algorithm
- auth_data
responses:
200:
description: The update succeeded.
schema:
type: object
properties: {}
400:
description: |-
A parameter was incorrect. For example, the ``algorithm`` does not
match the current backup algorithm, or the ``version`` in the body
does not match the ``version`` in the path.
examples:
application/json: {
"errcode": "M_INVALID_PARAM",
"error": "Algorithm does not match"
}
schema:
"$ref": "definitions/errors/error.yaml"
404:
description: The backup specified does not exist.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
delete:
summary: Delete an existing key backup.
description: |-
Delete an existing key backup. Both the information about the backup,
as well as all key data related to the backup will be deleted.
operationId: deleteRoomKeysVersion
security:
- accessToken: []
parameters:
- in: path
type: string
name: version
description: |-
The backup version to delete, as returned in the ``version``
parameter in the response of `POST
/_matrix/client/r0/room_keys/version`_ or `GET
/_matrix/client/r0/room_keys/version/{version}`_.
required: true
x-example: "1"
responses:
200:
description: |-
The delete succeeded, or the specified backup was previously
deleted.
schema:
type: object
properties: {}
404:
description: |-
The backup specified does not exist. If the backup was previously
deleted, the call should succeed rather than returning an error.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
"/room_keys/keys/{roomId}/{sessionId}":
put:
summary: Store a key in the backup.
description: |-
Store a key in the backup.
operationId: postRoomKeysKeyRoomIdSessionId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup in which to store the key. Must be the current backup.
required: true
x-example: "1"
- in: path
type: string
name: roomId
description: The ID of the room that the key is for.
required: true
x-example: "!roomid:example.org"
- in: path
type: string
name: sessionId
description: The ID of the megolm session that the key is for.
required: true
x-example: "sessionid"
- in: body
name: data
description: "The key data."
schema:
"$ref": "definitions/key_backup_data.yaml"
responses:
200:
description: The update succeeded.
schema:
type: object
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See ``GET /room_keys/version/{version}`` for more details.
type: string
example: "abcdefg"
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
403:
description: |-
The version specified does not match the current backup version.
The current version will be included in the ``current_version``
field.
examples:
application/json: {
"errcode": "M_WRONG_ROOM_KEYS_VERSION",
"error": "Wrong backup version.",
"current_version": "42"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
get:
summary: Retrieve a key from the backup
description: |-
Retrieve a key from the backup.
operationId: getRoomKeysKeyRoomIdSessionId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup from which to retrieve the key
required: true
x-example: "1"
- in: path
type: string
name: roomId
description: The ID of the room that the requested key is for.
required: true
x-example: "!roomid:example.org"
- in: path
type: string
name: sessionId
description: The ID of the megolm session whose key is requested.
required: true
x-example: "sessionid"
responses:
200:
description: The key data
schema:
"$ref": "definitions/key_backup_data.yaml"
404:
description: The key or backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Key not found."
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
delete:
summary: Delete a key from the backup
description: |-
Delete a key from the backup.
operationId: deleteRoomKeysKeyRoomIdSessionId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup from which to delete the key
required: true
x-example: "1"
- in: path
type: string
name: roomId
description: The ID of the room that the specified key is for.
required: true
x-example: "!roomid:example.org"
- in: path
type: string
name: sessionId
description: The ID of the megolm session whose key is to be deleted.
required: true
x-example: "sessionid"
responses:
200:
description: The update succeeded
schema:
type: object
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See ``GET /room_keys/version/{version}`` for more details.
type: string
example: "abcdefg"
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
404:
description: |-
The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
"/room_keys/keys/{roomId}":
put:
summary: Store several keys in the backup for a given room.
description: |-
Store a key in the backup.
operationId: postRoomKeysKeyRoomId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup in which to store the keys. Must be the current backup.
required: true
x-example: "1"
- in: path
type: string
name: roomId
description: The ID of the room that the keys are for.
required: true
x-example: "!roomid:example.org"
- in: body
description: "The backup data"
name: backupData
schema:
type: object
properties:
sessions:
type: object
description: |-
A map of session IDs to key data.
additionalProperties:
allOf:
- $ref: "definitions/key_backup_data.yaml"
example: {
"sessionid1": {
"ephemeral": "base64+ephemeral+key",
"ciphertext": "base64+ciphertext+of+JSON+data",
"mac": "base64+mac+of+ciphertext"
}
}
responses:
200:
description: The update succeeded
schema:
type: object
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See ``GET /room_keys/version/{version}`` for more details.
type: string
example: "abcdefg"
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
403:
description: |-
The version specified does not match the current backup version.
The current version will be included in the ``current_version``
field.
examples:
application/json: {
"errcode": "M_WRONG_ROOM_KEYS_VERSION",
"error": "Wrong backup version.",
"current_version": "42"
}
schema:
"$ref": "definitions/errors/error.yaml"
404:
description: |-
The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
get:
summary: Retrieve the keys from the backup for a given room
description: |-
Retrieve the keys from the backup for a given room
operationId: getRoomKeysKeyRoomId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup from which to retrieve the key
required: true
x-example: "1"
- in: path
type: string
name: roomId
description: The ID of the room that the requested key is for.
required: true
x-example: "!roomid:example.org"
responses:
200:
description: |-
The key data. If no keys are found, then an object with an empty
``sessions`` property will be returned (``{"sessions": {}}``).
schema:
type: object
properties:
sessions:
type: object
description: |-
A map of session IDs to key data.
additionalProperties:
allOf:
- $ref: "definitions/key_backup_data.yaml"
example: {
"sessionid1": {
"ephemeral": "base64+ephemeral+key",
"ciphertext": "base64+ciphertext+of+JSON+data",
"mac": "base64+mac+of+ciphertext"
}
}
404:
description: |-
The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
delete:
summary: Delete a key from the backup
description: |-
Delete a key from the backup.
operationId: deleteRoomKeysKeyRoomId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup from which to delete the key
required: true
x-example: "1"
- in: path
type: string
name: roomId
description: The ID of the room that the specified key is for.
required: true
x-example: "!roomid:example.org"
responses:
200:
description: The update succeeded
schema:
type: object
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See ``GET /room_keys/version/{version}`` for more details.
type: string
example: "abcdefg"
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
404:
description: |-
The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
"/room_keys/keys":
put:
summary: Store several keys in the backup.
description: |-
Store several keys in the backup.
operationId: postRoomKeysKey
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup in which to store the keys. Must be the current backup.
required: true
x-example: "1"
- in: body
description: "The backup data"
name: backupData
schema:
type: object
properties:
rooms:
type: object
description: |-
A map of room IDs to session IDs to key data.
additionalProperties:
type: object
additionalProperties:
allOf:
- $ref: "definitions/key_backup_data.yaml"
example: {
"!room:example.org": {
"sessions": {
"sessionid1": {
"ephemeral": "base64+ephemeral+key",
"ciphertext": "base64+ciphertext+of+JSON+data",
"mac": "base64+mac+of+ciphertext"
}
}
}
}
responses:
200:
description: The update succeeded
schema:
type: object
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See ``GET /room_keys/version/{version}`` for more details.
type: string
example: "abcdefg"
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
403:
description: |-
The version specified does not match the current backup version.
The current version will be included in the ``current_version``
field.
examples:
application/json: {
"errcode": "M_WRONG_ROOM_KEYS_VERSION",
"error": "Wrong backup version.",
"current_version": "42"
}
schema:
"$ref": "definitions/errors/error.yaml"
404:
description: |-
The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
get:
summary: Retrieve the keys from the backup for a given room
description: |-
Retrieve the keys from the backup for a given room
operationId: getRoomKeysKeyRoomId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup from which to retrieve the keys. If omitted, the keys are
retrieved from the current backup.
x-example: "1"
responses:
200:
description: |-
The key data. If no keys are found, then an object with an empty
``rooms`` property will be returned (``{"rooms": {}}``).
schema:
type: object
properties:
rooms:
type: object
description: |-
A map of room IDs to session IDs to key data.
additionalProperties:
type: object
additionalProperties:
allOf:
- $ref: "definitions/key_backup_data.yaml"
example: {
"!room:example.org": {
"sessions": {
"sessionid1": {
"ephemeral": "base64+ephemeral+key",
"ciphertext": "base64+ciphertext+of+JSON+data",
"mac": "base64+mac+of+ciphertext"
}
}
}
}
404:
description: The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version."
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
delete:
summary: Delete a key from the backup
description: |-
Delete a key from the backup.
operationId: deleteRoomKeysKeyRoomId
security:
- accessToken: []
parameters:
- in: query
type: string
name: version
description: |-
The backup from which to delete the key
required: true
x-example: "1"
responses:
200:
description: The update succeeded
schema:
type: object
properties:
etag:
description: |-
The new etag value representing stored keys in the backup.
See ``GET /room_keys/version/{version}`` for more details.
type: string
example: "abcdefg"
count:
description: The number of keys stored in the backup
type: integer
example: 10
required:
- etag
- count
404:
description: |-
The backup was not found.
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Unknown backup version"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"

@ -102,7 +102,7 @@ paths:
responses: responses:
200: 200:
description: description:
The provided keys were sucessfully uploaded. The provided keys were successfully uploaded.
schema: schema:
type: object type: object
properties: properties:

@ -154,7 +154,7 @@ paths:
properties: properties:
user_id: user_id:
type: string type: string
description: The fully-qualified Matrix ID that has been registered. description: The fully-qualified Matrix ID for the account.
access_token: access_token:
type: string type: string
description: |- description: |-

@ -39,7 +39,7 @@ paths:
- accessToken: [] - accessToken: []
responses: responses:
200: 200:
description: The access token used in the request was succesfully invalidated. description: The access token used in the request was successfully invalidated.
schema: schema:
type: object type: object
properties: {} properties: {}
@ -64,7 +64,7 @@ paths:
- accessToken: [] - accessToken: []
responses: responses:
200: 200:
description: The user's access tokens were succesfully invalidated. description: The user's access tokens were successfully invalidated.
schema: schema:
type: object type: object
properties: {} properties: {}

@ -62,7 +62,7 @@ paths:
200: 200:
description: |- description: |-
OpenID token information. This response is nearly compatible with the OpenID token information. This response is nearly compatible with the
response documented in the `OpenID 1.0 Specification <http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse>`_ response documented in the `OpenID Connect 1.0 Specification <http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse>`_
with the only difference being the lack of an ``id_token``. Instead, with the only difference being the lack of an ``id_token``. Instead,
the Matrix homeserver's name is provided. the Matrix homeserver's name is provided.
examples: examples:

@ -330,8 +330,8 @@ paths:
The homeserver may change the flows available depending on whether a The homeserver may change the flows available depending on whether a
valid access token is provided. The homeserver SHOULD NOT revoke the valid access token is provided. The homeserver SHOULD NOT revoke the
access token provided in the request, however all other access tokens access token provided in the request. Whether other access tokens for
for the user should be revoked if the request succeeds. the user are revoked depends on the request parameters.
security: security:
- accessToken: [] - accessToken: []
operationId: changePassword operationId: changePassword
@ -346,6 +346,15 @@ paths:
type: string type: string
description: The new password for the account. description: The new password for the account.
example: "ihatebananas" example: "ihatebananas"
logout_devices:
type: boolean
description: |-
Whether the user's other access tokens, and their associated devices, should be
revoked if the request succeeds. Defaults to true.
When ``false``, the server can still take advantage of `the soft logout method <#soft-logout>`_
for the user's remaining devices.
example: true
auth: auth:
description: |- description: |-
Additional authentication information for the user-interactive authentication API. Additional authentication information for the user-interactive authentication API.

@ -85,5 +85,7 @@ paths:
type: string type: string
description: |- description: |-
A unique identifier for the event. A unique identifier for the event.
required:
- event_id
tags: tags:
- Room participation - Room participation

@ -45,6 +45,12 @@ paths:
The body of the request should be the content object of the event; the The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See fields in this object will vary depending on the type of event. See
`Room Events`_ for the ``m.`` event specification. `Room Events`_ for the ``m.`` event specification.
If the event type being sent is ``m.room.canonical_alias`` servers
SHOULD ensure that any new aliases being listed in the event are valid
per their grammar/syntax and that they point to the room ID where the
state event is to be sent. Servers do not validate aliases which are
being removed or are already present in the state event.
operationId: setRoomStateWithKey operationId: setRoomStateWithKey
security: security:
- accessToken: [] - accessToken: []
@ -92,6 +98,8 @@ paths:
type: string type: string
description: |- description: |-
A unique identifier for the event. A unique identifier for the event.
required:
- event_id
403: 403:
description: |- description: |-
The sender doesn't have permission to send the event into the room. The sender doesn't have permission to send the event into the room.
@ -102,5 +110,23 @@ paths:
"errcode": "M_FORBIDDEN", "errcode": "M_FORBIDDEN",
"error": "You do not have permission to send the event." "error": "You do not have permission to send the event."
} }
400:
description: |-
The sender's request is malformed.
Some example error codes include:
* ``M_INVALID_PARAMETER``: One or more aliases within the ``m.room.canonical_alias``
event have invalid syntax.
* ``M_BAD_ALIAS``: One or more aliases within the ``m.room.canonical_alias`` event
do not point to the room ID for which the state event is to be sent to.
schema:
$ref: "definitions/errors/error.yaml"
examples:
application/json: {
"errcode": "M_BAD_ALIAS",
"error": "The alias '#hello:example.org' does not point to this room."
}
tags: tags:
- Room participation - Room participation

@ -56,3 +56,11 @@ doesn't work, as in the following example:
properties: properties:
... ...
``` ```
## OpenAPI 3's "2xx" format for response codes
<!-- TODO: Remove this section after upgrading to OpenAPI v3 -->
In some cases, the schema will have HTTP response code definitions like
`2xx`, `3xx`, and `4xx`. These indicate that a response code within those
ranges (`2xx` = `200` to `299`) is valid for the schema.

@ -32,8 +32,8 @@ paths:
summary: Retrieves the events which precede the given event summary: Retrieves the events which precede the given event
description: |- description: |-
Retrieves a sliding-window history of previous PDUs that occurred in the given room. Retrieves a sliding-window history of previous PDUs that occurred in the given room.
Starting from the PDU ID(s) given in the ``v`` argument, the PDUs that preceded it Starting from the PDU ID(s) given in the ``v`` argument, the PDUs given in ``v`` and
are retrieved, up to the total number given by the ``limit``. the PDUs that preceded them are retrieved, up to the total number given by the ``limit``.
operationId: backfillRoom operationId: backfillRoom
security: security:
- signedRequest: [] - signedRequest: []
@ -63,6 +63,14 @@ paths:
description: |- description: |-
A transaction containing the PDUs that preceded the given event(s), including the given A transaction containing the PDUs that preceded the given event(s), including the given
event(s), up to the given limit. event(s), up to the given limit.
.. Note::
Though the PDU definitions require that ``prev_events`` and ``auth_events`` be limited
in number, the response of backfill MUST NOT be validated on these specific restrictions.
Due to historical reasons, it is possible that events which were previously accepted
would now be rejected by these limitations. The events should be rejected per usual by
the ``/send``, ``/get_missing_events``, and remaining endpoints.
schema: schema:
$ref: "definitions/unlimited_pdu_transaction.yaml" $ref: "definitions/unlimited_pdu_transaction.yaml"
"/get_missing_events/{roomId}": "/get_missing_events/{roomId}":

@ -20,7 +20,6 @@ properties:
server_name: server_name:
type: string type: string
description: DNS name of the homeserver. description: DNS name of the homeserver.
required: true
example: "example.org" example: "example.org"
verify_keys: verify_keys:
type: object type: object
@ -31,7 +30,6 @@ properties:
algorithm and ``abc123`` being the version in the example below). Together, algorithm and ``abc123`` being the version in the example below). Together,
this forms the Key ID. The version must have characters matching the regular this forms the Key ID. The version must have characters matching the regular
expression ``[a-zA-Z0-9_]``. expression ``[a-zA-Z0-9_]``.
required: true
additionalProperties: additionalProperties:
type: object type: object
title: Verify Key title: Verify Key
@ -44,8 +42,8 @@ properties:
key: key:
type: string type: string
description: The `Unpadded Base64`_ encoded key. description: The `Unpadded Base64`_ encoded key.
required: true
example: "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA" example: "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA"
required: ["key"]
old_verify_keys: old_verify_keys:
type: object type: object
description: |- description: |-
@ -69,27 +67,24 @@ properties:
type: integer type: integer
format: int64 format: int64
description: POSIX timestamp in milliseconds for when this key expired. description: POSIX timestamp in milliseconds for when this key expired.
required: true
example: 1532645052628 example: 1532645052628
key: key:
type: string type: string
description: The `Unpadded Base64`_ encoded key. description: The `Unpadded Base64`_ encoded key.
required: true
example: "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg" example: "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg"
required: ["expired_ts", "key"]
signatures: signatures:
type: object type: object
description: Digital signatures for this object signed using the ``verify_keys``. description: |-
Digital signatures for this object signed using the ``verify_keys``.
The signature is calculated using the process described at `Signing
JSON`_.
title: Signatures
additionalProperties: additionalProperties:
type: object type: object
title: Signed Server
example: {
"example.org": {
"ad25519:abc123": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU"
}
}
additionalProperties: additionalProperties:
type: string type: string
name: Encoded Signature Verification Key
valid_until_ts: valid_until_ts:
type: integer type: integer
format: int64 format: int64
@ -103,3 +98,4 @@ properties:
publishes a key which is valid for a significant amount of time without a way publishes a key which is valid for a significant amount of time without a way
for the homeserver owner to revoke it. for the homeserver owner to revoke it.
example: 1052262000000 example: 1052262000000
required: ["server_name", "verify_keys"]

@ -32,6 +32,10 @@ allOf:
description: |- description: |-
Event IDs for the authorization events that would Event IDs for the authorization events that would
allow this event to be in the room. allow this event to be in the room.
Must contain less than or equal to 10 events. Note that if the relevant
auth event selection rules are used, this restriction should never be
encountered.
example: ["$base64EncodedHash", "$AnotherEvent"] example: ["$base64EncodedHash", "$AnotherEvent"]
prev_events: prev_events:
type: array type: array
@ -41,6 +45,8 @@ allOf:
description: |- description: |-
Event IDs for the most recent events in the room Event IDs for the most recent events in the room
that the homeserver was aware of when it made this event. that the homeserver was aware of when it made this event.
Must contain less than or equal to 20 events.
example: ["$base64EncodedHash", "$AnotherEvent"] example: ["$base64EncodedHash", "$AnotherEvent"]
hashes: hashes:
type: object type: object

@ -32,6 +32,10 @@ allOf:
description: |- description: |-
Event IDs for the authorization events that would Event IDs for the authorization events that would
allow this event to be in the room. allow this event to be in the room.
Must contain less than or equal to 10 events. Note that if the relevant
auth event selection rules are used, this restriction should never be
encountered.
example: ["$URLsafe-base64EncodedHash", "$Another_Event"] example: ["$URLsafe-base64EncodedHash", "$Another_Event"]
prev_events: prev_events:
type: array type: array
@ -41,6 +45,8 @@ allOf:
description: |- description: |-
Event IDs for the most recent events in the room Event IDs for the most recent events in the room
that the homeserver was aware of when it made this event. that the homeserver was aware of when it made this event.
Must contain less than or equal to 20 events.
example: ["$URLsafe-base64EncodedHash", "$Another_Event"] example: ["$URLsafe-base64EncodedHash", "$Another_Event"]
required: required:
- auth_events - auth_events

@ -0,0 +1,58 @@
# Copyright 2018 New Vector Ltd
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# 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.
type: object
title: Room State
description: The state for the room.
properties:
origin:
type: string
description: The resident server's DNS name.
auth_chain:
type: array
description: |-
The auth chain for the entire current room state prior to the join event.
Note that events have a different format depending on the room version - check the
`room version specification`_ for precise event formats.
items:
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ that make up the auth chain. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
schema:
type: object
properties: []
example:
$ref: "../examples/minimal_pdu.json"
state:
type: array
description: |-
The resolved current room state prior to the join event.
The event format varies depending on the room version - check the `room version specification`_
for precise event formats.
items:
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ for the fully resolved state of the room. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
schema:
type: object
properties: []
example:
$ref: "../examples/minimal_pdu.json"
required: ["auth_chain", "state", "origin"]

@ -53,6 +53,8 @@ properties:
description: |- description: |-
Event IDs and reference hashes for the most recent events in the room Event IDs and reference hashes for the most recent events in the room
that the homeserver was aware of when it made this event. that the homeserver was aware of when it made this event.
Must contain less than or equal to 20 events.
items: items:
type: array type: array
maxItems: 2 maxItems: 2
@ -84,6 +86,10 @@ properties:
description: |- description: |-
Event IDs and reference hashes for the authorization events that would Event IDs and reference hashes for the authorization events that would
allow this event to be in the room. allow this event to be in the room.
Must contain less than or equal to 10 events. Note that if the relevant
auth event selection rules are used, this restriction should never be
encountered.
items: items:
type: array type: array
maxItems: 2 maxItems: 2

@ -1,4 +1,5 @@
# Copyright 2018 New Vector Ltd # Copyright 2018 New Vector Ltd
# Copyright 2020 The Matrix.org Foundation C.I.C.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -62,7 +63,7 @@ paths:
200: 200:
description: |- description: |-
A template to be used for the rest of the `Joining Rooms`_ handshake. Note that A template to be used for the rest of the `Joining Rooms`_ handshake. Note that
events have a different format depending on the room version - check the events have a different format depending on the room version - check the
`room version specification`_ for precise event formats. **The response body `room version specification`_ for precise event formats. **The response body
here describes the common event fields in more detail and may be missing other here describes the common event fields in more detail and may be missing other
required fields for a PDU.** required fields for a PDU.**
@ -165,14 +166,18 @@ paths:
put: put:
summary: Submit a signed join event to a resident server summary: Submit a signed join event to a resident server
description: |- description: |-
.. Note::
Servers should instead prefer to use the v2 ``/send_join``
endpoint.
Submits a signed join event to the resident server for it Submits a signed join event to the resident server for it
to accept it into the room's graph. Note that events have to accept it into the room's graph. Note that events have
a different format depending on the room version - check a different format depending on the room version - check
the `room version specification`_ for precise event formats. the `room version specification`_ for precise event formats.
**The request and response body here describes the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
operationId: sendJoin operationId: sendJoinV1
security: security:
- signedRequest: [] - signedRequest: []
parameters: parameters:
@ -257,47 +262,7 @@ paths:
- type: integer - type: integer
description: The value ``200``. description: The value ``200``.
example: 200 example: 200
- type: object - $ref: "./definitions/send_join_response.yaml"
title: Room State
description: The state for the room.
properties:
origin:
type: string
description: The resident server's DNS name.
auth_chain:
type: array
description: |-
The auth chain. Note that events have a different format depending on
the room version - check the `room version specification`_ for precise
event formats.
items:
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ that make up the auth chain. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
schema:
type: object
properties: []
example:
$ref: "examples/minimal_pdu.json"
state:
type: array
description: |-
The room state. The event format varies depending on the room version -
check the `room version specification`_ for precise event formats.
items:
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ for the fully resolved state of the room. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
schema:
type: object
properties: []
example:
$ref: "examples/minimal_pdu.json"
required: ["auth_chain", "state", "origin"]
examples: examples:
application/json: [ application/json: [
200, 200,

@ -0,0 +1,136 @@
# Copyright 2018 New Vector Ltd
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# 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.
swagger: '2.0'
info:
title: "Matrix Federation Join Room API"
version: "1.0.0"
host: localhost:8448
schemes:
- https
basePath: /_matrix/federation/v2
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
# Note: there is no v2 of make_join (yet)
"/send_join/{roomId}/{eventId}":
put:
summary: Submit a signed join event to a resident server
description: |-
.. Note::
This API is nearly identical to the v1 API with the
exception of the response format being fixed.
This endpoint is preferred over the v1 API as it provides
a more standarised response format. Senders which receive
a 400, 404, or other status code which indicates this endpoint
is not available should retry using the v1 API instead.
Submits a signed join event to the resident server for it
to accept it into the room's graph. Note that events have
a different format depending on the room version - check
the `room version specification`_ for precise event formats.
**The request and response body here describe the common
event fields in more detail and may be missing other required
fields for a PDU.**
operationId: sendJoinV2
security:
- signedRequest: []
parameters:
- in: path
name: roomId
type: string
description: The room ID that is about to be joined.
required: true
x-example: "!abc123:matrix.org"
- in: path
name: eventId
type: string
description: The event ID for the join event.
required: true
x-example: "$abc123:example.org"
- in: body
name: body
type: object
required: true
schema:
type: object
properties:
sender:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
origin:
type: string
description: The name of the joining homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the joining homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "join"}
properties:
membership:
type: string
description: The value ``join``.
example: "join"
required: ['membership']
required:
- state_key
- sender
- origin
- origin_server_ts
- type
- content
example: {
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "join"
}
}
responses:
200:
description: |-
The full state for the room, having accepted the join event.
schema:
$ref: "./definitions/send_join_response.yaml"
examples:
application/json: {
"origin": "matrix.org",
"auth_chain": [{"$ref": "examples/minimal_pdu.json"}],
"state": [{"$ref": "examples/minimal_pdu.json"}]
}

@ -34,7 +34,7 @@ paths:
Intermediate notary servers should cache a response for half of its Intermediate notary servers should cache a response for half of its
lifetime to avoid serving a stale response. Originating servers should lifetime to avoid serving a stale response. Originating servers should
avoid returning responses that expire in less than an hour to avoid avoid returning responses that expire in less than an hour to avoid
repeated reqests for a certificate that is about to expire. Requesting repeated requests for a certificate that is about to expire. Requesting
servers should limit how frequently they query for certificates to servers should limit how frequently they query for certificates to
avoid flooding a server with requests. avoid flooding a server with requests.

@ -1,4 +1,5 @@
# Copyright 2018 New Vector Ltd # Copyright 2018 New Vector Ltd
# Copyright 2020 The Matrix.org Foundation C.I.C.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -53,7 +54,7 @@ paths:
200: 200:
description: |- description: |-
A template to be used to call ``/send_leave``. Note that A template to be used to call ``/send_leave``. Note that
events have a different format depending on the room version - check the events have a different format depending on the room version - check the
`room version specification`_ for precise event formats. **The response body `room version specification`_ for precise event formats. **The response body
here describes the common event fields in more detail and may be missing other here describes the common event fields in more detail and may be missing other
required fields for a PDU.** required fields for a PDU.**
@ -142,14 +143,18 @@ paths:
put: put:
summary: Submit a signed leave event to a resident server summary: Submit a signed leave event to a resident server
description: |- description: |-
.. Note::
Servers should instead prefer to use the v2 ``/send_leave``
endpoint.
Submits a signed leave event to the resident server for it Submits a signed leave event to the resident server for it
to accept it into the room's graph. Note that events have to accept it into the room's graph. Note that events have
a different format depending on the room version - check a different format depending on the room version - check
the `room version specification`_ for precise event formats. the `room version specification`_ for precise event formats.
**The request and response body here describes the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
operationId: sendLeave operationId: sendLeaveV1
security: security:
- signedRequest: [] - signedRequest: []
parameters: parameters:

@ -0,0 +1,140 @@
# Copyright 2018 New Vector Ltd
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# 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.
swagger: '2.0'
info:
title: "Matrix Federation Leave Room API"
version: "1.0.0"
host: localhost:8448
schemes:
- https
basePath: /_matrix/federation/v2
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
# Note: there is no v2 of make_leave (yet)
"/send_leave/{roomId}/{eventId}":
put:
summary: Submit a signed leave event to a resident server
description: |-
.. Note::
This API is nearly identical to the v1 API with the
exception of the response format being fixed.
This endpoint is preferred over the v1 API as it provides
a more standarised response format. Senders which receive
a 400, 404, or other status code which indicates this endpoint
is not available should retry using the v1 API instead.
Submits a signed leave event to the resident server for it
to accept it into the room's graph. Note that events have
a different format depending on the room version - check
the `room version specification`_ for precise event formats.
**The request and response body here describe the common
event fields in more detail and may be missing other required
fields for a PDU.**
operationId: sendLeaveV2
security:
- signedRequest: []
parameters:
- in: path
name: roomId
type: string
description: The room ID that is about to be left.
required: true
x-example: "!abc123:matrix.org"
- in: path
name: eventId
type: string
description: The event ID for the leave event.
required: true
x-example: "$abc123:example.org"
- in: body
name: body
type: object
required: true
schema:
type: object
properties:
sender:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
origin:
type: string
description: The name of the leaving homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the leaving homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "leave"}
properties:
membership:
type: string
description: The value ``leave``.
example: "leave"
required: ['membership']
depth:
type: integer
description: This field must be present but is ignored; it may be 0.
example: 12
required:
- state_key
- sender
- origin
- origin_server_ts
- type
- depth
- content
example: {
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "leave"
}
}
responses:
200:
description: |-
An empty response to indicate the event was accepted into the graph by
the receiving homeserver.
schema:
type: object
title: Empty Object
description: An empty object.
examples:
application/json: {}

@ -102,12 +102,16 @@ paths:
properties: properties:
signatures: signatures:
type: object type: object
description: The server signatures for this event. title: Signatures
additionalProperties: additionalProperties:
type: object type: object
title: Server Signatures
additionalProperties: additionalProperties:
type: string type: string
description: |-
The server signatures for this event.
The signature is calculated using the process
described at `Signing JSON`_.
example: { example: {
"magic.forest": { "magic.forest": {
"ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"

@ -38,7 +38,7 @@ paths:
The sending server must wait and retry for a 200 OK response before sending a The sending server must wait and retry for a 200 OK response before sending a
transaction with a different ``txnId`` to the receiving server. transaction with a different ``txnId`` to the receiving server.
Note that events have a different format depending on the room version - check Note that events have a different format depending on the room version - check
the `room version specification`_ for precise event formats. the `room version specification`_ for precise event formats.
operationId: sendTransaction operationId: sendTransaction
security: security:
@ -77,43 +77,33 @@ paths:
The result of processing the transaction. The server is to use this response even in The result of processing the transaction. The server is to use this response even in
the event of one or more PDUs failing to be processed. the event of one or more PDUs failing to be processed.
schema: schema:
type: array type: object
minItems: 2 title: PDU Processing Results
maxItems: 2 description: The results for the processing of each PDU in the transaction.
items: properties:
- type: integer pdus:
description: The value ``200``. type: object
example: 200 description: |-
- type: object The PDUs from the original transaction. The string key represents the ID of the
title: PDU Processing Results PDU (event) that was processed.
description: The results for the processing of each PDU in the transaction. additionalProperties:
properties: type: object
pdus: title: PDU Processing Result
type: object description: Information about how the PDU was handled.
description: |- properties:
The PDUs from the original transaction. The string key represents the ID of the error:
PDU (event) that was processed. type: string
additionalProperties: description: |-
type: object A human readable description about what went wrong in processing this PDU.
title: PDU Processing Result If no error is present, the PDU can be considered successfully handled.
description: Information about how the PDU was handled. example: "You are not allowed to send a message to this room."
properties: required: ['pdus']
error:
type: string
description: |-
A human readable description about what went wrong in processing this PDU.
If no error is present, the PDU can be considered successfully handled.
example: "You are not allowed to send a message to this room."
required: ['pdus']
examples: examples:
application/json: [ application/json: {
200, "pdus": {
{ "$successful_event:example.org": {},
"pdus": { "$failed_event:example.org": {
"$successful_event:example.org": {}, "error": "You are not allowed to send a message to this room."
"$failed_event:example.org": {
"error": "You are not allowed to send a message to this room."
}
} }
} }
] }

@ -73,7 +73,7 @@ paths:
One-time keys for the queried devices. A map from user ID, to a One-time keys for the queried devices. A map from user ID, to a
map from devices to a map from ``<algorithm>:<key_id>`` to the key object. map from devices to a map from ``<algorithm>:<key_id>`` to the key object.
See the Client-Server Key Algorithms section for more information on See the `Client-Server Key Algorithms`_ section for more information on
the Key Object format. the Key Object format.
additionalProperties: additionalProperties:
type: object type: object
@ -88,10 +88,16 @@ paths:
description: The key, encoded using unpadded base64. description: The key, encoded using unpadded base64.
signatures: signatures:
type: object type: object
description: |- title: Signatures
Signature for the device. Mapped from user ID to signature object.
additionalProperties: additionalProperties:
type: string type: object
additionalProperties:
type: string
description: |-
Signature of the key object.
The signature is calculated using the process described at `Signing
JSON`_.
required: ['key', 'signatures'] required: ['key', 'signatures']
example: { example: {
"@alice:example.com": { "@alice:example.com": {

@ -1,3 +1,40 @@
r0.6.1
======
New Endpoints
-------------
- Added ``/rooms/{roomId}/aliases`` for retrieving local aliases for a room. (`#2562 <https://github.com/matrix-org/matrix-doc/issues/2562>`_)
Backwards Compatible Changes
----------------------------
- Added data structures for defining moderation policies in rooms per `MSC2313 <https://github.com/matrix-org/matrix-doc/pull/2313>`_. (`#2434 <https://github.com/matrix-org/matrix-doc/issues/2434>`_)
- Optionally invalidate other access tokens during password modification per `MSC2457 <https://github.com/matrix-org/matrix-doc/pull/2457>`_. (`#2523 <https://github.com/matrix-org/matrix-doc/issues/2523>`_)
- Add User-Interactive Authentication for SSO-backed homeserver per `MSC2454 <https://github.com/matrix-org/matrix-doc/pull/2454>`_. (`#2532 <https://github.com/matrix-org/matrix-doc/issues/2532>`_)
- Add soft-logout support per `MSC1466 <https://github.com/matrix-org/matrix-doc/issues/1466>`_. (`#2546 <https://github.com/matrix-org/matrix-doc/issues/2546>`_)
- Replaced legacy room alias handling with a more sustainable solution per `MSC2432 <https://github.com/matrix-org/matrix-doc/pull/2432>`_. (`#2562 <https://github.com/matrix-org/matrix-doc/issues/2562>`_)
Spec Clarifications
-------------------
- List available enum values for the room versions capability. (`#2245 <https://github.com/matrix-org/matrix-doc/issues/2245>`_)
- Fix various spelling errors throughout the specification. (`#2351 <https://github.com/matrix-org/matrix-doc/issues/2351>`_, `#2415 <https://github.com/matrix-org/matrix-doc/issues/2415>`_, `#2453 <https://github.com/matrix-org/matrix-doc/issues/2453>`_, `#2524 <https://github.com/matrix-org/matrix-doc/issues/2524>`_, `#2553 <https://github.com/matrix-org/matrix-doc/issues/2553>`_, `#2569 <https://github.com/matrix-org/matrix-doc/issues/2569>`_)
- Minor clarifications to token-based User-Interactive Authentication. (`#2369 <https://github.com/matrix-org/matrix-doc/issues/2369>`_)
- Minor clarification for what the user directory searches. (`#2381 <https://github.com/matrix-org/matrix-doc/issues/2381>`_)
- Fix key export format example to match the specification. (`#2430 <https://github.com/matrix-org/matrix-doc/issues/2430>`_)
- Clarify the IV data type for encrypted files. (`#2492 <https://github.com/matrix-org/matrix-doc/issues/2492>`_)
- Fix the ``.m.rule.contains_user_name`` default push rule to set the highlight tweak. (`#2519 <https://github.com/matrix-org/matrix-doc/issues/2519>`_)
- Clarify that an ``event_id`` is returned when sending events. (`#2525 <https://github.com/matrix-org/matrix-doc/issues/2525>`_)
- Fix some numbers in the specification to match their explanation text. (`#2554 <https://github.com/matrix-org/matrix-doc/issues/2554>`_)
- Move redaction algorithm into the room version specifications. (`#2563 <https://github.com/matrix-org/matrix-doc/issues/2563>`_)
- Clarify signature object structures for encryption. (`#2566 <https://github.com/matrix-org/matrix-doc/issues/2566>`_)
- Clarify which events are created as part of ``/createRoom``. (`#2571 <https://github.com/matrix-org/matrix-doc/issues/2571>`_)
- Remove claims that the homeserver is exclusively responsible for profile information in membership events. (`#2574 <https://github.com/matrix-org/matrix-doc/issues/2574>`_)
r0.6.0 r0.6.0
====== ======

@ -0,0 +1 @@
Add key backup (``/room_keys/*``) endpoints.

@ -1 +0,0 @@
List available enum values for the room versions capability.

@ -1 +0,0 @@
Fix various spelling errors throughout the specification.

@ -1 +0,0 @@
Minor clarifications to token-based User-Interactive Authentication.

@ -1 +0,0 @@
Minor clarification for what the user directory searches.

@ -1 +0,0 @@
Fix various spelling errors throughout the specification.

@ -1 +0,0 @@
Fix key export format example to match the specification.

@ -1 +0,0 @@
Added data structures for defining moderation policies in rooms per `MSC2313 <https://github.com/matrix-org/matrix-doc/pull/2313>`_.

@ -1 +0,0 @@
Fix various spelling errors throughout the specification.

@ -1 +0,0 @@
Clarify the IV data type for encrypted files.

@ -0,0 +1 @@
Fix issues with ``age`` and ``unsigned`` being shown in the wrong places.

@ -0,0 +1 @@
Fix definitions for room version capabilities.

@ -0,0 +1 @@
Fix various typos throughout the specification.

@ -0,0 +1 @@
Fix various typos throughout the specification.

@ -0,0 +1 @@
Clarify link to OpenID Connect specification.

@ -0,0 +1 @@
Clarify the behaviour of SSO login and UI-Auth.

@ -1,3 +1,34 @@
r0.1.4
======
New Endpoints
-------------
- Add new ``POST /publicRooms`` endpoint for filtering the room directory. (`#2305 <https://github.com/matrix-org/matrix-doc/issues/2305>`_)
- Add new v2 ``/send_join`` and ``/send_leave`` endpoints per `MSC1802 <https://github.com/matrix-org/matrix-doc/pull/1802>`_. (`#2547 <https://github.com/matrix-org/matrix-doc/issues/2547>`_)
Removed Endpoints
-----------------
- Remove the unused ``query_auth`` API per `MSC2451 <https://github.com/matrix-org/matrix-doc/pull/2451>`_. (`#2470 <https://github.com/matrix-org/matrix-doc/issues/2470>`_)
Spec Clarifications
-------------------
- Move auth event selection to a more obvious location. (`#2392 <https://github.com/matrix-org/matrix-doc/issues/2392>`_)
- Fix typo in Request Authentication python example. (`#2510 <https://github.com/matrix-org/matrix-doc/issues/2510>`_)
- Clarify which fields are required on the key server endpoints. (`#2527 <https://github.com/matrix-org/matrix-doc/issues/2527>`_)
- Clarify the limits of ``prev_events`` and ``auth_events`` for PDUs. (`#2538 <https://github.com/matrix-org/matrix-doc/issues/2538>`_)
- Clarify which events are targeted by backfill. (`#2559 <https://github.com/matrix-org/matrix-doc/issues/2559>`_)
- Fix the response format of the ``/send`` endpoint. (`#2560 <https://github.com/matrix-org/matrix-doc/issues/2560>`_)
- Clarify signature object structures for encryption. (`#2566 <https://github.com/matrix-org/matrix-doc/issues/2566>`_)
- Clarify the server names to use when signing requests. (`#2570 <https://github.com/matrix-org/matrix-doc/issues/2570>`_)
- Clarify the state/auth chain requirements for ``/send_join``. (`#2575 <https://github.com/matrix-org/matrix-doc/issues/2575>`_)
- Fix various spelling errors throughout the specification. (`#2577 <https://github.com/matrix-org/matrix-doc/issues/2577>`_)
r0.1.3 r0.1.3
====== ======

@ -1 +0,0 @@
Add new ``POST /publicRooms`` endpoint for filtering the room directory.

@ -1 +0,0 @@
Move auth event selection to a more obvious location.

@ -1 +0,0 @@
Remove the unused ``query_auth`` API per `MSC2451 <https://github.com/matrix-org/matrix-doc/pull/2451>`_.

@ -1 +0,0 @@
Fix typo in Request Authentication python example.

@ -1,8 +0,0 @@
{
"$ref": "core/state_event.json",
"state_key": "example.org",
"type": "m.room.aliases",
"content": {
"aliases": ["#somewhere:example.org", "#another:example.org"]
}
}

@ -3,6 +3,10 @@
"type": "m.room.canonical_alias", "type": "m.room.canonical_alias",
"state_key": "", "state_key": "",
"content": { "content": {
"alias": "#somewhere:localhost" "alias": "#somewhere:localhost",
"alt_aliases": [
"#somewhere:example.org",
"#myroom:example.com"
]
} }
} }

@ -1,14 +1,10 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.server_acl",
"state_key": "",
"content": { "content": {
"allow_ip_literals": false, "allow_ip_literals": false,
"allow": ["*"], "allow": ["*"],
"deny": ["*.evil.com", "evil.com"] "deny": ["*.evil.com", "evil.com"]
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.server_acl",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -34,24 +34,7 @@ properties:
type: integer type: integer
format: int64 format: int64
unsigned: unsigned:
description: Contains optional extra information about the event. $ref: unsigned_prop.yaml
properties:
age:
description: The time in milliseconds that has elapsed since the event was
sent. This field is generated by the local homeserver, and may be incorrect
if the local time on at least one of the two servers is out of sync, which can
cause the age to either be negative or greater than it actually is.
type: integer
redacted_because:
description: Optional. The event that redacted this event, if any.
title: Event
type: object
transaction_id:
description: The client-supplied transaction ID, if the client being given
the event is the same one which sent it.
type: string
title: UnsignedData
type: object
required: required:
- event_id - event_id
- sender - sender

@ -0,0 +1,34 @@
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# 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.
title: UnsignedData
type: object
description: Contains optional extra information about the event.
properties:
age:
description: The time in milliseconds that has elapsed since the event was
sent. This field is generated by the local homeserver, and may be incorrect
if the local time on at least one of the two servers is out of sync, which can
cause the age to either be negative or greater than it actually is.
type: integer
redacted_because:
description: The event that redacted this event, if any.
title: Event
type: object
transaction_id:
description: |
The client-supplied transaction ID, for example, provided via
``PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}``,
if the client being given the event is the same one which sent it.
type: string

@ -1,24 +0,0 @@
---
allOf:
- $ref: core-event-schema/state_event.yaml
description: 'This event is sent by a homeserver directly to inform of changes to the list of aliases it knows about for that room. The ``state_key`` for this event is set to the homeserver which owns the room alias. The entire set of known aliases for the room is the union of all the ``m.room.aliases`` events, one for each homeserver. Clients **should** check the validity of any room alias given in this list before presenting it to the user as trusted fact. The lists given by this event should be considered simply as advice on which aliases might exist, for which the client can perform the lookup to confirm whether it receives the correct room ID.'
properties:
content:
properties:
aliases:
description: A list of room aliases.
items:
type: string
type: array
required:
- aliases
type: object
state_key:
description: The homeserver domain which owns these room aliases.
type: string
type:
enum:
- m.room.aliases
type: string
title: Informs the room about what room aliases it has been given.
type: object

@ -3,21 +3,25 @@ allOf:
- $ref: core-event-schema/state_event.yaml - $ref: core-event-schema/state_event.yaml
description: |- description: |-
This event is used to inform the room about which alias should be This event is used to inform the room about which alias should be
considered the canonical one. This could be for display purposes or as considered the canonical one, and which other aliases point to the room.
suggestion to users which alias to use to advertise the room. This could be for display purposes or as suggestion to users which alias
to use to advertise and access the room.
A room with an ``m.room.canonical_alias`` event with an absent, null, or
empty ``alias`` field should be treated the same as a room with no
``m.room.canonical_alias`` event.
properties: properties:
content: content:
properties: properties:
alias: alias:
description: The canonical alias. description: |
The canonical alias for the room. If not present, null, or empty the
room should be considered to have no canonical alias.
type: string type: string
alt_aliases:
description: |
Alternative aliases the room advertises. This list can have aliases
despite the ``alias`` field being null, empty, or otherwise not present.
type: array
items:
type: string
type: object type: object
required:
- alias
state_key: state_key:
description: A zero-length string. description: A zero-length string.
pattern: '^$' pattern: '^$'

@ -48,10 +48,10 @@ properties:
content: content:
properties: properties:
avatar_url: avatar_url:
description: 'The avatar URL for this user, if any. This is added by the homeserver.' description: 'The avatar URL for this user, if any.'
type: string type: string
displayname: displayname:
description: 'The display name for this user, if any. This is added by the homeserver.' description: 'The display name for this user, if any.'
type: type:
- "string" - "string"
- "null" - "null"
@ -82,6 +82,10 @@ properties:
description: 'A single signature from the verifying server, in the format specified by the Signing Events section of the server-server API.' description: 'A single signature from the verifying server, in the format specified by the Signing Events section of the server-server API.'
title: Signatures title: Signatures
type: object type: object
additionalProperties:
type: object
additionalProperties:
type: string
token: token:
description: The token property of the containing third_party_invite object. description: The token property of the containing third_party_invite object.
type: string type: string
@ -96,16 +100,6 @@ properties:
- signed - signed
title: Invite title: Invite
type: object type: object
unsigned:
type: object
title: UnsignedData
description: Contains optional extra information about the event.
properties:
invite_room_state:
description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.'
items:
$ref: "stripped_state.yaml"
type: array
required: required:
- membership - membership
title: EventContent title: EventContent
@ -120,5 +114,15 @@ properties:
enum: enum:
- m.room.member - m.room.member
type: string type: string
unsigned:
allOf:
- $ref: "core-event-schema/unsigned_prop.yaml"
- type: object
properties:
invite_room_state:
description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.'
items:
$ref: "stripped_state.yaml"
type: array
title: The current membership state of a user in the room. title: The current membership state of a user in the room.
type: object type: object

@ -1,4 +1,4 @@
# Example: Proposal to adopt a template for MSCs # MSC0000: Template for new MSCs
*Note: Text written in italics represents notes about the section or proposal process. This document *Note: Text written in italics represents notes about the section or proposal process. This document
serves as an example of what a proposal could look like (in this case, a proposal to have a template) serves as an example of what a proposal could look like (in this case, a proposal to have a template)

@ -1,4 +1,11 @@
# Proposal for Open Governance of Matrix.org # Proposal for Open Governance of Matrix.org
----
**For updated information on the Foundation, please see https://matrix.org/foundation/**.
This MSC exists for historical reference.
----
This whole document is the proposed constitution proposal for Matrix.org, and This whole document is the proposed constitution proposal for Matrix.org, and
will form the basis of the first full Articles of Association (AoA) for [The will form the basis of the first full Articles of Association (AoA) for [The
@ -446,7 +453,7 @@ a high-level mission lock aligned with the above:
The foundation was then converted into a Community Interest Company, formalising The foundation was then converted into a Community Interest Company, formalising
its non-profit status under the approval of the independent [Community Interest its non-profit status under the approval of the independent [Community Interest
Companies Regulator](https://www.gov.uk/government/organisations/office-of-the-regulator-of-community-interest-companies), Companies Regulator](https://www.gov.uk/government/organisations/office-of-the-regulator-of-community-interest-companies),
which took effect Jan 2019. which took effect Jan 2019.
We are currently planning to release r0 of the Matrix Spec at the end of Jan 2019, and We are currently planning to release r0 of the Matrix Spec at the end of Jan 2019, and

@ -8,6 +8,12 @@ decryption key for the backups on the server, or cross-signing
([MSC1756](https://github.com/matrix-org/matrix-doc/pull/1756)) can store the ([MSC1756](https://github.com/matrix-org/matrix-doc/pull/1756)) can store the
signing keys. This proposal presents a standardized way of storing such data. signing keys. This proposal presents a standardized way of storing such data.
## Changes
- [MSC2472](https://github.com/matrix-org/matrix-doc/pull/2472) changed the
encryption algorithm used from an asymmetric algorithm (Curve25519) to a
symmetric algorithm (AES).
## Proposal ## Proposal
Secrets are data that clients need to use and that are sent through or stored Secrets are data that clients need to use and that are sent through or stored
@ -32,9 +38,8 @@ Each key has an ID, and the description of the key is stored in the user's
account_data using the event type `m.secret_storage.key.[key ID]`. The contents account_data using the event type `m.secret_storage.key.[key ID]`. The contents
of the account data for the key will include an `algorithm` property, which of the account data for the key will include an `algorithm` property, which
indicates the encryption algorithm used, as well as a `name` property, which is indicates the encryption algorithm used, as well as a `name` property, which is
a human-readable name. The contents will be signed as signed JSON using the a human-readable name. Other properties depend on the encryption algorithm,
user's master cross-signing key. Other properties depend on the encryption and are described below.
algorithm, and are described below.
Example: Example:
@ -43,7 +48,7 @@ A key with ID `abcdefg` is stored in `m.secret_storage.key.abcdefg`
```json ```json
{ {
"name": "Some key", "name": "Some key",
"algorithm": "m.secret_storage.v1.curve25519-aes-sha2", "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
// ... other properties according to algorithm // ... other properties according to algorithm
} }
``` ```
@ -55,13 +60,6 @@ secrets that the user would expect to be available on all their clients.
Unless the user specifies otherwise, clients will try to use the default key to Unless the user specifies otherwise, clients will try to use the default key to
decrypt secrets. decrypt secrets.
Clients MUST ensure that the key is trusted before using it to encrypt secrets.
One way to do that is to have the client that creates the key sign the key
description (as signed JSON) using the user's master cross-signing key.
Another way to do that is to prompt the user to enter the passphrase used to
generate the encryption key and ensure that the generated private key
corresponds to the public key.
#### Secret storage #### Secret storage
Encrypted data is stored in the user's account_data using the event type Encrypted data is stored in the user's account_data using the event type
@ -106,7 +104,7 @@ and the key descriptions for the keys would be:
```json ```json
{ {
"name": "Some key", "name": "Some key",
"algorithm": "m.secret_storage.v1.curve25519-aes-sha2", "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
// ... other properties according to algorithm // ... other properties according to algorithm
} }
``` ```
@ -116,45 +114,50 @@ and the key descriptions for the keys would be:
```json ```json
{ {
"name": "Some other key", "name": "Some other key",
"algorithm": "m.secret_storage.v1.curve25519-aes-sha2", "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
// ... other properties according to algorithm // ... other properties according to algorithm
} }
``` ```
#### Encryption algorithms #### Encryption algorithms
##### `m.secret_storage.v1.curve25519-aes-sha2` ##### `m.secret_storage.v1.aes-hmac-sha2`
Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. The data
is encrypted and MACed as follows:
The public key is stored in the `pubkey` property of the `m.secret_storage.key.[key 1. Given the secret storage key, generate 64 bytes by performing an HKDF with
ID]` account_data as a base64-encoded string. SHA-256 as the hash, a salt of 32 bytes of 0, and with the secret name as
the info. The first 32 bytes are used as the AES key, and the next 32 bytes
are used as the MAC key
2. Generate 16 random bytes, set bit 63 to 0 (in order to work around
differences in AES-CTR implementations), and use this as the AES
initialization vector. This becomes the `iv` property, encoded using base64.
3. Encrypt the data using AES-CTR-256 using the AES key generated above. This
encrypted data, encoded using base64, becomes the `ciphertext` property.
4. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256
using the MAC key generated above. The resulting MAC is base64-encoded and
becomes the `mac` property.
The data is encrypted and MACed as follows: (We use AES-CTR to match file encryption and key exports.)
1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral For the purposes of allowing clients to check whether a user has correctly
key and the public key to generate a shared secret. The public half of the entered the key, clients should:
ephemeral key, encoded using base64, becomes the `ephemeral` property.
2. Using the shared secret, generate 80 bytes by performing an HKDF using
SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string
as the info. The first 32 bytes are used as the AES key, the next 32 bytes
are used as the MAC key, and the last 16 bytes are used as the AES
initialization vector.
4. Encrypt the data using AES-CBC-256 with PKCS#7 padding. This encrypted
data, encoded using base64, becomes the `ciphertext` property.
5. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256
using the MAC key generated above. The first 8 bytes of the resulting MAC
are base64-encoded, and become the `mac` property.
(The key HKDF, AES, and HMAC steps are the same as what are used for encryption 1. encrypt and MAC a message consisting of 32 bytes of 0 as described above,
in olm and megolm.) using the empty string as the info parameter to the HKDF in step 1.
2. store the `iv` and `mac` in the `m.secret_storage.key.[key ID]`
account-data.
For example, the `m.secret_storage.key.[key ID]` for a key using this algorithm For example, the `m.secret_storage.key.key_id` for a key using this algorithm
could look like: could look like:
```json ```json
{ {
"name": "m.default", "name": "m.default",
"algorithm": "m.secret_storage.v1.curve25519-aes-sha2", "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
"pubkey": "base64+public+key" "iv": "random+data",
"mac": "mac+of+encrypted+zeros"
} }
``` ```
@ -164,8 +167,8 @@ and data encrypted using this algorithm could look like this:
{ {
"encrypted": { "encrypted": {
"key_id": { "key_id": {
"iv": "16+bytes+base64",
"ciphertext": "base64+encoded+encrypted+data", "ciphertext": "base64+encoded+encrypted+data",
"ephemeral": "base64+ephemeral+key",
"mac": "base64+encoded+mac" "mac": "base64+encoded+mac"
} }
} }
@ -174,7 +177,7 @@ and data encrypted using this algorithm could look like this:
###### Keys ###### Keys
When a user is given a raw key for `m.secret_storage.v1.curve25519-aes-sha2`, When a user is given a raw key for `m.secret_storage.v1.aes-hmac-sha2`,
it will be encoded as follows (this is the same as what is proposed in MSC1703): it will be encoded as follows (this is the same as what is proposed in MSC1703):
* prepend the two bytes 0x8b and 0x01 to the key * prepend the two bytes 0x8b and 0x01 to the key
@ -200,7 +203,8 @@ ID]` account-data:
"passphrase": { "passphrase": {
"algorithm": "m.pbkdf2", "algorithm": "m.pbkdf2",
"salt": "MmMsAlty", "salt": "MmMsAlty",
"iterations": 100000 "iterations": 100000,
"bits": 256
}, },
... ...
} }
@ -209,7 +213,9 @@ ID]` account-data:
**`m.pbkdf2`** **`m.pbkdf2`**
The key is generated using PBKDF2 using the salt given in the `salt` parameter, The key is generated using PBKDF2 using the salt given in the `salt` parameter,
and the number of iterations given in the `iterations` parameter. and the number of iterations given in the `iterations` parameter. The key size
that is generated is given by the `bits` parameter, or 256 bits if no `bits`
parameter is given.
### Sharing ### Sharing

@ -0,0 +1,77 @@
# Update auth rules to check notifications key in m.room.power_levels
## Introduction
The key `notifications` was added to the `m.room.power_levels` event after the
finalisation of the auth rules specified in room version 1. This leads to the
fact, that this dictionary is not subject to the same validation as other
dictionaries in the event, such as `users` or `events`. This especially means
that Alice is able to alter any entry within the dictionary including ones,
that are above her own power level, which is inconsistent with the behaviour
for the other two dictionaries.
[m.room.power_levels](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-power-levels)
[room version 1](https://matrix.org/docs/spec/rooms/v1)
## Proposal
The auth rules regarding `m.room.power_levels` have been established in room
version 1. The general idea here was that creators of a new `m.room.power_levels`
event are only able to make changes to something that is equal to or lower than
their own power level.
So, assuming a room with Alice (50), Bob (100), `m.room.power_levels` at 50,
`m.room.name` at 75 and `m.room.topic` at 25 would mean the following:
* Alice CAN alter `m.room.topic` to any power level up to her own, in this case 50
* Alice is NOT able to alter `m.room.name` since the current value is higher than
her own (75 > 50)
* Alice is NOT able to alter the power level of Bob, since his current value is
higher than her own (100 > 50)
* Alice is free to set the level for anything that has not been defined such as
`org.alice.message` up to a maximum of 50
Later on the key `notifications` was added to the `m.room.power_levels` event.
It contains a mapping of notification keys to a power level, that is required
for a user to trigger the specific notification. The most famous notification
type is the `@room` notification.
Going back to the original example because this key was added later on, the auth
rules make no mention of it, which enables the following behaviour. *It is assumed
that `@room` is at 75*
* Alice can add any key to the dictionary and set the value to any value she wants,
including ones higher than her own.
* Alice can alter the value for `@room` to any value she wants, including ones that
are higher than her own, even though her own value is lower.
The proposed solution is to alter the auth rules and add the `notifications` dictionary
to the same rules that handle `events` and `users`.
So the rule [10.d](https://matrix.org/docs/spec/rooms/v1.html) of the auth rules in
room version 1 would be updated in a new room version to:
> For each entry being added, changed or removed in events, users __and notifications__
>keys:
## Tradeoffs
The proposed solution would be a breaking change with current room versions and
the alternative would be to leave the `notifications` key without any checks.
## Security considerations
This is likely to improve security because it prevents malicious users that were
only given the right to emit `m.room.power_levels` so that they could alter a very
specific key, such as `invite`, from altering the rules established for triggering
notifications.
## Conclusion
The current spec conform behaviour regarding `notifications` is inconsistent with
behaviour shown by the other dictionaries and room administrators are very likely
expecting the `notifications` to work the same as them. The required change is minimal
is and also in line with the general spirit of the auth rules regarding the
`m.room.power_levels` event. A new room version is, however, required. This can be
done with other pending changes.

@ -0,0 +1,14 @@
# MSC2240: Room Version 6
A new room version, `6`, is proposed using [room version 5](https://matrix.org/docs/spec/rooms/v5.html) as a base
and incorporating the following MSCs:
* [MSC2209](https://github.com/matrix-org/matrix-doc/pull/2209) - Including notifications in power level auth rules.
* [MSC2432](https://github.com/matrix-org/matrix-doc/pull/2432) - Alias event authorisation and redaction.
* [MSC2540](https://github.com/matrix-org/matrix-doc/pull/2540) - Integers in canonical JSON compliance.
Though other MSCs are capable of being included in this version, they do not have sufficient implementation to be
considered stable enough for v6 rooms. A future room version may still include them.
Room version 6 upon being added to the specification shall be considered stable. No other room versions are affected
by this MSC.

@ -0,0 +1,101 @@
# Proposal for mandating case folding when processing e-mail addresses
[RFC822](https://tools.ietf.org/html/rfc822#section-3.4.7) mandates that
localparts in e-mail addresses must be processed with the original case
preserved. [The Matrix spec](https://matrix.org/docs/spec/appendices#pid-types)
doesn't mandate anything about processing e-mail addresses, other than the fact
that the domain part must be converted to lowercase, as domain names are case
insensitive.
On the other hand, most major e-mail providers nowadays process the localparts
of e-mail addresses as case insensitive. Therefore, most users expect localparts
to be treated case insensitively, and get confused when it's not. Some users,
for example, get confused over the fact that registering a 3PID association for
`john.doe@example.com` doesn't mean that the association is valid for
`John.Doe@example.com`, and don't expect to have to remember the exact
case they used to initially register the association (and sometimes get locked
out of their account because of that). So far we've seen that confusion occur
and lead to troubles of various degrees over several deployments of Synapse and
Sydent.
## Proposal
This proposal suggests changing the specification of the e-mail 3PID type in
[the Matrix spec appendices](https://matrix.org/docs/spec/appendices#pid-types)
to mandate that, before any processing, e-mail addresses must go through a full
case folding based on [the unicode mapping
file](https://www.unicode.org/Public/8.0.0/ucd/CaseFolding.txt), on top of
having their domain lowercased.
This means that `Strauß@Example.com` must be considered as being the same e-mail
address as `strauss@example.com`.
## Other considered solutions
A first look at this issue concluded that there was no need to add such a
mention to the spec, and that it can be considered an implementation detail.
However, [MSC2134](https://github.com/matrix-org/matrix-doc/pull/2134) changes
this: because hashing functions are case sensitive, we need both clients and
identity servers to follow the same policy regarding case sensitivity.
An initial version of this proposal proposed to mandate lowercasing e-mail
addresses instead of case folding them, however it was pointed out that this
solution might not be the best and most future-proof one.
Unicode normalisation was also looked at but judged unnecessary.
## Tradeoffs
Implementing this MSC in identity servers and homeservers might require the
databases of existing instances to be updated in a large part to case fold the
email addresses of existing associations, in order to avoid conflicts. However,
most of this update can usually be done by a background job running at startup,
so the UX improvement outweighs this trouble.
## Potential issues
### Conflicts with existing associations
Some users might already have two different accounts associated with the same
e-mail address but with different cases. This appears to happen in a small
number of cases, however, and can be dealt with by the identity server's or the
homeserver's maintainer.
For example, with Sydent, the process of dealing with such cases could look
like:
1. list all MXIDs associated with a variant of the email address, and the
timestamp of that association
2. delete all associations except for the most recent one [0]
3. inform the user of the deletion by sending them an email notice to the email
address
### Storing and querying
Most database engines don't support case folding, therefore querying all
e-mail addresses matching a case folded e-mail address might not be trivial,
e.g. an identity server querying all associations for `strauss@example.com` when
processing a `/lookup` request would be expected to also get associations for
`Strauß@Example.com`.
To address this issue, implementation maintainers are strongly encouraged to
make e-mail addresses go through a full case folding before storing them.
### Implementing case folding
The need for case folding in services on the Internet doesn't seem to be very
large currently (probably due to its young age), therefore there seem to be only
a few third-party implementation librairies out there. However, both
[Go](https://godoc.org/golang.org/x/text/cases#Fold), [Python
2](https://docs.python.org/2/library/stringprep.html#stringprep.map_table_b3)
and [Python 3](https://docs.python.org/3/library/stdtypes.html#str.casefold)
support it natively, and [a third-party JavaScript
implementation](https://github.com/ar-nelson/foldcase) exists which, although
young, seems to be working.
## Footnotes
[0]: This is specific to Sydent because of a bug it has where v1 lookups are
already processed case insensitively, which means it will return the most recent
association for any case of the given email address, therefore keeping only this
association won't change the result of v1 lookups.

@ -0,0 +1,230 @@
# User-Interactive Authentication for SSO-backed homeserver
Certain endpoints, such as `DELETE /_matrix/client/r0/devices/{deviceId}` and
`POST /_matrix/client/r0/account/3pid/add`, require the user to reconfirm their
identity, as a guard against a leaked access token being used to take over an
entire account.
On a normal homeserver, this is done by prompting the user to enter their
password. However, on a homeserver where users authenticate via a single-sign-on
system, the user doesn't have a password registered with the homeserver. Instead
we need to delegate that check to the SSO system.
At the protocol level, this means adding support for SSO to the
[user-interactive authentication API](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api).
In theory, once SSO is added as a possible flow for authentication, any clients
that already implement the [fallback process for unknown authentication types](https://matrix.org/docs/spec/client_server/r0.6.0#fallback)
will work fine without modification. It is unknown whether this is widely
supported among clients.
## Proposal
An [additional authentication type](https://matrix.org/docs/spec/client_server/r0.6.0#authentication-types)
of `m.login.sso` is added to the user-interactive authentication specification.
There are no additional parameters as part of this authentication type. As per
the user-interactive authentication specification, the only parameter included in
the `auth` dictionary should be the session ID from the homeserver, e.g.:
```json
{
"auth": {
"session": "<session ID>"
}
}
```
### Detailed fallback authentication flow:
The following is a re-iteration of the [fallback authentication flow](https://matrix.org/docs/spec/client_server/r0.6.0#fallback),
but with details filled in for the proposed new authentication type.
When choosing this authentication flow, the following should occur:
1. If the client wants to complete authentication using SSO, it opens a browser
window for `/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>`
with session set to the UI-Auth session id (from the "auth" dict).
The homeserver returns a page which asks for the user's confirmation before
proceeding. See the security considerations section below for why this is
necessary. For example, the page could say words to the effect of:
> A client is trying to remove a device/add an email address/take over your
> account. To confirm this action, **re-authenticate with single sign-on**.
> If you did not expect this, your account may be compromised!
2. The link, once the user clicks on it, goes to the SSO provider's page.
3. The SSO provider validates the user, and redirects the browser back to the
homeserver.
4. The homeserver validates the response from the SSO provider, updates the
user-interactive auth session to show that the SSO has completed, and
[serves the fallback auth completion page as specced](https://matrix.org/docs/spec/client_server/r0.6.0#fallback).
5. The client resubmits its original request, with its original session id,
which now should complete.
Note that the post-SSO URL on the homeserver is left up to the homeserver
implementation rather than forming part of the specification, choices might be
limited by the chosen SSO implementation, for example:
* SAML2 servers typically only support one URL per service provider, so in
practice it will need to be the same as that already used for the login flow
(for synapse, it's `/_matrix/saml2/authn_response`) - and the server needs to
be able to figure out if it's doing SSO for a login attempt or an SSO
attempt.
* CAS doesn't have the same restriction.
### Example flow:
A more complete example is provided below in which a user attempts to delete
a device and is pushed into the user interactive authentication process with
SSO being the only possible flow.
0. Client submits the request, which the server says requires SSO:
```
POST /_matrix/client/r0/delete_devices HTTP/1.1
Content-Type: application/json
Authorization: Bearer xyzzy
{
"devices": ["FSVVTZRRAA"]
}
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"flows": [
{
"stages": [
"m.login.sso"
]
}
],
"params": {},
"session": "dTKfsLHSAJeAhqfxUsvrIVJd"
}
```
1. Client opens a browser window for the fallback endpoint:
```
GET /_matrix/client/r0/auth/m.login.sso/fallback/web
?session=dTKfsLHSAJeAhqfxUsvrIVJd HTTP/1.1
HTTP/1.1 200 OK
<body>
A client is trying to remove a device from your account. To confirm this
action, <a href="https://sso_provider/validate?SAMLRequest=...">re-authenticate with single sign-on</a>.
If you did not expect this, your account may be compromised!
</body>
```
2. The user clicks the confirmation link which goes to the SSO provider's site:
```
GET https://sso_provider/validate?SAMLRequest=<etc> HTTP/1.1
<SAML/CAS or other SSO data>
```
3. The SSO provider validates the user and ends up redirecting the browser back
to the homeserver. The example below shows a 302 for simplicity, this might
vary based on SSO implementation.
```
HTTP/1.1 302 Moved
Location: https://homeserver/_matrix/saml2/authn_response?
SAMLResponse=<etc>
```
4. The browser sends the SSO response to the homeserver, which validates it and
shows the fallback auth completion page:
```
GET /_matrix/saml2/authn_response?SAMLResponse=<etc>
HTTP/1.1 200 OK
<script>
if (window.onAuthDone) {
window.onAuthDone();
} else if (window.opener && window.opener.postMessage) {
window.opener.postMessage("authDone", "*");
}
</script>
<p>Thank you.</p>
<p>You may now close this window and return to the application.</p>
```
5. The client closes the browser popup if it is still open, and resubmits its
original request, which now succeeds:
```
POST /_matrix/client/r0/delete_devices HTTP/1.1
Content-Type: application/json
Authorization: Bearer xyzzy
{
"auth": {
"session": "dTKfsLHSAJeAhqfxUsvrIVJd"
}
}
HTTP/1.1 200 OK
Content-Type: application/json
{}
```
## Alternatives
An alternative client flow where the fallback auth ends up redirecting to a
given URI, instead of doing JavaScript `postMessage` foo could be considered.
This is probably an orthogonal change to the fallback auth though.
## Security considerations
### Why we need user to confirm before the SSO flow
Recall that the thing we are trying to guard against here is a single leaked
access-token being used to take over an entire account. So let's assume the
attacker has got hold of an access token for your account. What happens if the
confirmation step is skipped?
The attacker, who has your access token, starts a UI Authentication session to
add their email address to your account.
They then sends you a link "hey, check out this cool video!"; the link leads (via
an innocent-looking URL shortener or some other phishing technique) to
`/_matrix/client/r0/auth/m.login.sso/fallback/web?session=<...>`, with the ID of
the session that he just created.
Since there is no confirmation step, the server redirects directly to the SSO
provider.
It's common for SSO providers to redirect straight back to the app if you've
recently authenticated with them; even in the best case, the SSO provider shows
an innocent message along the lines of "Confirm that you want to sign in to
\<your Matrix homeserver>".
After redirecting back to the homeserver, the SSO is completed and the
attacker's session is validated. They are now able to make their malicious
change to your account.
This problem can be mitigated by clearly telling the user what is about to happen.
### Reusing User Interactive Authentication sessions
The security of this relies on User Interactive Authentication sessions only
being used for the same request as they were initiated for. This security is not
only a concern for the proposed SSO authentication type. It is not believed
that this is currently enforced in implementations.
## Unstable prefix
A vendor prefix of `org.matrix.login.sso` is proposed (instead of `m.login.sso`)
until this is part of the specification.

@ -0,0 +1,56 @@
# Invalidating devices during password modification
There are multiple use cases for why a user might want to modify their password:
* Adopting a password manager (to use a unique password or more secure password).
* Password rotation.
* Re-secure a compromised account.
* ... probably tons of others ...
These can be summarized into two groups:
1. "My account has been compromised and I need to re-secure it."
2. "I just want to change my password."
The [current Matrix specification](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password)
does not provide a way to differentiate between these use cases. It currently
specifies behavior that fits well into the first use-case above: that the
sessions except the current session should be revoked.
It is reasonable for a client to want to specify this behavior to offer two
different workflows:
1. Modify a password and log all other devices out (for use when an account has
been compromised).
2. Modify a password and do not touch any session data (for use in a
non-malicious situations).
Alternately a client may default to whichever workflow is best for their users.
## Proposal
An optional field is added to the JSON body of the [password reset endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-password)
called `logout_devices`. This is a boolean flag (defaulting to `true`) that
signals to whether other devices and sessions should be invalidated after
modifying the password.
## Potential issues
The specification states:
> The homeserver SHOULD NOT revoke the access token provided in the request,
> however all other access tokens for the user should be revoked if the request
> succeeds.
Defaulting `logout_devices` to `true` should be backwards compatible.
## Alternatives
A new endpoint could be provided in a future version of the specification that
supports an additional field (as described above).
## Security considerations
By defaulting to invalidating devices and sessions the security considerations
of this endpoint should remain intact. A client will need to be modified to
choose to keep other devices active.

@ -0,0 +1,88 @@
# Symmetric SSSS
[MSC1946 (Secure Secret Storage and
Sharing)](https://github.com/matrix-org/matrix-doc/pull/1946) proposed a way of
storing encrypted secrets on the server. In the proposal, secrets were
encrypted using a Curve25519 key, which was chosen to allow easier migration
from key backups that we created before the backup key was stored using it.
However this does not provide any guarantees that data stored using the
proposal came from a trusted source. To remedy this, we propose to change the
encryption to use AES with a MAC to ensure that only someone who knows the key
is able to store data.
## Proposal
* The `m.secret_storage.v1.curve25519-aes-sha2` method proposed in MSC1946 is
removed.
* A new method, `m.secret_storage.v1.aes-hmac-sha2`, is added. With this
method, the Secret Storage key may be any size (though 256 bits is
recommended), and data is encrypted as follows:
1. Given the secret storage key, generate 64 bytes by performing an HKDF with
SHA-256 as the hash, a salt of 32 bytes of 0, and with the secret name as
the info. The first 32 bytes are used as the AES key, and the next 32 bytes
are used as the MAC key
2. Generate 16 random bytes, set bit 63 to 0 (in order to work around
differences in AES-CTR implementations), and use this as the AES
initialization vector. This becomes the `iv` property, encoded using base64.
3. Encrypt the data using AES-CTR-256 using the AES key generated above. This
encrypted data, encoded using base64, becomes the `ciphertext` property.
4. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256
using the MAC key generated above. The resulting MAC is base64-encoded and
becomes the `mac` property.
(We use AES-CTR to match file encryption and key exports.)
If the key Secret Storage key is generated from a passphrase, information
about how to generate the key is stored in the `passphrase` property of the
key's account-data in a similar manner to what was done with the original
`m.secret_storage.v1.curve25519-aes-sha2` method, except that there is an
optional `bits` parameter that defaults to 256, and indicates the number of
bits that should be generated from PBKDF2 (in other words, the size of the
key).
* For the purposes of allowing clients to check whether a user has correctly
entered the key, clients should:
1. encrypt and MAC a message consisting of 32 bytes of 0 as described above,
using the empty string as the info parameter to the HKDF in step 1.
2. store the `iv` and `mac` in the `m.secret_storage.key.[key ID]`
account-data.
* The `passthrough` property specified in the "Enconding the recovery key for
server-side storage via MSC1946" section of MSC1219 is removed. The primary
purpose of that property was to allow easy migration of pre-MSC1946 backups,
so that users could reuse the backup recovery key as the Secret Storage key
without needing to re-enter the recovery key. However, since we are now
using a symmetric encryption algorithm, the client needs to know the key that
is used to encrypt, so the purpose of the field cannot be fulfilled.
* Signing the Secret Storage key with the user's master cross-signing key is no
longer required. The key is trusted on the basis of the user entering the
key/passphrase.
## Potential issues
Users who have data stored using the old encryption algorithm will need their
data migrated. Clients that support the old algorithm but not the new
algorithm will not be able to use the migrated secrets until they are updated
with the new algorithms. This should not be a major problem because the only
clients that are known to have implemented the old algorithm are Riot
Web/Android/iOS, and they have been upgraded to implement the new algorithm.
## Alternatives
Rather than switching to a symmetric encryption algorithm, we could stay with
an asymmetric encryption algorithm, and add on a method to authenticate the
data. However, it is much safer to use well-known cryptographic methods rather
than trying to invent something new. Since the main reason for using an
asymmetric scheme was to ease migration from older key backups without
requiring the user to re-enter the key, but this is no longer possible due to
the need to authenticate the data using the Secret Storage key, there is no
reason to stay with an asymmetric algorithm. It is also better to use
cryptographic methods already used in Matrix where possible, rather than
introducing something new.

@ -0,0 +1,14 @@
# MSC2526: Add ability to delete key backups
[MSC1219](https://github.com/matrix-org/matrix-doc/issues/1219) defined a
mechanism for key backups. However, it inadvertently omitted the endpoint to
delete an entire key backup. This proposal adds the endpoint.
## Proposal
An endpoint is added, `DELETE /room_keys/version/{version}`, that deletes a
backup version. Both the information about the key backup, as well as all keys
associated with the backup should be deleted. If the specified version was
previously deleted, the endpoint succeeds, returning an HTTP code of 200. If
the specified version never existed, the endpoint returns an HTTP code of 404
with a Matrix `errcode` of `M_NOT_FOUND`.

@ -0,0 +1,65 @@
# MSC2540: Stricter event validation: JSON compliance
## Background
There has been [prior discussions](https://github.com/matrix-org/matrix-doc/issues/1646)
about validating events more strictly. This MSC proposes fixing a small piece of
this: JSON compliance.
The [Canonical JSON](https://matrix.org/docs/spec/appendices#canonical-json)
specification requires that numbers that are serialized in JSON are integers in
the inclusive range of `[-(2^53) + 1, (2^53) - 1]`, which matches the requirements of
[section 6 of RFC 7159](https://tools.ietf.org/html/rfc7159). Note that it is
not explicit, but all floats are invalid.
It is worth mentioning that there are common extensions to JSON which produce
invalid JSON according to the Matrix specification; some programming langauges
even support these by default. One common additional feature is handling
"special" float values: `Infinity`, `-Infinity`, and `NaN`.
## Proposal
In a future room version, homeserver implementations are to strictly enforce
the JSON compliance of the Canonical JSON specification for events.
Non-compliant events should be treated like any other malformed event,
for example by rejecting the request with an HTTP 400 error with `M_BAD_JSON`,
or by discarding the event.
The rationale for doing this in a future room version is to avoid a split brain
room -- where some federated servers believe an event is valid and others reject
it as invalid. Rooms will be able to opt into this behavior as part of a room
version upgrade.
Homeserver implementations are not to strictly enforce this JSON compliance in
[room versions 1, 2, 3, 4, and 5](https://matrix.org/docs/spec/#complete-list-of-room-versions).
The rationale is essentially the same as why a future room version is necessary:
this ensures that all federated servers treat the same events as valid.
## Potential issues
Homeserver implementations might include JSON parsers which are stricter than
others. It may not be worthwhile or reasonable to loosen those restrictions for
stable room versions.
## Alternatives
It could be argued that this MSC is unnecessary since it does not add any new
requirements for handling of JSON data. Unfortunately starting to enforce these
requirements in current rooms could cause federation to break as homeservers
will disagree on whether events are valid.
## Security considerations
N/A
## Unstable prefix
A room version of `org.matrix.strict_canonicaljson` until a future room version
is available. This room version will use
[room version 5](https://matrix.org/docs/spec/rooms/v5) as base and include the
above modifications.

@ -0,0 +1,83 @@
/*
Original styles generated from:
pygmentize -f html -S colorful -a pre.code > ./scripts/css/pygments.css
Rules for which we don't want the syntax highlighter to kick in are commented
out at the bottom.
Windows users: if you regenerate this file, you'll need to re-save it as utf-8
to make docutils happy.
*/
/* DIFFS */
pre.code .gd { color: #A00000 } /* Generic.Deleted */
pre.code .gi { color: #00A000 } /* Generic.Inserted */
/* UNUSED */
/*pre.code .hll { background-color: #ffffcc }*/
/*pre.code { background: #ffffff; }*/
/*pre.code .c { color: #888888 } !* Comment *!*/
/*pre.code .err { color: #FF0000; background-color: #FFAAAA } !* Error *!*/
/*pre.code .k { color: #008800; font-weight: bold } !* Keyword *!*/
/*pre.code .o { color: #333333 } !* Operator *!*/
/*pre.code .ch { color: #888888 } !* Comment.Hashbang *!*/
/*pre.code .cm { color: #888888 } !* Comment.Multiline *!*/
/*pre.code .cp { color: #557799 } !* Comment.Preproc *!*/
/*pre.code .cpf { color: #888888 } !* Comment.PreprocFile *!*/
/*pre.code .c1 { color: #888888 } !* Comment.Single *!*/
/*pre.code .cs { color: #cc0000; font-weight: bold } !* Comment.Special *!*/
/*pre.code .ge { font-style: italic } !* Generic.Emph *!*/
/*pre.code .gr { color: #FF0000 } !* Generic.Error *!*/
/*pre.code .gh { color: #000080; font-weight: bold } !* Generic.Heading *!*/
/*pre.code .go { color: #888888 } !* Generic.Output *!*/
/*pre.code .gp { color: #c65d09; font-weight: bold } !* Generic.Prompt *!*/
/*pre.code .gs { font-weight: bold } !* Generic.Strong *!*/
/*pre.code .gu { color: #800080; font-weight: bold } !* Generic.Subheading *!*/
/*pre.code .gt { color: #0044DD } !* Generic.Traceback *!*/
/*pre.code .kc { color: #008800; font-weight: bold } !* Keyword.Constant *!*/
/*pre.code .kd { color: #008800; font-weight: bold } !* Keyword.Declaration *!*/
/*pre.code .kn { color: #008800; font-weight: bold } !* Keyword.Namespace *!*/
/*pre.code .kp { color: #003388; font-weight: bold } !* Keyword.Pseudo *!*/
/*pre.code .kr { color: #008800; font-weight: bold } !* Keyword.Reserved *!*/
/*pre.code .kt { color: #333399; font-weight: bold } !* Keyword.Type *!*/
/*pre.code .m { color: #6600EE; font-weight: bold } !* Literal.Number *!*/
/*pre.code .s { background-color: #fff0f0 } !* Literal.String *!*/
/*pre.code .na { color: #0000CC } !* Name.Attribute *!*/
/*pre.code .nb { color: #007020 } !* Name.Builtin *!*/
/*pre.code .nc { color: #BB0066; font-weight: bold } !* Name.Class *!*/
/*pre.code .no { color: #003366; font-weight: bold } !* Name.Constant *!*/
/*pre.code .nd { color: #555555; font-weight: bold } !* Name.Decorator *!*/
/*pre.code .ni { color: #880000; font-weight: bold } !* Name.Entity *!*/
/*pre.code .ne { color: #FF0000; font-weight: bold } !* Name.Exception *!*/
/*pre.code .nf { color: #0066BB; font-weight: bold } !* Name.Function *!*/
/*pre.code .nl { color: #997700; font-weight: bold } !* Name.Label *!*/
/*pre.code .nn { color: #0e84b5; font-weight: bold } !* Name.Namespace *!*/
/*pre.code .nt { color: #007700 } !* Name.Tag *!*/
/*pre.code .nv { color: #996633 } !* Name.Variable *!*/
/*pre.code .ow { color: #000000; font-weight: bold } !* Operator.Word *!*/
/*pre.code .w { color: #bbbbbb } !* Text.Whitespace *!*/
/*pre.code .mb { color: #6600EE; font-weight: bold } !* Literal.Number.Bin *!*/
/*pre.code .mf { color: #6600EE; font-weight: bold } !* Literal.Number.Float *!*/
/*pre.code .mh { color: #005588; font-weight: bold } !* Literal.Number.Hex *!*/
/*pre.code .mi { color: #0000DD; font-weight: bold } !* Literal.Number.Integer *!*/
/*pre.code .mo { color: #4400EE; font-weight: bold } !* Literal.Number.Oct *!*/
/*pre.code .sa { background-color: #fff0f0 } !* Literal.String.Affix *!*/
/*pre.code .sb { background-color: #fff0f0 } !* Literal.String.Backtick *!*/
/*pre.code .sc { color: #0044DD } !* Literal.String.Char *!*/
/*pre.code .dl { background-color: #fff0f0 } !* Literal.String.Delimiter *!*/
/*pre.code .sd { color: #DD4422 } !* Literal.String.Doc *!*/
/*pre.code .s2 { background-color: #fff0f0 } !* Literal.String.Double *!*/
/*pre.code .se { color: #666666; font-weight: bold; background-color: #fff0f0 } !* Literal.String.Escape *!*/
/*pre.code .sh { background-color: #fff0f0 } !* Literal.String.Heredoc *!*/
/*pre.code .si { background-color: #eeeeee } !* Literal.String.Interpol *!*/
/*pre.code .sx { color: #DD2200; background-color: #fff0f0 } !* Literal.String.Other *!*/
/*pre.code .sr { color: #000000; background-color: #fff0ff } !* Literal.String.Regex *!*/
/*pre.code .s1 { background-color: #fff0f0 } !* Literal.String.Single *!*/
/*pre.code .ss { color: #AA6600 } !* Literal.String.Symbol *!*/
/*pre.code .bp { color: #007020 } !* Name.Builtin.Pseudo *!*/
/*pre.code .fm { color: #0066BB; font-weight: bold } !* Name.Function.Magic *!*/
/*pre.code .vc { color: #336699 } !* Name.Variable.Class *!*/
/*pre.code .vg { color: #dd7700; font-weight: bold } !* Name.Variable.Global *!*/
/*pre.code .vi { color: #3333BB } !* Name.Variable.Instance *!*/
/*pre.code .vm { color: #996633 } !* Name.Variable.Magic *!*/
/*pre.code .il { color: #0000DD; font-weight: bold } !* Literal.Number.Integer.Long *!*/

@ -273,6 +273,7 @@ def rst2html(i, o, stylesheets):
writer_name="html", writer_name="html",
settings_overrides={ settings_overrides={
"stylesheet_path": stylesheets, "stylesheet_path": stylesheets,
"syntax_highlight": "short",
}, },
) )

@ -213,6 +213,7 @@ def get_json_schema_object_fields(obj, enforce_title=False):
res = process_data_type(additionalProps) res = process_data_type(additionalProps)
tables = res["tables"] tables = res["tables"]
val_title = res["title"] val_title = res["title"]
gen_title = "{%s: %s}" % (key_type, val_title)
if res.get("enum_desc") and val_title != "enum": if res.get("enum_desc") and val_title != "enum":
# A map to enum needs another table with enum description # A map to enum needs another table with enum description
tables.append(TypeTable( tables.append(TypeTable(
@ -220,7 +221,7 @@ def get_json_schema_object_fields(obj, enforce_title=False):
rows=[TypeTableRow(key="(mapped value)", title="enum", desc=res["desc"])] rows=[TypeTableRow(key="(mapped value)", title="enum", desc=res["desc"])]
)) ))
return { return {
"title": "{%s: %s}" % (key_type, val_title), "title": obj_title if obj_title else gen_title,
"tables": tables, "tables": tables,
} }
@ -909,6 +910,13 @@ class MatrixUnits(Units):
Units.prop(json_schema, "properties/content") Units.prop(json_schema, "properties/content")
) )
# Include UnsignedData if it is present on the object
unsigned = Units.prop(json_schema, "properties/unsigned")
if unsigned:
tbls = get_tables_for_schema(unsigned)
for tbl in tbls:
schema["content_fields"].append(tbl)
# grab msgtype if it is the right kind of event # grab msgtype if it is the right kind of event
msgtype = Units.prop( msgtype = Units.prop(
json_schema, "properties/content/properties/msgtype/enum" json_schema, "properties/content/properties/msgtype/enum"

@ -45,7 +45,7 @@ following grammar::
IPv6char = DIGIT / %x41-46 / %x61-66 / ":" / "." IPv6char = DIGIT / %x41-46 / %x61-66 / ":" / "."
; 0-9, A-F, a-f, :, . ; 0-9, A-F, a-f, :, .
dns-name = *255dns-char dns-name = 1*255dns-char
dns-char = DIGIT / ALPHA / "-" / "." dns-char = DIGIT / ALPHA / "-" / "."

@ -39,6 +39,17 @@ range where they can be accurately represented using IEEE double precision
floating point numbers since a number of JSON libraries represent all numbers floating point numbers since a number of JSON libraries represent all numbers
using this representation. using this representation.
.. WARNING::
Events in room versions 1, 2, 3, 4, and 5 might not be fully compliant with
these restrictions. Servers SHOULD be capable of handling JSON which is considered
invalid by these restrictions where possible.
The most notable consideration is that integers might not be in the range
specified above.
.. Note::
Float values are not permitted by this encoding.
.. code:: python .. code:: python
import json import json

@ -1,4 +1,4 @@
.. Copyright 2016 OpenMarket Ltd .. Copyright 2016-2020 The Matrix.org Foundation C.I.C.
.. ..
.. Licensed under the Apache License, Version 2.0 (the "License"); .. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License. .. you may not use this file except in compliance with the License.
@ -45,6 +45,7 @@ Other versions of this specification
The following other versions are also available, in reverse chronological order: 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. - `HEAD <https://matrix.org/docs/spec/client_server/unstable.html>`_: Includes all changes since the latest versioned release.
- `r0.6.1 <https://matrix.org/docs/spec/client_server/r0.6.1.html>`_
- `r0.6.0 <https://matrix.org/docs/spec/client_server/r0.6.0.html>`_ - `r0.6.0 <https://matrix.org/docs/spec/client_server/r0.6.0.html>`_
- `r0.5.0 <https://matrix.org/docs/spec/client_server/r0.5.0.html>`_ - `r0.5.0 <https://matrix.org/docs/spec/client_server/r0.5.0.html>`_
- `r0.4.0 <https://matrix.org/docs/spec/client_server/r0.4.0.html>`_ - `r0.4.0 <https://matrix.org/docs/spec/client_server/r0.4.0.html>`_
@ -123,6 +124,10 @@ The common error codes are:
:``M_UNKNOWN_TOKEN``: :``M_UNKNOWN_TOKEN``:
The access token specified was not recognised. The access token specified was not recognised.
An additional response parameter, ``soft_logout``, might be present on the response
for 401 HTTP status codes. See `the soft logout section <#soft-logout>`_ for more
information.
:``M_MISSING_TOKEN``: :``M_MISSING_TOKEN``:
No access token was specified for the request. No access token was specified for the request.
@ -404,6 +409,24 @@ should pass the ``device_id`` in the request body. If the client sets the
to that device. There is therefore at most one active access token assigned to to that device. There is therefore at most one active access token assigned to
each device at any one time. each device at any one time.
Soft logout
~~~~~~~~~~~
When a request fails due to a 401 status code per above, the server can
include an extra response parameter, ``soft_logout``, to indicate if the client's
persisted information can be retained. This defaults to ``false``, indicating
that the server has destroyed the session. Any persisted state held by the client,
such as encryption keys and device information, must not be reused and must be discarded.
When ``soft_logout`` is true, the client can acquire a new access token by
specifying the device ID it is already using to the login API. In most cases
a ``soft_logout: true`` response indicates that the user's session has expired
on the server-side and the user simply needs to provide their credentials again.
In either case, the client's previously known access token will no longer function.
.. _`user-interactive authentication`:
User-Interactive Authentication API User-Interactive Authentication API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -643,6 +666,7 @@ This specification defines the following auth types:
- ``m.login.password`` - ``m.login.password``
- ``m.login.recaptcha`` - ``m.login.recaptcha``
- ``m.login.oauth2`` - ``m.login.oauth2``
- ``m.login.sso``
- ``m.login.email.identity`` - ``m.login.email.identity``
- ``m.login.msisdn`` - ``m.login.msisdn``
- ``m.login.token`` - ``m.login.token``
@ -782,6 +806,18 @@ 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 the OAuth flow has completed, the client retries the request with the session
only, as above. only, as above.
Single Sign-On
<<<<<<<<<<<<<<
:Type:
``m.login.sso``
:Description:
Authentication is supported by authorising with an external single sign-on
provider.
A client wanting to complete authentication using SSO should use the
`Fallback`_ mechanism. See `SSO during User-Interactive Authentication`_ for
more information.
Email-based (identity / homeserver) Email-based (identity / homeserver)
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
:Type: :Type:
@ -886,6 +922,8 @@ should open is::
Where ``auth type`` is the type name of the stage it is attempting and 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. ``session ID`` is the ID of the session given by the homeserver.
.. _`user-interactive authentication fallback completion`:
This MUST return an HTML page which can perform this authentication stage. This This MUST return an HTML page which can perform this authentication stage. This
page must use the following JavaScript when the authentication has been page must use the following JavaScript when the authentication has been
completed: completed:
@ -1103,7 +1141,7 @@ with ``403 Forbidden`` and an error code of ``M_FORBIDDEN``.
If the homeserver advertises ``m.login.sso`` as a viable flow, and the client 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 supports it, the client should redirect the user to the ``/redirect`` endpoint
for `Single Sign-On <#sso-client-login>`_. After authentication is complete, the for `client login via SSO`_. After authentication is complete, the
client will need to submit a ``/login`` request matching ``m.login.token``. client will need to submit a ``/login`` request matching ``m.login.token``.
{{login_cs_http_api}} {{login_cs_http_api}}
@ -1561,8 +1599,6 @@ Room Events
This specification outlines several standard event types, all of which are This specification outlines several standard event types, all of which are
prefixed with ``m.`` prefixed with ``m.``
{{m_room_aliases_event}}
{{m_room_canonical_alias_event}} {{m_room_canonical_alias_event}}
{{m_room_create_event}} {{m_room_create_event}}
@ -1575,6 +1611,15 @@ prefixed with ``m.``
{{m_room_redaction_event}} {{m_room_redaction_event}}
Historical events
+++++++++++++++++
Some events within the ``m.`` namespace might appear in rooms, however they
serve no significant meaning in this version of the specification. They are:
* ``m.room.aliases``
Previous versions of the specification have more information on these events.
Syncing Syncing
~~~~~~~ ~~~~~~~
@ -1746,39 +1791,7 @@ redacted include a ``redacted_because`` key whose value is the event that caused
it to be redacted, which may include a reason. 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 exact algorithm to apply against an event is defined in the `room version specification`_.
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`` The server should add the event causing the redaction to the ``unsigned``
property of the redacted event, under the ``redacted_because`` key. When a property of the redacted event, under the ``redacted_because`` key. When a
@ -1838,15 +1851,15 @@ send update requests to other servers. However, homeservers MUST handle
``GET`` requests to resolve aliases on other servers; they should do this using ``GET`` requests to resolve aliases on other servers; they should do this using
the federation API if necessary. the federation API if necessary.
Rooms store a *partial* list of room aliases via the ``m.room.aliases`` state Rooms do not store a list of all aliases present on a room, though members
event. This alias list is partial because it cannot guarantee that the alias of the room with relevant permissions may publish preferred aliases through
list is in any way accurate or up-to-date, as room aliases can point to the ``m.room.canonical_alias`` state event. The aliases in the state event
different room IDs over time. Crucially, the aliases in this event are should point to the room ID they are published within, however room aliases
**purely informational** and SHOULD NOT be treated as accurate. They SHOULD can and do drift to other room IDs over time. Clients SHOULD NOT treat the
be checked before they are used or shared with another user. If a room aliases as accurate. They SHOULD be checked before they are used or shared
appears to have a room alias of ``#alias:example.com``, this SHOULD be checked with another user. If a room appears to have a room alias of ``#alias:example.com``,
to make sure that the room's ID matches the ``room_id`` returned from the this SHOULD be checked to make sure that the room's ID matches the ``room_id``
request. returned from the request.
{{directory_cs_http_api}} {{directory_cs_http_api}}

@ -550,6 +550,7 @@ The available room versions are:
* `Version 3 <rooms/v3.html>`_ - **Stable**. Introduces events whose IDs are the event's hash. * `Version 3 <rooms/v3.html>`_ - **Stable**. Introduces events whose IDs are the event's hash.
* `Version 4 <rooms/v4.html>`_ - **Stable**. Builds on v3 by using URL-safe base64 for event IDs. * `Version 4 <rooms/v4.html>`_ - **Stable**. Builds on v3 by using URL-safe base64 for event IDs.
* `Version 5 <rooms/v5.html>`_ - **Stable**. Introduces enforcement of signing key validity periods. * `Version 5 <rooms/v5.html>`_ - **Stable**. Introduces enforcement of signing key validity periods.
* `Version 6 <rooms/v6.html>`_ - **Stable**. Alters several authorization rules for events.
Specification Versions Specification Versions
---------------------- ----------------------

@ -83,9 +83,23 @@ Base64`_. Example:
"JGLn/yafz74HB2AbPLYJWIVGnKAtqECOBf11yyXac2Y" "JGLn/yafz74HB2AbPLYJWIVGnKAtqECOBf11yyXac2Y"
The name ``signed_curve25519`` also corresponds to the Curve25519 algorithm, The name ``signed_curve25519`` also corresponds to the Curve25519 algorithm,
but keys using this algorithm are objects with the properties ``key`` (giving but a key using this algorithm is represented by an object with a the following
the Base64-encoded 32-byte Curve25519 public key), and ``signatures`` (giving a properties:
signature for the key object, as described in `Signing JSON`_). Example:
``KeyObject``
========== ================ =====================================================
Parameter Type Description
========== ================ =====================================================
key string **Required.** The unpadded Base64-encoded 32-byte
Curve25519 public key.
signatures Signatures **Required.** Signatures of the key object.
The signature is calculated using the process described
at `Signing JSON`_.
========== ================ =====================================================
Example:
.. code:: json .. code:: json
@ -513,7 +527,7 @@ received the other party's part. Thus an attacker essentially only has one attem
attack the Diffie-Hellman exchange, and hence we can verify fewer bits while still attack the Diffie-Hellman exchange, and hence we can verify fewer bits while still
achieving a high degree of security: if we verify n bits, then an attacker has a 1 in achieving a high degree of security: if we verify n bits, then an attacker has a 1 in
2\ :sup:`n` chance of success. For example, if we verify 40 bits, then an attacker has 2\ :sup:`n` chance of success. For example, if we verify 40 bits, then an attacker has
a 1 in 1,099,511,627,776 chance (or less than 1 in 1012 chance) of success. A failed a 1 in 1,099,511,627,776 chance (or less than 1 in 10\ :sup:`12` chance) of success. A failed
attack would result in a mismatched Short Authentication String, alerting users to the attack would result in a mismatched Short Authentication String, alerting users to the
attack. attack.
@ -565,15 +579,15 @@ The process between Alice and Bob verifying each other would be:
they match or not. they match or not.
#. Assuming they match, Alice and Bob's devices calculate the HMAC of their own device keys #. Assuming they match, Alice and Bob's devices calculate the HMAC of their own device keys
and a comma-separated sorted list of of the key IDs that they wish the other user and a comma-separated sorted list of of the key IDs that they wish the other user
to verify, using SHA-256 as the hash function. HMAC is defined in [RFC 2104](https://tools.ietf.org/html/rfc2104). to verify, using SHA-256 as the hash function. HMAC is defined in `RFC 2104 <https://tools.ietf.org/html/rfc2104>`_.
The key for the HMAC is different for each item and is calculated by generating The key for the HMAC is different for each item and is calculated by generating
32 bytes (256 bits) using `the key verification HKDF <#SAS-HKDF>`_. 32 bytes (256 bits) using `the key verification HKDF <#sas-hkdf>`_.
#. Alice's device sends Bob's device a ``m.key.verification.mac`` message containing the #. Alice's device sends Bob's device a ``m.key.verification.mac`` message containing the
MAC of Alice's device keys and the MAC of her key IDs to be verified. Bob's device does MAC of Alice's device keys and the MAC of her key IDs to be verified. Bob's device does
the same for Bob's device keys and key IDs concurrently with Alice. the same for Bob's device keys and key IDs concurrently with Alice.
#. When the other device receives the ``m.key.verification.mac`` message, the device #. When the other device receives the ``m.key.verification.mac`` message, the device
calculates the HMAC of its copies of the other device's keys given in the message, calculates the HMAC of its copies of the other device's keys given in the message,
as well as the HMAC of the comma-seperated, sorted, list of key IDs in the message. as well as the HMAC of the comma-separated, sorted, list of key IDs in the message.
The device compares these with the HMAC values given in the message, and if everything The device compares these with the HMAC values given in the message, and if everything
matches then the device keys are verified. matches then the device keys are verified.
@ -653,14 +667,14 @@ are used in addition to those already specified:
{{m_key_verification_mac_event}} {{m_key_verification_mac_event}}
.. _`SAS-HKDF`: .. _sas-hkdf:
HKDF calculation HKDF calculation
<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<
In all of the SAS methods, HKDF is as defined in [RFC 5869](https://tools.ietf.org/html/rfc5869) In all of the SAS methods, HKDF is as defined in `RFC 5869 <https://tools.ietf.org/html/rfc5869>`_
and uses the previously agreed-upon hash function for the hash function. The shared and uses the previously agreed-upon hash function for the hash function. The shared
secret is supplied as the input keying material. No salt is used, and the input secret is supplied as the input keying material. No salt is used, and the info
parameter is the concatenation of: parameter is the concatenation of:
* The string ``MATRIX_KEY_VERIFICATION_SAS``. * The string ``MATRIX_KEY_VERIFICATION_SAS``.
@ -677,7 +691,7 @@ parameter is the concatenation of:
For verification of each party's device keys, HKDF is as defined in RFC 5869 and For verification of each party's device keys, HKDF is as defined in RFC 5869 and
uses SHA-256 as the hash function. The shared secret is supplied as the input keying uses SHA-256 as the hash function. The shared secret is supplied as the input keying
material. No salt is used, and in the input parameter is the concatenation of: material. No salt is used, and in the info parameter is the concatenation of:
* The string ``MATRIX_KEY_VERIFICATION_MAC``. * The string ``MATRIX_KEY_VERIFICATION_MAC``.
* The Matrix ID of the user whose key is being MAC-ed. * The Matrix ID of the user whose key is being MAC-ed.
@ -691,7 +705,7 @@ material. No salt is used, and in the input parameter is the concatenation of:
SAS method: ``decimal`` SAS method: ``decimal``
<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<
Generate 5 bytes using `HKDF <#SAS-HKDF>`_ then take sequences of 13 bits to Generate 5 bytes using `HKDF <#sas-hkdf>`_ then take sequences of 13 bits to
convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive
each). Add 1000 to each calculated number. each). Add 1000 to each calculated number.
@ -708,7 +722,7 @@ such as dashes, or with the numbers on individual lines.
SAS method: ``emoji`` SAS method: ``emoji``
<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<
Generate 6 bytes using `HKDF <#SAS-HKDF>`_ then split the first 42 bits into Generate 6 bytes using `HKDF <#sas-hkdf>`_ then split the first 42 bits into
7 groups of 6 bits, similar to how one would base64 encode something. Convert 7 groups of 6 bits, similar to how one would base64 encode something. Convert
each group of 6 bits to a number and use the following table to get the corresponding each group of 6 bits to a number and use the following table to get the corresponding
emoji: emoji:
@ -767,6 +781,136 @@ previously-received ``request`` message with the same ``request_id`` and
A reasonable strategy is for a user's client to only send keys requested by the A reasonable strategy is for a user's client to only send keys requested by the
verified devices of the same user. verified devices of the same user.
Server-side key backups
~~~~~~~~~~~~~~~~~~~~~~~
Devices may upload encrypted copies of keys to the server. When a device tries
to read a message that it does not have keys for, it may request the key from
the server and decrypt it. Backups are per-user, and users may replace backups
with new backups.
In contrast with `Key requests`_, Server-side key backups do not require another
device to be online from which to request keys. However, as the session keys are
stored on the server encrypted, it requires users to enter a decryption key to
decrypt the session keys.
To create a backup, a client will call `POST
/_matrix/client/r0/room_keys/version`_ and define how the keys are to be
encrypted through the backup's ``auth_data``; other clients can discover the
backup by calling `GET /_matrix/client/r0/room_keys/version`_. Keys are
encrypted according to the backup's ``auth_data`` and added to the backup by
calling `PUT /_matrix/client/r0/room_keys/keys`_ or one of its variants, and
can be retrieved by calling `GET /_matrix/client/r0/room_keys/keys`_ or one of
its variants. Keys can only be written to the most recently created version of
the backup. Backups can also be deleted using `DELETE
/_matrix/client/r0/room_keys/version/{version}`_, or individual keys can be
deleted using `DELETE /_matrix/client/r0/room_keys/keys`_ or one of its
variants.
Clients must only store keys in backups after they have ensured that the
``auth_data`` is trusted, either by checking the signatures on it, or by
deriving the public key from a private key that it obtained from a trusted
source.
When a client uploads a key for a session that the server already has a key
for, the server will choose to either keep the existing key or replace it with
the new key based on the key metadata as follows:
- if the keys have different values for ``is_verified``, then it will keep the
key that has ``is_verified`` set to ``true``;
- if they have the same values for ``is_verified``, then it will keep the key
with a lower ``first_message_index``;
- and finally, is ``is_verified`` and ``first_message_index`` are equal, then
it will keep the key with a lower ``forwarded_count``.
Recovery key
<<<<<<<<<<<<
If the recovery key (the private half of the backup encryption key) is
presented to the user to save, it is presented as a string constructed as
follows:
1. The 256-bit curve25519 private key is prepended by the bytes ``0x8B`` and
``0x01``
2. All the bytes in the string above, including the two header bytes, are XORed
together to form a parity byte. This parity byte is appended to the byte
string.
3. The byte string is encoded using base58, using the same `mapping as is used
for Bitcoin addresses
<https://en.bitcoin.it/wiki/Base58Check_encoding#Base58_symbol_chart>`_,
that is, using the alphabet
``123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz``.
4. A space should be added after every 4th character.
When reading in a recovery key, clients must disregard whitespace, and perform
the reverse of steps 1 through 3.
Backup algorithm: ``m.megolm_backup.v1.curve25519-aes-sha2``
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
When a backup is created with the ``algorithm`` set to
``m.megolm_backup.v1.curve25519-aes-sha2``, the ``auth_data`` should have the
following format:
``AuthData``
.. table::
:widths: auto
========== =========== ======================================================
Parameter Type Description
========== =========== ======================================================
public_key string **Required.** The curve25519 public key used to encrypt
the backups, encoded in unpadded base64.
signatures Signatures Optional. Signatures of the ``auth_data``, as Signed
JSON
========== =========== ======================================================
The ``session_data`` field in the backups is constructed as follows:
1. Encode the session key to be backed up as a JSON object with the properties:
.. table::
:widths: auto
=============================== ======== =========================================
Parameter Type Description
=============================== ======== =========================================
algorithm string **Required.** The end-to-end message
encryption algorithm that the key is
for. Must be ``m.megolm.v1.aes-sha2``.
forwarding_curve25519_key_chain [string] **Required.** Chain of Curve25519 keys
through which this session was
forwarded, via
`m.forwarded_room_key`_ events.
sender_key string **Required.** Unpadded base64-encoded
device curve25519 key.
sender_claimed_keys {string: **Required.** A map from algorithm name
string} (``ed25519``) to the identity key
for the sending device.
session_key string **Required.** Unpadded base64-encoded
session key in `session-sharing format
<https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-sharing-format>`_.
=============================== ======== =========================================
2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral
key and the backup's public key to generate a shared secret. The public
half of the ephemeral key, encoded using unpadded base64, becomes the ``ephemeral``
property of the ``session_data``.
3. Using the shared secret, generate 80 bytes by performing an HKDF using
SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string
as the info. The first 32 bytes are used as the AES key, the next 32 bytes
are used as the MAC key, and the last 16 bytes are used as the AES
initialization vector.
4. Stringify the JSON object, and encrypt it using AES-CBC-256 with PKCS#7
padding. This encrypted data, encoded using unpadded base64, becomes the
``ciphertext`` property of the ``session_data``.
5. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256
using the MAC key generated above. The first 8 bytes of the resulting MAC
are base64-encoded, and become the ``mac`` property of the ``session_data``.
{{key_backup_cs_http_api}}
Key exports Key exports
~~~~~~~~~~~ ~~~~~~~~~~~
@ -840,6 +984,9 @@ described as follows:
session_key string Required. The key for the session. session_key string Required. The key for the session.
=============================== =========== ==================================== =============================== =========== ====================================
This is similar to the format before encryption used for the session keys in
`Server-side key backups`_ but adds the ``room_id`` and ``session_id`` fields.
Example: Example:
.. code:: json .. code:: json

@ -275,13 +275,9 @@ choose a name:
1. If the room has an `m.room.name`_ state event with a non-empty ``name`` 1. If the room has an `m.room.name`_ state event with a non-empty ``name``
field, use the name given by that field. field, use the name given by that field.
#. If the room has an `m.room.canonical_alias`_ state event with a non-empty #. If the room has an `m.room.canonical_alias`_ state event with a valid
``alias`` field, use the alias given by that field as the name. ``alias`` field, use the alias given by that field as the name. Note that
clients should avoid using ``alt_aliases`` when calculating the room name.
#. If neither of the above conditions are met, the client can optionally guess
an alias from the ``m.room.alias`` events in the room. This is a temporary
measure while clients do not promote canonical aliases as prominently. This
step may be removed in a future version of the specification.
#. If none of the above conditions are met, a name should be composed based #. If none of the above conditions are met, a name should be composed based
on the members of the room. Clients should consider `m.room.member`_ events on the members of the room. Clients should consider `m.room.member`_ events

@ -473,6 +473,9 @@ Definition (as a ``content`` rule):
{ {
"set_tweak": "sound", "set_tweak": "sound",
"value": "default" "value": "default"
},
{
"set_tweak": "highlight"
} }
] ]
} }

@ -47,7 +47,7 @@ The read markers API can additionally update the user's read receipt (``m.read``
location in the same operation as setting the fully read marker location. This is location in the same operation as setting the fully read marker location. This is
because read receipts and read markers are commonly updated at the same time, because read receipts and read markers are commonly updated at the same time,
and therefore the client might wish to save an extra HTTP call. Providing an and therefore the client might wish to save an extra HTTP call. Providing an
``m.read`` location performs the same task as a request to ``/receipts/m.read/$event:example.org``. ``m.read`` location performs the same task as a request to ``/receipt/m.read/$event:example.org``.
{{read_markers_cs_http_api}} {{read_markers_cs_http_api}}
@ -56,7 +56,7 @@ Server behaviour
The server MUST prevent clients from setting ``m.fully_read`` directly in The server MUST prevent clients from setting ``m.fully_read`` directly in
room account data. The server must additionally ensure that it treats the room account data. The server must additionally ensure that it treats the
presence of ``m.read`` in the ``/read_markers`` request the same as how it presence of ``m.read`` in the ``/read_markers`` request the same as how it
would for a request to ``/receipts/m.read/$event:example.org``. would for a request to ``/receipt/m.read/$event:example.org``.
Upon updating the ``m.fully_read`` event due to a request to ``/read_markers``, Upon updating the ``m.fully_read`` event due to a request to ``/read_markers``,
the server MUST send the updated account data event through to the client via the server MUST send the updated account data event through to the client via

@ -1,4 +1,4 @@
.. Copyright 2019 New Vector Ltd .. Copyright 2019-2020 The Matrix.org Foundation C.I.C.
.. ..
.. Licensed under the Apache License, Version 2.0 (the "License"); .. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License. .. you may not use this file except in compliance with the License.
@ -12,34 +12,98 @@
.. See the License for the specific language governing permissions and .. See the License for the specific language governing permissions and
.. limitations under the License. .. limitations under the License.
SSO client login SSO client login/authentication
================ ===============================
.. _module:sso_login: .. _module:sso_login:
Single Sign-On (SSO) is a generic term which refers to protocols which allow Single Sign-On (SSO) is a generic term which refers to protocols which allow
users to log into applications via a single web-based authentication portal. users to log into applications via a single web-based authentication portal.
Examples include "Central Authentication Service" (CAS) and SAML. Examples include OpenID Connect, "Central Authentication Service" (CAS) and SAML.
An overview of the process, as used in Matrix, is as follows: This module allows a Matrix homeserver to delegate user authentication to an
external authentication server supporting one of these protocols. In this
process, there are three systems involved:
1. The Matrix client instructs the user's browser to navigate to the * A Matrix client, using the APIs defined this specification, which is seeking
|/login/sso/redirect|_ endpoint on the user's homeserver. to authenticate a user to a Matrix homeserver.
* A Matrix homeserver, implementing the APIs defined in this specification, but
which is delegating user authentication to the authentication server.
* An "authentication server", which is responsible for authenticating the
user.
This specification is concerned only with communication between the Matrix
client and the homeserver, and is independent of the SSO protocol used to
communicate with the authentication server. Different Matrix homeserver
implementations might support different SSO protocols.
Clients and homeservers implementing the SSO flow will need to consider both login_
and `user-interactive authentication`_. The flow is
similar in both cases, but there are slight differences.
Typically, SSO systems require a single "callback" URI to be configured at the
authentication server. Once the user is authenticated, their browser is
redirected to that URI. It is up to the Matrix homeserver implementation to
implement a suitable endpoint. For example, for CAS authentication the
homeserver should provide a means for the administrator to configure where the
CAS server is and the REST endpoints which consume the ticket.
Client login via SSO
---------------------
An overview of the process is as follows:
0. The Matrix client calls |GET /login|_ to find the supported login
types, and the homeserver includes a flow with ``"type": "m.login.sso"`` in the
response.
1. To initiate the ``m.login.sso`` login type, the Matrix client instructs the
user's browser to navigate to the |/login/sso/redirect|_ endpoint on the
user's homeserver.
2. The homeserver responds with an HTTP redirect to the SSO user interface, 2. The homeserver responds with an HTTP redirect to the SSO user interface,
which the browser follows. which the browser follows.
3. The SSO system authenticates the user. 3. The authentication server and the homeserver interact to verify the user's
identity and other authentication information, potentially using a number of
redirects.
4. The SSO server and the homeserver interact to verify the user's identity 4. The browser is directed to the ``redirectUrl`` provided by the client with
and other authentication information, potentially using a number of redirects.
5. The browser is directed to the ``redirectUrl`` provided by the client with
a ``loginToken`` query parameter for the client to log in with. a ``loginToken`` query parameter for the client to log in with.
5. The client exchanges the login token for an access token by calling the
|/login|_ endpoint with a ``type`` of ``m.login.token``.
For native applications, typically steps 1 to 4 are carried out by opening an
embedded web view.
These steps are illustrated as follows::
Matrix Client Matrix Homeserver Auth Server
| | |
|-------------(0) GET /login----------->| |
|<-------------login types--------------| |
| | |
| Webview | |
| | | |
|----->| | |
| |--(1) GET /login/sso/redirect-->| |
| |<---------(2) 302---------------| |
| | | |
| |<========(3) Authentication process================>|
| | | |
| |<--(4) redirect to redirectUrl--| |
|<-----| | |
| | |
|---(5) POST /login with login token--->| |
|<-------------access token-------------| |
.. Note:: .. Note::
In the older `r0.4.0 version <https://matrix.org/docs/spec/client_server/r0.4.0.html#cas-based-client-login>`_ In the older `r0.4.0 version <https://matrix.org/docs/spec/client_server/r0.4.0.html#cas-based-client-login>`_
of this specification it was possible to authenticate via CAS when the server of this specification it was possible to authenticate via CAS when the homeserver
provides a ``m.login.cas`` login flow. This specification deprecates the use provides a ``m.login.cas`` login flow. This specification deprecates the use
of ``m.login.cas`` to instead prefer ``m.login.sso``, which is the same process of ``m.login.cas`` to instead prefer ``m.login.sso``, which is the same process
with the only change being which redirect endpoint to use: for ``m.login.cas``, use with the only change being which redirect endpoint to use: for ``m.login.cas``, use
@ -47,40 +111,65 @@ An overview of the process, as used in Matrix, is as follows:
The endpoints are otherwise the same. The endpoints are otherwise the same.
Client behaviour Client behaviour
---------------- ~~~~~~~~~~~~~~~~
The client starts the process by instructing the browser to navigate to The client starts the process by instructing the browser to navigate to
|/login/sso/redirect|_ with an appropriate ``redirectUrl``. Once authentication |/login/sso/redirect|_ with an appropriate ``redirectUrl``. Once authentication
is successful, the browser will be redirected to that ``redirectUrl``. is successful, the browser will be redirected to that ``redirectUrl``.
.. TODO-spec {{sso_login_redirect_cs_http_api}}
Should we recommend some sort of CSRF protection here (specifically, we Security considerations
should guard against people accidentally logging in by sending them a link +++++++++++++++++++++++
to ``/login/sso/redirect``.
Maybe we should recommend that the ``redirectUrl`` should contain a CSRF 1. CSRF attacks via manipulation of parameters on the ``redirectUrl``
token which the client should then check before sending the login token to
``/login``?
{{sso_login_redirect_cs_http_api}} Clients should validate any requests to the ``redirectUrl``. In particular, it
may be possible for attackers to falsify any query parameters, leading to
cross-site request forgery (CSRF) attacks.
For example, consider a web-based client at ``https://client.example.com``,
which wants to initiate SSO login on the homeserver at ``server.example.org``.
It does this by storing the homeserver name in a query parameter for the
``redirectUrl``: it redirects to
``https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=server.example.org``.
An attacker could trick a victim into following a link to
``https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=evil.com``,
which would result in the client sending a login token for the victim's
account to the attacker-controlled site ``evil.com``.
To guard against this, clients MUST NOT store state (such as the address of
the homeserver being logged into) anywhere it can be modified by external
processes.
Instead, the state could be stored in `localStorage
<https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage>`_ or
in a cookie.
2. For added security, clients SHOULD include a unique identifier in the
``redirectUrl`` and reject any callbacks that do not contain a recognised
identifier, to guard against unsolicited login attempts and replay attacks.
Server behaviour Server behaviour
---------------- ~~~~~~~~~~~~~~~~
Redirecting to the Authentication server
++++++++++++++++++++++++++++++++++++++++
The URI for the SSO system to be used should be configured on the server by the The server should handle
server administrator. The server is expected to set up any endpoints required to ``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`` as follows:
interact with that SSO system. For example, for CAS authentication the homeserver
should provide a means for the administrator to configure where the CAS server is
and the REST endpoints which consume the ticket. A good reference for how CAS could
be implemented is available in the older `r0.4.0 version <https://matrix.org/docs/spec/client_server/r0.4.0.html#cas-based-client-login>`_
of this specification.
Handling the redirect endpoint #. It should build a suitable request for the SSO system.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When responding to the ``/login/sso/redirect`` endpoint, the server must #. It should store enough state that the flow can be securely resumed after the
generate a URI for the SSO login page with any appropriate parameters. SSO process completes. One way to do this is by storing a cookie which is
stored in the user's browser, by adding a ``Set-Cookie`` header to the response.
#. It should redirect the user's browser to the SSO login page with the
appropriate parameters.
See also the "Security considerations" below.
.. TODO-spec: .. TODO-spec:
@ -89,24 +178,169 @@ generate a URI for the SSO login page with any appropriate parameters.
endpoint, and to give more meaningful errors in the case of endpoint, and to give more meaningful errors in the case of
faulty/poorly-configured clients. faulty/poorly-configured clients.
Handling the authentication endpoint Handling the callback from the Authentication server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++++++++++++++++++++++++++++++
Note that there will normally be a single callback URI which is used for both login
and user-interactive authentication: it is up to the homeserver implementation
to distinguish which is taking place.
The homeserver should validate the response from the SSO system: this may
require additional calls to the authentication server, and/or may require
checking a signature on the response.
The homeserver then proceeds as follows:
#. The homeserver MUST map the user details received from the authentication
server to a valid `Matrix user identifier <../appendices.html#user-identifiers>`_.
The guidance in `Mapping from other character sets
<../appendices.html#mapping-from-other-character-sets>`_ may be useful.
#. If the generated user identifier represents a new user, it should be
registered as a new user.
#. The homeserver should generate a short-term login token. This is an opaque
token, suitable for use with the ``m.login.token`` type of the |/login|_
API. The lifetime of this token SHOULD be limited to around five
seconds.
#. The homeserver adds a query parameter of ``loginToken``, with the value of
the generated login token, to the ``redirectUrl`` given in the
``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect``
request. (Note: ``redirectURL`` may or may not include existing query
parameters. If it already includes one or more ``loginToken`` parameters,
they should be removed before adding the new one.)
#. The homeserver redirects the user's browser to the URI thus built.
Security considerations
~~~~~~~~~~~~~~~~~~~~~~~
1. Homeservers should ensure that login tokens are not sent to malicious
clients.
For example, consider a homeserver at ``server.example.org``. An attacker tricks
a victim into following a link to
``https://server.example.org/login/sso/redirect?redirectUrl=https://evil.com``,
resulting in a login token being sent to the attacker-controlled site
``evil.com``. This is a form of cross-site request forgery (CSRF).
To mitigate this, Homeservers SHOULD confirm with the user that they are
happy to grant access to their matrix account to the site named in the
``redirectUrl``. This can be done either *before* redirecting to the SSO
login page when handling the
``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`` endpoint, or
*after* login when handling the callback from the authentication server. (If
the check is performed before redirecting, it is particularly important that
the homeserver guards against unsolicited authentication attempts as below).
It may be appropriate to whitelist a set of known-trusted client URLs in
this process. In particular, the homeserver's own `login fallback`_
implementation could be excluded.
2. For added security, homeservers SHOULD guard against unsolicited
authentication attempts by tracking pending requests. One way to do this is
to set a cookie when handling
``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect``, which is
checked and cleared when handling the callback from the authentication
server.
SSO during User-Interactive Authentication
------------------------------------------
`User-interactive authentication`_ is used by client-server
endpoints which require additional confirmation of the user's identity (beyond
holding an access token). Typically this means that the user must re-enter
their password, but for homeservers which delegate to an SSO server, this means
redirecting to the authentication server during user-interactive auth.
The implemementation of this is based on the `Fallback`_ mechanism for
user-interactive auth.
Client behaviour
----------------
Clients do not need to take any particular additional steps beyond ensuring
that the fallback mechanism has been implemented, and treating the
``m.login.sso`` authentication type the same as any other unknown type
(i.e. they should open a browser window for
``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web?session=<session_id>``.
Once the flow has completed, the client retries the request with the session
only.)
Server behaviour
----------------
Redirecting to the Authentication server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The server should handle
``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web`` in
much the same way as
``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect``, which is to say:
#. It should build a suitable request for the SSO system.
#. It should store enough state that the flow can be securely resumed after the
SSO process completes. One way to do this is by storing a cookie which is
stored in the user's browser, by adding a ``Set-Cookie`` header to the response.
#. It should redirect the user's browser to the SSO login page with the
appropriate parameters.
See also the "Security considerations" below.
Handling the callback from the Authentication server
++++++++++++++++++++++++++++++++++++++++++++++++++++
Note that there will normally be a single callback URI which is used for both login
and user-interactive authentication: it is up to the homeserver implementation
to distinguish which is taking place.
The homeserver should validate the response from the SSO system: this may
require additional calls to the authentication server, and/or may require
checking a signature on the response.
The homeserver then returns the `user-interactive authentication fallback
completion`_ page to the user's browser.
Security considerations
+++++++++++++++++++++++
1. Confirming the operation
The homeserver SHOULD confirm that the user is happy for the operation to go
ahead. The goal of the user-interactive authentication operation is to guard
against a compromised ``access_token`` being used to take over the user's
account. Simply redirecting the user to the SSO system is insufficient,
since they may not realise what is being asked of them, or the SSO system
may even confirm the authentication automatically.
For example, the homeserver might serve a page with words to the effect of:
A client is trying to remove a device from your account. To confirm this
action, re-authenticate with single sign-on. If you did not expect this, your
account may be compromised!
Once the homeserver has verified the user's identity with the SSO system, it This confirmation could take place before redirecting to the SSO
MUST map the user ID to a valid `Matrix user identifier <../index.html#user-identifiers>`_. authentication page (when handling the
The guidance in `Mapping from other character sets ``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web``
<../index.html#mapping-from-other-character-sets>`_ may be useful. endpoint), or *after* authentication when handling the callback from the
authentication server. (If the check is performed before redirecting, it is
particularly important that the homeserver guards against unsolicited
authentication attempts as below).
If the generated user identifier represents a new user, it should be registered 2. For added security, homeservers SHOULD guard against unsolicited
as a new user. authentication attempts by tracking pending requests. One way to do this is
to set a cookie when handling
``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web``,
which is checked and cleared when handling the callback from the
authentication server.
Finally, the server should generate a short-term login token. The generated
token should be a macaroon, suitable for use with the ``m.login.token`` type of
the |/login|_ API, and `token-based interactive login <#token-based>`_. The
lifetime of this token SHOULD be limited to around five seconds. This token is
given to the client via the ``loginToken`` query parameter previously mentioned.
.. |GET /login| replace:: ``GET /login``
.. _GET /login: #get-matrix-client-%CLIENT_MAJOR_VERSION%-login
.. |/login| replace:: ``/login`` .. |/login| replace:: ``/login``
.. _/login: #post-matrix-client-%CLIENT_MAJOR_VERSION%-login .. _/login: #post-matrix-client-%CLIENT_MAJOR_VERSION%-login
.. |/login/sso/redirect| replace:: ``/login/sso/redirect`` .. |/login/sso/redirect| replace:: ``/login/sso/redirect``

@ -55,7 +55,7 @@ The tag namespace is defined as follows:
display name directly). These non-namespaced tags are supported for historical reasons. New tags should use display name directly). These non-namespaced tags are supported for historical reasons. New tags should use
one of the defined namespaces above. one of the defined namespaces above.
Two special names are listed in the specification: Several special names are listed in the specification:
The following tags are defined in the ``m.*`` namespace: The following tags are defined in the ``m.*`` namespace:
* ``m.favourite``: The user's favourite rooms. These should be shown with higher precedence than other rooms. * ``m.favourite``: The user's favourite rooms. These should be shown with higher precedence than other rooms.

@ -230,7 +230,8 @@ follows:
comments and in relevant rooms on Matrix. Discussion outside of GitHub should comments and in relevant rooms on Matrix. Discussion outside of GitHub should
be summarised in a comment on the PR. be summarised in a comment on the PR.
- When a member of the Spec Core Team believes that no new discussion points are - When a member of the Spec Core Team believes that no new discussion points are
being made, they will propose a motion for a final comment period (FCP), being made, and the proposal has suitable evidence of working (see `implementing a
proposal`_ below), they will propose a motion for a final comment period (FCP),
along with a *disposition* of either merge, close or postpone. This FCP is along with a *disposition* of either merge, close or postpone. This FCP is
provided to allow a short period of time for any invested party to provide a provided to allow a short period of time for any invested party to provide a
final objection before a major decision is made. If sufficient reasoning is final objection before a major decision is made. If sufficient reasoning is
@ -371,6 +372,100 @@ though that can always change as priorities evolve. We still encourage that MSCs
opened, even if not the focus for the time being, as they can still make progress and opened, even if not the focus for the time being, as they can still make progress and
even be merged without the Spec Core Team focusing on them specifically. even be merged without the Spec Core Team focusing on them specifically.
Implementing a proposal
-----------------------
As part of the proposal process the spec core team will require evidence of the MSC
working in order for it to move into FCP. This can usually be a branch/pull request
to whichever implementation of choice that proves the MSC works in practice, though
in some cases the MSC itself will be small enough to be considered proven. Where it's
unclear if a MSC will require an implementation proof, ask in `#matrix-spec:matrix.org
<https://matrix.to/#/#matrix-spec:matrix.org>`_.
Early release of a MSC/idea
~~~~~~~~~~~~~~~~~~~~~~~~~~~
To help facilitate early releases of software dependent on a spec release, implementations
are required to use the following process to ensure that the official Matrix namespace
is not cluttered with development or testing data.
.. Note::
Unreleased implementations (including proofs-of-concept demonstrating that a
particular MSC works) do not have to follow this process.
1. Have an idea for a feature.
2. Implement the feature using unstable endpoints, vendor prefixes, and unstable
feature flags as appropriate.
* When using unstable endpoints, they MUST include a vendor prefix. For example:
``/_matrix/client/unstable/com.example/login``. Vendor prefixes throughout Matrix
always use the Java package naming convention. The MSC for the feature should
identify which preferred vendor prefix is to be used by early adopters.
* Note that unstable namespaces do not automatically inherit endpoints from stable
namespaces: for example, the fact that ``/_matrix/client/r0/sync`` exists does
not imply that ``/_matrix/client/unstable/com.example/sync`` exists.
* If the client needs to be sure the server supports the feature, an unstable
feature flag that MUST be vendor prefixed is to be used. This kind of flag shows
up in the ``unstable_features`` section of ``/versions`` as, for example,
``com.example.new_login``. The MSC for the feature should identify which preferred
feature flag is to be used by early adopters.
* When using this approach correctly, the implementation can ship/release the
feature at any time, so long as the implementation is able to accept the technical
debt that results from needing to provide adequate backwards and forwards
compatibility. The implementation MUST support the flag (and server-side implementation) disappearing and be
generally safe for users. Note that implementations early in the MSC review
process may also be required to provide backwards compatibility with earlier
editions of the proposal.
* If the implementation cannot support the technical debt (or if it's impossible
to provide forwards/backwards compatibility - e.g. a user authentication change
which can't be safely rolled back), the implementation should not attempt to
implement the feature and should instead wait for a spec release.
* If at any point after early release, the idea changes in a backwards-incompatible way, the feature flag should also change so that
implementations can adapt as needed.
3. In parallel, or ahead of implementation, open an MSC and solicit review per above.
4. Before FCP can be called, the Spec Core Team will require evidence of the MSC
working as proposed. A typical example of this is an implementation of the MSC,
though the implementation does not need to be shipped anywhere and can therefore
avoid the forwards/backwards compatibility concerns mentioned here.
5. The FCP process is completed, and assuming nothing is flagged the MSC lands.
6. A spec PR is written to incorporate the changes into Matrix.
7. A spec release happens.
8. Implementations switch to using stable prefixes (e.g.: ``/r0``) if the server
supports the specification version released. If the server doesn't advertise the
specification version, but does have the feature flag, unstable prefixes should
still be used.
9. A transition period of about 2 months starts immediately after the spec release,
before implementations start to encourage other implementations to switch
to stable endpoints. For example, a server implementation should start asking
client implementations to support the stable endpoints 2 months after the spec
release, if they haven't already. The same applies in the reverse: if clients
cannot switch to stable prefixes because server implementations haven't started
supporting the new spec release, some noise should be raised in the general direction
of the implementation.
.. Note::
MSCs MUST still describe what the stable endpoints/feature looks like with a note
towards the bottom for what the unstable feature flag/prefixes are. For example,
a MSC would propose `/_matrix/client/r0/new/endpoint`, not `/_matrix/client/unstable/
com.example/new/endpoint`.
In summary:
* Implementations MUST NOT use stable endpoints before the MSC is in the spec. This
includes NOT using stable endpoints in the period between completion of FCP and release of the spec.
passed.
* Implementations are able to ship features that are exposed to users by default before
an MSC has been merged to the spec, provided they follow the process above.
* Implementations SHOULD be wary of the technical debt they are incurring by moving faster
than the spec.
* The vendor prefix is chosen by the developer of the feature, using the Java package
naming convention. The foundation's preferred vendor prefix is `org.matrix`.
* The vendor prefixes, unstable feature flags, and unstable endpoints should be included
in the MSC, though the MSC MUST be written in a way that proposes new stable endpoints.
Typically this is solved by a small table at the bottom mapping the various values
from stable to unstable.
Proposal Tracking Proposal Tracking
----------------- -----------------

@ -1,4 +1,5 @@
.. Copyright 2017,2019 New Vector Ltd .. Copyright 2017,2019 New Vector Ltd
.. Copyright 2020 The Matrix.org Foundation C.I.C.
.. ..
.. Licensed under the Apache License, Version 2.0 (the "License"); .. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License. .. you may not use this file except in compliance with the License.
@ -21,13 +22,57 @@ blocks for other room versions.
.. contents:: Table of Contents .. contents:: Table of Contents
.. sectnum:: .. sectnum::
Client considerations
---------------------
Clients may need to consider some algorithms performed by the server for their own
implementation.
Redactions
~~~~~~~~~~
Upon receipt of a redaction event, the server must 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 must 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``.
Server implementation components Server implementation components
-------------------------------- --------------------------------
.. WARNING:: .. WARNING::
The information contained in this section is strictly for server implementors. The information contained in this section is strictly for server implementors.
Applications which use the Client-Server API are generally unaffected by the Applications which use the Client-Server API are generally unaffected by the
details contained here, and can safely ignore their presence. intricacies contained here. The section above regarding client considerations
is the resource that Client-Server API use cases should reference.
The algorithms defined here should only apply to version 1 rooms. Other algorithms The algorithms defined here should only apply to version 1 rooms. Other algorithms
@ -112,7 +157,7 @@ The types of state events that affect authorization are:
.. NOTE:: .. NOTE::
Power levels are inferred from defaults when not explicitly supplied. Power levels are inferred from defaults when not explicitly supplied.
For example, mentions of the ``sender``'s power level can also refer For example, mentions of the ``sender``'s power level can also refer
to the default power level for users in the room. to the default power level for users in the room.
The rules are as follows: The rules are as follows:
@ -250,7 +295,7 @@ The rules are as follows:
#. If there is no previous ``m.room.power_levels`` event in the room, allow. #. If there is no previous ``m.room.power_levels`` event in the room, allow.
#. For the keys ``users_default``, ``events_default``, #. For the keys ``users_default``, ``events_default``,
``state_default``, ``ban``, ``redact``, ``kick``, ``invite`` check if they ``state_default``, ``ban``, ``redact``, ``kick``, ``invite`` check if they
were added, changed or removed. For each found alteration: were added, changed or removed. For each found alteration:
i. If the current value is higher than the ``sender``'s current power level, i. If the current value is higher than the ``sender``'s current power level,
@ -258,13 +303,13 @@ The rules are as follows:
#. If the new value is higher than the ``sender``'s current power level, #. If the new value is higher than the ``sender``'s current power level,
reject. reject.
#. For each entry being added, changed or removed in both the ``events`` and #. For each entry being added, changed or removed in both the ``events`` and
``users`` keys: ``users`` keys:
i. If the current value is higher than the ``sender``'s current power level, i. If the current value is higher than the ``sender``'s current power level,
reject. reject.
#. If the new value is higher than the ``sender``'s current power level, #. If the new value is higher than the ``sender``'s current power level,
reject. reject.
@ -307,6 +352,12 @@ Events in version 1 rooms have the following structure:
{{definition_ss_pdu}} {{definition_ss_pdu}}
Canonical JSON
~~~~~~~~~~~~~~
Servers MUST NOT strictly enforce the JSON format specified in the
`appendices <../appendices.html#canonical-json>`_ for the reasons described there.
.. _`auth events selection`: ../server_server/%SERVER_RELEASE_LABEL%.html#auth-events-selection .. _`auth events selection`: ../server_server/%SERVER_RELEASE_LABEL%.html#auth-events-selection
.. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events .. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events

@ -0,0 +1,100 @@
.. Copyright 2020 The Matrix.org Foundation C.I.C.
..
.. 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.
Room Version 6
==============
This room version builds on `version 5 <v5.html>`_ while changing various
authorization rules performed on events.
.. contents:: Table of Contents
.. sectnum::
Client considerations
---------------------
The redaction algorithm has changed from `room version 1 <v1.html>`_ to remove
all rules against events of type ``m.room.aliases``. Room versions 2, 3, 4, and
5 all use v1's redaction algorithm. The algorithm is otherwise unchanged.
Server implementation components
--------------------------------
.. WARNING::
The information contained in this section is strictly for server implementors.
Applications which use the Client-Server API are generally unaffected by the
intricacies contained here. The section above regarding client considerations
is the resource that Client-Server API use cases should reference.
Room version 6 makes the following alterations to algorithms described in `room version 5 <v5.html>`_.
Redactions
~~~~~~~~~~
As mentioned in the client considerations portion of this specification, all
special meaning has been removed for events of type ``m.room.aliases``. The
algorithm is otherwise unchanged.
Authorization rules for events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Like redactions, all rules relating specifically to events of type ``m.room.aliases``
are removed. They must still pass authorization checks relating to state events.
Additionally, the authorization rules for events of type ``m.room.power_levels``
now include the content key ``notifications``. This new rule takes the place of the
rule which checks the ``events`` and ``users`` keys.
For completeness, the changes to the auth rules can be represented as follows:
.. code:: diff
...
-If type is `m.room.aliases`:
-
- a. If event has no `state_key`, reject.
- b. If sender's domain doesn't matches `state_key`, reject.
- c. Otherwise, allow.
...
If type is `m.room.power_levels`:
...
- * For each entry being added, changed or removed in both the `events` and `users` keys:
+ * For each entry being added, changed or removed in the `events`, `users`, and `notifications` keys:
i. If the current value is higher than the `sender`'s current power level, reject.
ii. If the new value is higher than the `sender`'s current power level, reject.
...
The remaining rules are the same as in `room version 3 <v3.html#authorization-rules-for-events>`_
(the last inherited room version to specify the authorization rules).
Canonical JSON
~~~~~~~~~~~~~~
Servers MUST strictly enforce the JSON format specified in the
`appendices <../appendices.html#canonical-json>`_. This translates to a 400 ``M_BAD_JSON`` error
on most endpoints, or discarding of events over federation. For example, the Federation API's
``/send`` endpoint would discard the event whereas the Client Server API's ``/send/{eventType}``
endpoint would return a ``M_BAD_JSON`` error.

@ -80,6 +80,8 @@ Other versions of this specification
The following other versions are also available, in reverse chronological order: The following other versions are also available, in reverse chronological order:
- `HEAD <https://matrix.org/docs/spec/server_server/unstable.html>`_: Includes all changes since the latest versioned release. - `HEAD <https://matrix.org/docs/spec/server_server/unstable.html>`_: Includes all changes since the latest versioned release.
- `r0.1.4 <https://matrix.org/docs/spec/server_server/r0.1.4.html>`_
- `r0.1.3 <https://matrix.org/docs/spec/server_server/r0.1.3.html>`_
- `r0.1.2 <https://matrix.org/docs/spec/server_server/r0.1.2.html>`_ - `r0.1.2 <https://matrix.org/docs/spec/server_server/r0.1.2.html>`_
- `r0.1.1 <https://matrix.org/docs/spec/server_server/r0.1.1.html>`_ - `r0.1.1 <https://matrix.org/docs/spec/server_server/r0.1.1.html>`_
- `r0.1.0 <https://matrix.org/docs/spec/server_server/r0.1.0.html>`_ - `r0.1.0 <https://matrix.org/docs/spec/server_server/r0.1.0.html>`_
@ -291,6 +293,11 @@ Step 1 sign JSON:
} }
} }
The server names in the JSON above are the server names for each homeserver involved. Delegation from
the `server name resolution section <#resolving-server-names>`_ above do not affect
these - the server names from before delegation would take place are used. This
same condition applies throughout the request signing process.
Step 2 add Authorization header: Step 2 add Authorization header:
.. code:: .. code::
@ -780,7 +787,9 @@ and responds to the joining server with the full set of state for the
newly-joined room. The resident server must also send the event to other servers newly-joined room. The resident server must also send the event to other servers
participating in the room. participating in the room.
{{joins_ss_http_api}} {{joins_v1_ss_http_api}}
{{joins_v2_ss_http_api}}
.. TODO-spec .. TODO-spec
- (paul) I don't really understand why the full auth_chain events are given - (paul) I don't really understand why the full auth_chain events are given
@ -817,7 +826,9 @@ signs the event and replaces the ``event_id`` with it's own. This is then sent t
the resident server via ``/send_leave``. The resident server will then send the the resident server via ``/send_leave``. The resident server will then send the
event to other servers in the room. event to other servers in the room.
{{leaving_ss_http_api}} {{leaving_v1_ss_http_api}}
{{leaving_v2_ss_http_api}}
Third-party invites Third-party invites
------------------- -------------------
@ -1071,8 +1082,11 @@ The following endpoint prefixes MUST be protected:
* ``/_matrix/federation/v1/make_join`` * ``/_matrix/federation/v1/make_join``
* ``/_matrix/federation/v1/make_leave`` * ``/_matrix/federation/v1/make_leave``
* ``/_matrix/federation/v1/send_join`` * ``/_matrix/federation/v1/send_join``
* ``/_matrix/federation/v2/send_join``
* ``/_matrix/federation/v1/send_leave`` * ``/_matrix/federation/v1/send_leave``
* ``/_matrix/federation/v2/send_leave``
* ``/_matrix/federation/v1/invite`` * ``/_matrix/federation/v1/invite``
* ``/_matrix/federation/v2/invite``
* ``/_matrix/federation/v1/state`` * ``/_matrix/federation/v1/state``
* ``/_matrix/federation/v1/state_ids`` * ``/_matrix/federation/v1/state_ids``
* ``/_matrix/federation/v1/backfill`` * ``/_matrix/federation/v1/backfill``
@ -1250,3 +1264,4 @@ issue.
.. _`Device Management module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#device-management .. _`Device Management module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#device-management
.. _`End-to-End Encryption module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#end-to-end-encryption .. _`End-to-End Encryption module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#end-to-end-encryption
.. _`room version specification`: ../index.html#room-versions .. _`room version specification`: ../index.html#room-versions
.. _`Client-Server Key Algorithms`: ../client_server/%CLIENT_RELEASE_LABEL%.html#key-algorithms

@ -45,6 +45,10 @@ targets:
files: files:
- rooms/v5.rst - rooms/v5.rst
version_label: v5 version_label: v5
rooms@v6: # this is translated to be rooms/v6.html
files:
- rooms/v6.rst
version_label: v6
appendices: appendices:
files: files:
- appendices.rst - appendices.rst

Loading…
Cancel
Save