Add syntax highlighting
This commit is contained in:
parent
6c6bd57ebf
commit
ab64bda76d
11 changed files with 1100 additions and 905 deletions
|
@ -87,9 +87,10 @@ Note
|
||||||
|
|
||||||
Float values are not permitted by this encoding.
|
Float values are not permitted by this encoding.
|
||||||
|
|
||||||
import json
|
```py
|
||||||
|
import json
|
||||||
|
|
||||||
def canonical_json(value):
|
def canonical_json(value):
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
value,
|
value,
|
||||||
# Encode code-points outside of ASCII as UTF-8 rather than \u escapes
|
# Encode code-points outside of ASCII as UTF-8 rather than \u escapes
|
||||||
|
@ -100,6 +101,7 @@ Float values are not permitted by this encoding.
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
# Encode the resulting Unicode as UTF-8 bytes.
|
# Encode the resulting Unicode as UTF-8 bytes.
|
||||||
).encode("UTF-8")
|
).encode("UTF-8")
|
||||||
|
```
|
||||||
|
|
||||||
#### Grammar
|
#### Grammar
|
||||||
|
|
||||||
|
@ -138,45 +140,62 @@ transformation code.
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{}
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{}
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"one": 1,
|
"one": 1,
|
||||||
"two": "Two"
|
"two": "Two"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"one":1,"two":"Two"}
|
```json
|
||||||
|
{"one":1,"two":"Two"}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"b": "2",
|
"b": "2",
|
||||||
"a": "1"
|
"a": "1"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"a":"1","b":"2"}
|
```json
|
||||||
|
{"a":"1","b":"2"}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{"b":"2","a":"1"}
|
```json
|
||||||
|
{"b":"2","a":"1"}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"a":"1","b":"2"}
|
```json
|
||||||
|
{"a":"1","b":"2"}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"auth": {
|
"auth": {
|
||||||
"success": true,
|
"success": true,
|
||||||
"mxid": "@john.doe:example.com",
|
"mxid": "@john.doe:example.com",
|
||||||
|
@ -194,52 +213,71 @@ Given the following JSON object:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}
|
```json
|
||||||
|
{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"a": "日本語"
|
"a": "日本語"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"a":"日本語"}
|
```json
|
||||||
|
{"a":"日本語"}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"本": 2,
|
"本": 2,
|
||||||
"日": 1
|
"日": 1
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"日":1,"本":2}
|
```json
|
||||||
|
{"日":1,"本":2}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"a": "\u65E5"
|
"a": "\u65E5"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"a":"日"}
|
```json
|
||||||
|
{"a":"日"}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object:
|
Given the following JSON object:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"a": null
|
"a": null
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The following canonical JSON should be produced:
|
The following canonical JSON should be produced:
|
||||||
|
|
||||||
{"a":null}
|
```json
|
||||||
|
{"a":null}
|
||||||
|
```
|
||||||
|
|
||||||
### Signing Details
|
### Signing Details
|
||||||
|
|
||||||
|
@ -263,7 +301,8 @@ The `unsigned` object and the `signatures` object are not covered by the
|
||||||
signature. Therefore intermediate entities can add unsigned data such as
|
signature. Therefore intermediate entities can add unsigned data such as
|
||||||
timestamps and additional signatures.
|
timestamps and additional signatures.
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"name": "example.org",
|
"name": "example.org",
|
||||||
"signing_keys": {
|
"signing_keys": {
|
||||||
"ed25519:1": "XSl0kuyvrXNj6A+7/tkrB9sxSbRi08Of5uRhxOqZtEQ"
|
"ed25519:1": "XSl0kuyvrXNj6A+7/tkrB9sxSbRi08Of5uRhxOqZtEQ"
|
||||||
|
@ -276,9 +315,11 @@ timestamps and additional signatures.
|
||||||
"ed25519:1": "s76RUgajp8w172am0zQb/iPTHsRnb4SkrzGoeCOSFfcBY2V/1c8QfrmdXHpvnc2jK5BD1WiJIxiMW95fMjK7Bw"
|
"ed25519:1": "s76RUgajp8w172am0zQb/iPTHsRnb4SkrzGoeCOSFfcBY2V/1c8QfrmdXHpvnc2jK5BD1WiJIxiMW95fMjK7Bw"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
def sign_json(json_object, signing_key, signing_name):
|
```py
|
||||||
|
def sign_json(json_object, signing_key, signing_name):
|
||||||
signatures = json_object.pop("signatures", {})
|
signatures = json_object.pop("signatures", {})
|
||||||
unsigned = json_object.pop("unsigned", None)
|
unsigned = json_object.pop("unsigned", None)
|
||||||
|
|
||||||
|
@ -293,6 +334,7 @@ timestamps and additional signatures.
|
||||||
json_object["unsigned"] = unsigned
|
json_object["unsigned"] = unsigned
|
||||||
|
|
||||||
return json_object
|
return json_object
|
||||||
|
```
|
||||||
|
|
||||||
### Checking for a Signature
|
### Checking for a Signature
|
||||||
|
|
||||||
|
@ -872,28 +914,35 @@ In each case, the server name and key ID are as follows:
|
||||||
|
|
||||||
Given an empty JSON object:
|
Given an empty JSON object:
|
||||||
|
|
||||||
{}
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
||||||
The JSON signing algorithm should emit the following signed data:
|
The JSON signing algorithm should emit the following signed data:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"domain": {
|
"domain": {
|
||||||
"ed25519:1": "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"
|
"ed25519:1": "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following JSON object with data values in it:
|
Given the following JSON object with data values in it:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"one": 1,
|
"one": 1,
|
||||||
"two": "Two"
|
"two": "Two"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The JSON signing algorithm should emit the following signed JSON:
|
The JSON signing algorithm should emit the following signed JSON:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"one": 1,
|
"one": 1,
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"domain": {
|
"domain": {
|
||||||
|
@ -901,13 +950,15 @@ The JSON signing algorithm should emit the following signed JSON:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"two": "Two"
|
"two": "Two"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Event Signing
|
### Event Signing
|
||||||
|
|
||||||
Given the following minimally-sized event:
|
Given the following minimally-sized event:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"room_id": "!x:domain",
|
"room_id": "!x:domain",
|
||||||
"sender": "@a:domain",
|
"sender": "@a:domain",
|
||||||
"origin": "domain",
|
"origin": "domain",
|
||||||
|
@ -922,11 +973,13 @@ Given the following minimally-sized event:
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age_ts": 1000000
|
"age_ts": 1000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The event signing algorithm should emit the following signed event:
|
The event signing algorithm should emit the following signed event:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"auth_events": [],
|
"auth_events": [],
|
||||||
"content": {},
|
"content": {},
|
||||||
"depth": 3,
|
"depth": 3,
|
||||||
|
@ -947,11 +1000,13 @@ The event signing algorithm should emit the following signed event:
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age_ts": 1000000
|
"age_ts": 1000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Given the following event containing redactable content:
|
Given the following event containing redactable content:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"content": {
|
"content": {
|
||||||
"body": "Here is the message content"
|
"body": "Here is the message content"
|
||||||
},
|
},
|
||||||
|
@ -965,11 +1020,13 @@ Given the following event containing redactable content:
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age_ts": 1000000
|
"age_ts": 1000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The event signing algorithm should emit the following signed event:
|
The event signing algorithm should emit the following signed event:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"content": {
|
"content": {
|
||||||
"body": "Here is the message content"
|
"body": "Here is the message content"
|
||||||
},
|
},
|
||||||
|
@ -990,4 +1047,5 @@ The event signing algorithm should emit the following signed event:
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age_ts": 1000000
|
"age_ts": 1000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -71,10 +71,12 @@ inconsistency.
|
||||||
Any errors which occur at the Matrix API level MUST return a "standard
|
Any errors which occur at the Matrix API level MUST return a "standard
|
||||||
error response". This is a JSON object which looks like:
|
error response". This is a JSON object which looks like:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"errcode": "<error code>",
|
"errcode": "<error code>",
|
||||||
"error": "<error message>"
|
"error": "<error message>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The `error` string will be a human-readable error message, usually a
|
The `error` string will be a human-readable error message, usually a
|
||||||
sentence explaining what went wrong. The `errcode` string will be a
|
sentence explaining what went wrong. The `errcode` string will be a
|
||||||
|
@ -439,7 +441,8 @@ homeserver returns an HTTP 401 response, with a JSON body, as follows:
|
||||||
HTTP/1.1 401 Unauthorized
|
HTTP/1.1 401 Unauthorized
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"flows": [
|
"flows": [
|
||||||
{
|
{
|
||||||
"stages": [ "example.type.foo", "example.type.bar" ]
|
"stages": [ "example.type.foo", "example.type.bar" ]
|
||||||
|
@ -454,7 +457,8 @@ homeserver returns an HTTP 401 response, with a JSON body, as follows:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"session": "xxxxxx"
|
"session": "xxxxxx"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In addition to the `flows`, this object contains some extra information:
|
In addition to the `flows`, this object contains some extra information:
|
||||||
|
|
||||||
|
@ -482,7 +486,8 @@ type `example.type.foo`, it might submit something like this:
|
||||||
POST /_matrix/client/r0/endpoint HTTP/1.1
|
POST /_matrix/client/r0/endpoint HTTP/1.1
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"a_request_parameter": "something",
|
"a_request_parameter": "something",
|
||||||
"another_request_parameter": "something else",
|
"another_request_parameter": "something else",
|
||||||
"auth": {
|
"auth": {
|
||||||
|
@ -490,7 +495,8 @@ type `example.type.foo`, it might submit something like this:
|
||||||
"session": "xxxxxx",
|
"session": "xxxxxx",
|
||||||
"example_credential": "verypoorsharedsecret"
|
"example_credential": "verypoorsharedsecret"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If the homeserver deems the authentication attempt to be successful but
|
If the homeserver deems the authentication attempt to be successful but
|
||||||
still requires more stages to be completed, it returns HTTP status 401
|
still requires more stages to be completed, it returns HTTP status 401
|
||||||
|
@ -501,7 +507,8 @@ client has completed successfully:
|
||||||
HTTP/1.1 401 Unauthorized
|
HTTP/1.1 401 Unauthorized
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"completed": [ "example.type.foo" ],
|
"completed": [ "example.type.foo" ],
|
||||||
"flows": [
|
"flows": [
|
||||||
{
|
{
|
||||||
|
@ -517,7 +524,8 @@ client has completed successfully:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"session": "xxxxxx"
|
"session": "xxxxxx"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Individual stages may require more than one request to complete, in
|
Individual stages may require more than one request to complete, in
|
||||||
which case the response will be as if the request was unauthenticated
|
which case the response will be as if the request was unauthenticated
|
||||||
|
@ -531,7 +539,8 @@ status 401 response as above, with the addition of the standard
|
||||||
HTTP/1.1 401 Unauthorized
|
HTTP/1.1 401 Unauthorized
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"errcode": "M_FORBIDDEN",
|
"errcode": "M_FORBIDDEN",
|
||||||
"error": "Invalid password",
|
"error": "Invalid password",
|
||||||
"completed": [ "example.type.foo" ],
|
"completed": [ "example.type.foo" ],
|
||||||
|
@ -549,7 +558,8 @@ status 401 response as above, with the addition of the standard
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"session": "xxxxxx"
|
"session": "xxxxxx"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If the request fails for a reason other than authentication, the server
|
If the request fails for a reason other than authentication, the server
|
||||||
returns an error message in the standard format. For example:
|
returns an error message in the standard format. For example:
|
||||||
|
@ -557,10 +567,12 @@ returns an error message in the standard format. For example:
|
||||||
HTTP/1.1 400 Bad request
|
HTTP/1.1 400 Bad request
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"errcode": "M_EXAMPLE_ERROR",
|
"errcode": "M_EXAMPLE_ERROR",
|
||||||
"error": "Something was wrong"
|
"error": "Something was wrong"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If the client has completed all stages of a flow, the homeserver
|
If the client has completed all stages of a flow, the homeserver
|
||||||
performs the API call and returns the result as normal. Completed stages
|
performs the API call and returns the result as normal. Completed stages
|
||||||
|
@ -641,14 +653,16 @@ plain-text.
|
||||||
To use this authentication type, clients should submit an auth dict as
|
To use this authentication type, clients should submit an auth dict as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
"type": "m.login.password",
|
"type": "m.login.password",
|
||||||
"identifier": {
|
"identifier": {
|
||||||
...
|
...
|
||||||
},
|
},
|
||||||
"password": "<password>",
|
"password": "<password>",
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
where the `identifier` property is a user identifier object, as
|
where the `identifier` property is a user identifier object, as
|
||||||
described in [Identifier types](#identifier-types).
|
described in [Identifier types](#identifier-types).
|
||||||
|
@ -656,7 +670,8 @@ described in [Identifier types](#identifier-types).
|
||||||
For example, to authenticate using the user's Matrix ID, clients would
|
For example, to authenticate using the user's Matrix ID, clients would
|
||||||
submit:
|
submit:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.password",
|
"type": "m.login.password",
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"type": "m.id.user",
|
"type": "m.id.user",
|
||||||
|
@ -664,13 +679,15 @@ submit:
|
||||||
},
|
},
|
||||||
"password": "<password>",
|
"password": "<password>",
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Alternatively reply using a 3PID bound to the user's account on the
|
Alternatively reply using a 3PID bound to the user's account on the
|
||||||
homeserver using the `/account/3pid`\_ API rather than giving the `user`
|
homeserver using the `/account/3pid`\_ API rather than giving the `user`
|
||||||
explicitly as follows:
|
explicitly as follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.password",
|
"type": "m.login.password",
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"type": "m.id.thirdparty",
|
"type": "m.id.thirdparty",
|
||||||
|
@ -679,7 +696,8 @@ explicitly as follows:
|
||||||
},
|
},
|
||||||
"password": "<password>",
|
"password": "<password>",
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In the case that the homeserver does not know about the supplied 3PID,
|
In the case that the homeserver does not know about the supplied 3PID,
|
||||||
the homeserver must respond with 403 Forbidden.
|
the homeserver must respond with 403 Forbidden.
|
||||||
|
@ -695,11 +713,13 @@ The user completes a Google ReCaptcha 2.0 challenge
|
||||||
To use this authentication type, clients should submit an auth dict as
|
To use this authentication type, clients should submit an auth dict as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.recaptcha",
|
"type": "m.login.recaptcha",
|
||||||
"response": "<captcha response>",
|
"response": "<captcha response>",
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Single Sign-On
|
#### Single Sign-On
|
||||||
|
|
||||||
|
@ -730,7 +750,8 @@ information should be submitted to the homeserver.
|
||||||
To use this authentication type, clients should submit an auth dict as
|
To use this authentication type, clients should submit an auth dict as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.email.identity",
|
"type": "m.login.email.identity",
|
||||||
"threepidCreds": [
|
"threepidCreds": [
|
||||||
{
|
{
|
||||||
|
@ -741,7 +762,8 @@ follows:
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Note that `id_server` (and therefore `id_access_token`) is optional if
|
Note that `id_server` (and therefore `id_access_token`) is optional if
|
||||||
the `/requestToken` request did not include them.
|
the `/requestToken` request did not include them.
|
||||||
|
@ -762,7 +784,8 @@ information should be submitted to the homeserver.
|
||||||
To use this authentication type, clients should submit an auth dict as
|
To use this authentication type, clients should submit an auth dict as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.msisdn",
|
"type": "m.login.msisdn",
|
||||||
"threepidCreds": [
|
"threepidCreds": [
|
||||||
{
|
{
|
||||||
|
@ -773,7 +796,8 @@ follows:
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Note that `id_server` (and therefore `id_access_token`) is optional if
|
Note that `id_server` (and therefore `id_access_token`) is optional if
|
||||||
the `/requestToken` request did not include them.
|
the `/requestToken` request did not include them.
|
||||||
|
@ -798,10 +822,12 @@ server can instead send flows `m.login.recaptcha, m.login.dummy` and
|
||||||
To use this authentication type, clients should submit an auth dict with
|
To use this authentication type, clients should submit an auth dict with
|
||||||
just the type and session, if provided:
|
just the type and session, if provided:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.dummy",
|
"type": "m.login.dummy",
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### Fallback
|
##### Fallback
|
||||||
|
|
||||||
|
@ -820,11 +846,13 @@ This MUST return an HTML page which can perform this authentication
|
||||||
stage. This page must use the following JavaScript when the
|
stage. This page must use the following JavaScript when the
|
||||||
authentication has been completed:
|
authentication has been completed:
|
||||||
|
|
||||||
if (window.onAuthDone) {
|
```js
|
||||||
|
if (window.onAuthDone) {
|
||||||
window.onAuthDone();
|
window.onAuthDone();
|
||||||
} else if (window.opener && window.opener.postMessage) {
|
} else if (window.opener && window.opener.postMessage) {
|
||||||
window.opener.postMessage("authDone", "*");
|
window.opener.postMessage("authDone", "*");
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This allows the client to either arrange for the global function
|
This allows the client to either arrange for the global function
|
||||||
`onAuthDone` to be defined in an embedded browser, or to use the HTML5
|
`onAuthDone` to be defined in an embedded browser, or to use the HTML5
|
||||||
|
@ -836,16 +864,19 @@ Once a client receives the notification that the authentication stage
|
||||||
has been completed, it should resubmit the request with an auth dict
|
has been completed, it should resubmit the request with an auth dict
|
||||||
with just the session ID:
|
with just the session ID:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"session": "<session ID>"
|
"session": "<session ID>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
A client webapp might use the following JavaScript to open a popup
|
A client webapp might use the following JavaScript to open a popup
|
||||||
window which will handle unknown login types:
|
window which will handle unknown login types:
|
||||||
|
|
||||||
/**
|
```js
|
||||||
|
/**
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* homeserverUrl: the base url of the homeserver (e.g. "https://matrix.org")
|
* homeserverUrl: the base url of the homeserver (e.g. "https://matrix.org")
|
||||||
*
|
*
|
||||||
|
@ -858,7 +889,7 @@ window which will handle unknown login types:
|
||||||
*
|
*
|
||||||
* onComplete: a callback which will be called with the results of the request
|
* onComplete: a callback which will be called with the results of the request
|
||||||
*/
|
*/
|
||||||
function unknownLoginType(homeserverUrl, apiEndpoint, loginType, sessionID, onComplete) {
|
function unknownLoginType(homeserverUrl, apiEndpoint, loginType, sessionID, onComplete) {
|
||||||
var popupWindow;
|
var popupWindow;
|
||||||
|
|
||||||
var eventListener = function(ev) {
|
var eventListener = function(ev) {
|
||||||
|
@ -891,9 +922,9 @@ window which will handle unknown login types:
|
||||||
"/fallback/web?session=" +
|
"/fallback/web?session=" +
|
||||||
encodeURIComponent(sessionID);
|
encodeURIComponent(sessionID);
|
||||||
|
|
||||||
|
|
||||||
popupWindow = window.open(url);
|
popupWindow = window.open(url);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### Identifier types
|
##### Identifier types
|
||||||
|
|
||||||
|
@ -920,10 +951,12 @@ A client can identify a user using their Matrix ID. This can either be
|
||||||
the fully qualified Matrix user ID, or just the localpart of the user
|
the fully qualified Matrix user ID, or just the localpart of the user
|
||||||
ID.
|
ID.
|
||||||
|
|
||||||
"identifier": {
|
```json
|
||||||
|
"identifier": {
|
||||||
"type": "m.id.user",
|
"type": "m.id.user",
|
||||||
"user": "<user_id or user localpart>"
|
"user": "<user_id or user localpart>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Third-party ID
|
#### Third-party ID
|
||||||
|
|
||||||
|
@ -940,11 +973,13 @@ using the `/account/3pid`\_ API. See the [3PID
|
||||||
Types](../appendices.html#pid-types) Appendix for a list of Third-party
|
Types](../appendices.html#pid-types) Appendix for a list of Third-party
|
||||||
ID media.
|
ID media.
|
||||||
|
|
||||||
"identifier": {
|
```json
|
||||||
|
"identifier": {
|
||||||
"type": "m.id.thirdparty",
|
"type": "m.id.thirdparty",
|
||||||
"medium": "<The medium of the third party identifier>",
|
"medium": "<The medium of the third party identifier>",
|
||||||
"address": "<The canonicalised third party address of the user>"
|
"address": "<The canonicalised third party address of the user>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Phone number
|
#### Phone number
|
||||||
|
|
||||||
|
@ -962,11 +997,13 @@ If the client wishes to canonicalise the phone number, then it can use
|
||||||
the `m.id.thirdparty` identifier type with a `medium` of `msisdn`
|
the `m.id.thirdparty` identifier type with a `medium` of `msisdn`
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
"identifier": {
|
```json
|
||||||
|
"identifier": {
|
||||||
"type": "m.id.phone",
|
"type": "m.id.phone",
|
||||||
"country": "<The country that the phone number is from>",
|
"country": "<The country that the phone number is from>",
|
||||||
"phone": "<The phone number>"
|
"phone": "<The phone number>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The `country` is the two-letter uppercase ISO-3166-1 alpha-2 country
|
The `country` is the two-letter uppercase ISO-3166-1 alpha-2 country
|
||||||
code that the number in `phone` should be parsed as if it were dialled
|
code that the number in `phone` should be parsed as if it were dialled
|
||||||
|
@ -983,27 +1020,31 @@ API](#user-interactive-authentication-api).
|
||||||
For a simple username/password login, clients should submit a `/login`
|
For a simple username/password login, clients should submit a `/login`
|
||||||
request as follows:
|
request as follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.password",
|
"type": "m.login.password",
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"type": "m.id.user",
|
"type": "m.id.user",
|
||||||
"user": "<user_id or user localpart>"
|
"user": "<user_id or user localpart>"
|
||||||
},
|
},
|
||||||
"password": "<password>"
|
"password": "<password>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Alternatively, a client can use a 3PID bound to the user's account on
|
Alternatively, a client can use a 3PID bound to the user's account on
|
||||||
the homeserver using the `/account/3pid`\_ API rather than giving the
|
the homeserver using the `/account/3pid`\_ API rather than giving the
|
||||||
`user` explicitly, as follows:
|
`user` explicitly, as follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.password",
|
"type": "m.login.password",
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"medium": "<The medium of the third party identifier>",
|
"medium": "<The medium of the third party identifier>",
|
||||||
"address": "<The canonicalised third party address of the user>"
|
"address": "<The canonicalised third party address of the user>"
|
||||||
},
|
},
|
||||||
"password": "<password>"
|
"password": "<password>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In the case that the homeserver does not know about the supplied 3PID,
|
In the case that the homeserver does not know about the supplied 3PID,
|
||||||
the homeserver must respond with `403 Forbidden`.
|
the homeserver must respond with `403 Forbidden`.
|
||||||
|
@ -1011,10 +1052,12 @@ the homeserver must respond with `403 Forbidden`.
|
||||||
To log in using a login token, clients should submit a `/login` request
|
To log in using a login token, clients should submit a `/login` request
|
||||||
as follows:
|
as follows:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.login.token",
|
"type": "m.login.token",
|
||||||
"token": "<login token>"
|
"token": "<login token>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
As with [token-based]() interactive login, the `token` must encode the
|
As with [token-based]() interactive login, the `token` must encode the
|
||||||
user ID. In the case that the token is not valid, the homeserver must
|
user ID. In the case that the token is not valid, the homeserver must
|
||||||
|
@ -1166,13 +1209,15 @@ to change their password.
|
||||||
|
|
||||||
An example of the capability API's response for this capability is:
|
An example of the capability API's response for this capability is:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"m.change_password": {
|
"m.change_password": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `m.room_versions` capability
|
### `m.room_versions` capability
|
||||||
|
|
||||||
|
@ -1183,7 +1228,8 @@ upgrade their rooms.
|
||||||
|
|
||||||
An example of the capability API's response for this capability is:
|
An example of the capability API's response for this capability is:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"m.room_versions": {
|
"m.room_versions": {
|
||||||
"default": "1",
|
"default": "1",
|
||||||
|
@ -1195,7 +1241,8 @@ An example of the capability API's response for this capability is:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This capability mirrors the same restrictions of [room
|
This capability mirrors the same restrictions of [room
|
||||||
versions](../index.html#room-versions) to describe which versions are
|
versions](../index.html#room-versions) to describe which versions are
|
||||||
|
@ -1626,36 +1673,48 @@ There are several APIs provided to `GET` events for a room:
|
||||||
|
|
||||||
Valid requests look like:
|
Valid requests look like:
|
||||||
|
|
||||||
PUT /rooms/!roomid:domain/state/m.example.event
|
```
|
||||||
{ "key" : "without a state key" }
|
PUT /rooms/!roomid:domain/state/m.example.event
|
||||||
|
{ "key" : "without a state key" }
|
||||||
PUT /rooms/!roomid:domain/state/m.another.example.event/foo
|
```
|
||||||
{ "key" : "with 'foo' as the state key" }
|
```
|
||||||
|
PUT /rooms/!roomid:domain/state/m.another.example.event/foo
|
||||||
|
{ "key" : "with 'foo' as the state key" }
|
||||||
|
```
|
||||||
|
|
||||||
In contrast, these requests are invalid:
|
In contrast, these requests are invalid:
|
||||||
|
|
||||||
POST /rooms/!roomid:domain/state/m.example.event/
|
```
|
||||||
{ "key" : "cannot use POST here" }
|
POST /rooms/!roomid:domain/state/m.example.event/
|
||||||
|
{ "key" : "cannot use POST here" }
|
||||||
PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
|
```
|
||||||
{ "key" : "txnIds are not supported" }
|
```
|
||||||
|
PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
|
||||||
|
{ "key" : "txnIds are not supported" }
|
||||||
|
```
|
||||||
|
|
||||||
Care should be taken to avoid setting the wrong `state key`:
|
Care should be taken to avoid setting the wrong `state key`:
|
||||||
|
|
||||||
PUT /rooms/!roomid:domain/state/m.another.example.event/11
|
```
|
||||||
{ "key" : "with '11' as the state key, but was probably intended to be a txnId" }
|
PUT /rooms/!roomid:domain/state/m.another.example.event/11
|
||||||
|
{ "key" : "with '11' as the state key, but was probably intended to be a txnId" }
|
||||||
|
```
|
||||||
|
|
||||||
The `state_key` is often used to store state about individual users, by
|
The `state_key` is often used to store state about individual users, by
|
||||||
using the user ID as the `state_key` value. For example:
|
using the user ID as the `state_key` value. For example:
|
||||||
|
|
||||||
PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Aexample.org
|
```
|
||||||
{ "animal" : "cat", "reason": "fluffy" }
|
PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Aexample.org
|
||||||
|
{ "animal" : "cat", "reason": "fluffy" }
|
||||||
|
```
|
||||||
|
|
||||||
In some cases, there may be no need for a `state_key`, so it can be
|
In some cases, there may be no need for a `state_key`, so it can be
|
||||||
omitted:
|
omitted:
|
||||||
|
|
||||||
PUT /rooms/!roomid:domain/state/m.room.bgd.color
|
```
|
||||||
{ "color": "red", "hex": "#ff0000" }
|
PUT /rooms/!roomid:domain/state/m.room.bgd.color
|
||||||
|
{ "color": "red", "hex": "#ff0000" }
|
||||||
|
```
|
||||||
|
|
||||||
{{room\_send\_cs\_http\_api}}
|
{{room\_send\_cs\_http\_api}}
|
||||||
|
|
||||||
|
@ -1872,19 +1931,23 @@ someone, the user performing the ban MUST have the required power level.
|
||||||
To ban a user, a request should be made to `/rooms/<room_id>/ban`\_
|
To ban a user, a request should be made to `/rooms/<room_id>/ban`\_
|
||||||
with:
|
with:
|
||||||
|
|
||||||
{
|
```json
|
||||||
"user_id": "<user id to ban>"
|
{
|
||||||
|
"user_id": "<user id to ban>",
|
||||||
"reason": "string: <reason for the ban>"
|
"reason": "string: <reason for the ban>"
|
||||||
}
|
}
|
||||||
|
````
|
||||||
|
|
||||||
Banning a user adjusts the banned member's membership state to `ban`.
|
Banning a user adjusts the banned member's membership state to `ban`.
|
||||||
Like with other membership changes, a user can directly adjust the
|
Like with other membership changes, a user can directly adjust the
|
||||||
target member's state, by making a request to
|
target member's state, by making a request to
|
||||||
`/rooms/<room id>/state/m.room.member/<user id>`:
|
`/rooms/<room id>/state/m.room.member/<user id>`:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"membership": "ban"
|
"membership": "ban"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
A user must be explicitly unbanned with a request to
|
A user must be explicitly unbanned with a request to
|
||||||
`/rooms/<room_id>/unban`\_ before they can re-join the room or be
|
`/rooms/<room_id>/unban`\_ before they can re-join the room or be
|
||||||
|
@ -1938,11 +2001,13 @@ Homeservers SHOULD implement rate limiting to reduce the risk of being
|
||||||
overloaded. If a request is refused due to rate limiting, it should
|
overloaded. If a request is refused due to rate limiting, it should
|
||||||
return a standard error response of the form:
|
return a standard error response of the form:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"errcode": "M_LIMIT_EXCEEDED",
|
"errcode": "M_LIMIT_EXCEEDED",
|
||||||
"error": "string",
|
"error": "string",
|
||||||
"retry_after_ms": integer (optional)
|
"retry_after_ms": integer (optional)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The `retry_after_ms` key SHOULD be included to tell the client how long
|
The `retry_after_ms` key SHOULD be included to tell the client how long
|
||||||
they have to wait in milliseconds before they can try again.
|
they have to wait in milliseconds before they can try again.
|
||||||
|
|
|
@ -99,14 +99,16 @@ with the following properties:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"key":"06UzBknVHFMwgi7AVloY7ylC+xhOhEX4PkNge14Grl8",
|
"key":"06UzBknVHFMwgi7AVloY7ylC+xhOhEX4PkNge14Grl8",
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"@user:example.com": {
|
"@user:example.com": {
|
||||||
"ed25519:EGURVBUNJP": "YbJva03ihSj5mPk+CHMJKUKlCXCPFXjXOK6VqBnN9nA2evksQcTGn6hwQfrgRHIDDXO2le49x7jnWJHMJrJoBQ"
|
"ed25519:EGURVBUNJP": "YbJva03ihSj5mPk+CHMJKUKlCXCPFXjXOK6VqBnN9nA2evksQcTGn6hwQfrgRHIDDXO2le49x7jnWJHMJrJoBQ"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### Device keys
|
##### Device keys
|
||||||
|
|
||||||
|
@ -1239,7 +1241,8 @@ keys in [Server-side key backups](#server-side-key-backups) but adds the
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
[
|
```
|
||||||
|
[
|
||||||
{
|
{
|
||||||
"algorithm": "m.megolm.v1.aes-sha2",
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
"forwarding_curve25519_key_chain": [
|
"forwarding_curve25519_key_chain": [
|
||||||
|
@ -1254,7 +1257,8 @@ Example:
|
||||||
"session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
|
"session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
|
||||||
#### Messaging Algorithms
|
#### Messaging Algorithms
|
||||||
|
|
||||||
|
@ -1297,7 +1301,8 @@ device key, and must publish Curve25519 one-time keys.
|
||||||
|
|
||||||
An event encrypted using Olm has the following format:
|
An event encrypted using Olm has the following format:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.room.encrypted",
|
"type": "m.room.encrypted",
|
||||||
"content": {
|
"content": {
|
||||||
"algorithm": "m.olm.v1.curve25519-aes-sha2",
|
"algorithm": "m.olm.v1.curve25519-aes-sha2",
|
||||||
|
@ -1309,7 +1314,8 @@ An event encrypted using Olm has the following format:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`ciphertext` is a mapping from device Curve25519 key to an encrypted
|
`ciphertext` is a mapping from device Curve25519 key to an encrypted
|
||||||
payload for that device. `body` is a Base64-encoded Olm message body.
|
payload for that device. `body` is a Base64-encoded Olm message body.
|
||||||
|
@ -1334,7 +1340,8 @@ message.
|
||||||
|
|
||||||
The plaintext payload is of the form:
|
The plaintext payload is of the form:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "<type of the plaintext event>",
|
"type": "<type of the plaintext event>",
|
||||||
"content": "<content for the plaintext event>",
|
"content": "<content for the plaintext event>",
|
||||||
"sender": "<sender_user_id>",
|
"sender": "<sender_user_id>",
|
||||||
|
@ -1345,7 +1352,8 @@ The plaintext payload is of the form:
|
||||||
"keys": {
|
"keys": {
|
||||||
"ed25519": "<sender_ed25519_key>"
|
"ed25519": "<sender_ed25519_key>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The type and content of the plaintext message event are given in the
|
The type and content of the plaintext message event are given in the
|
||||||
payload.
|
payload.
|
||||||
|
@ -1418,7 +1426,8 @@ Devices that support Megolm must support Olm, and include
|
||||||
|
|
||||||
An event encrypted using Megolm has the following format:
|
An event encrypted using Megolm has the following format:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "m.room.encrypted",
|
"type": "m.room.encrypted",
|
||||||
"content": {
|
"content": {
|
||||||
"algorithm": "m.megolm.v1.aes-sha2",
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
@ -1427,16 +1436,19 @@ An event encrypted using Megolm has the following format:
|
||||||
"session_id": "<outbound_group_session_id>",
|
"session_id": "<outbound_group_session_id>",
|
||||||
"ciphertext": "<encrypted_payload_base_64>"
|
"ciphertext": "<encrypted_payload_base_64>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The encrypted payload can contain any message event. The plaintext is of
|
The encrypted payload can contain any message event. The plaintext is of
|
||||||
the form:
|
the form:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"type": "<event_type>",
|
"type": "<event_type>",
|
||||||
"content": "<event_content>",
|
"content": "<event_content>",
|
||||||
"room_id": "<the room_id>"
|
"room_id": "<the room_id>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
We include the room ID in the payload, because otherwise the homeserver
|
We include the room ID in the payload, because otherwise the homeserver
|
||||||
would be able to change the room a message was sent in.
|
would be able to change the room a message was sent in.
|
||||||
|
@ -1554,7 +1566,8 @@ already shared a room.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"next_batch": "s72595_4483_1934",
|
"next_batch": "s72595_4483_1934",
|
||||||
"rooms": {"leave": {}, "join": {}, "invite": {}},
|
"rooms": {"leave": {}, "join": {}, "invite": {}},
|
||||||
"device_lists": {
|
"device_lists": {
|
||||||
|
@ -1569,7 +1582,8 @@ Example response:
|
||||||
"curve25519": 10,
|
"curve25519": 10,
|
||||||
"signed_curve25519": 20
|
"signed_curve25519": 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Reporting that decryption keys are withheld
|
#### Reporting that decryption keys are withheld
|
||||||
|
|
||||||
|
|
|
@ -332,7 +332,8 @@ a rich reply, infinitely.
|
||||||
|
|
||||||
An `m.in_reply_to` relationship looks like the following:
|
An `m.in_reply_to` relationship looks like the following:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
...
|
...
|
||||||
"type": "m.room.message",
|
"type": "m.room.message",
|
||||||
"content": {
|
"content": {
|
||||||
|
@ -346,7 +347,8 @@ An `m.in_reply_to` relationship looks like the following:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
####### Fallbacks and event representation
|
####### Fallbacks and event representation
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,14 @@ Mentions apply only to [m.room.message]() events where the `msgtype` is
|
||||||
To make a mention, reference the entity being mentioned in the
|
To make a mention, reference the entity being mentioned in the
|
||||||
`formatted_body` using an anchor, like so:
|
`formatted_body` using an anchor, like so:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"body": "Hello Alice!",
|
"body": "Hello Alice!",
|
||||||
"msgtype": "m.text",
|
"msgtype": "m.text",
|
||||||
"format": "org.matrix.custom.html",
|
"format": "org.matrix.custom.html",
|
||||||
"formatted_body": "Hello <a href='https://matrix.to/#/@alice:example.org'>Alice</a>!"
|
"formatted_body": "Hello <a href='https://matrix.to/#/@alice:example.org'>Alice</a>!"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Client behaviour
|
#### Client behaviour
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,8 @@ user. By default this rule is disabled.
|
||||||
|
|
||||||
Definition
|
Definition
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.master",
|
"rule_id": ".m.rule.master",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
@ -290,7 +291,8 @@ Definition
|
||||||
"actions": [
|
"actions": [
|
||||||
"dont_notify"
|
"dont_notify"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.suppress_notices`
|
######## `.m.rule.suppress_notices`
|
||||||
|
|
||||||
|
@ -298,7 +300,8 @@ Matches messages with a `msgtype` of `notice`.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.suppress_notices",
|
"rule_id": ".m.rule.suppress_notices",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -312,7 +315,8 @@ Definition:
|
||||||
"actions": [
|
"actions": [
|
||||||
"dont_notify",
|
"dont_notify",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.invite_for_me`
|
######## `.m.rule.invite_for_me`
|
||||||
|
|
||||||
|
@ -320,7 +324,8 @@ Matches any invites to a new room for this user.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.invite_for_me",
|
"rule_id": ".m.rule.invite_for_me",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -348,7 +353,8 @@ Definition:
|
||||||
"value": "default"
|
"value": "default"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.member_event`
|
######## `.m.rule.member_event`
|
||||||
|
|
||||||
|
@ -356,7 +362,8 @@ Matches any `m.room.member_event`.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.member_event",
|
"rule_id": ".m.rule.member_event",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -370,7 +377,8 @@ Definition:
|
||||||
"actions": [
|
"actions": [
|
||||||
"dont_notify"
|
"dont_notify"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.contains_display_name`
|
######## `.m.rule.contains_display_name`
|
||||||
|
|
||||||
|
@ -379,7 +387,8 @@ current display name in the room in which it was sent.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.contains_display_name",
|
"rule_id": ".m.rule.contains_display_name",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -398,7 +407,8 @@ Definition:
|
||||||
"set_tweak": "highlight"
|
"set_tweak": "highlight"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.tombstone`
|
######## `.m.rule.tombstone`
|
||||||
|
|
||||||
|
@ -408,7 +418,8 @@ an `@room` notification would accomplish.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.tombstone",
|
"rule_id": ".m.rule.tombstone",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -430,7 +441,8 @@ Definition:
|
||||||
"set_tweak": "highlight"
|
"set_tweak": "highlight"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.roomnotif`
|
######## `.m.rule.roomnotif`
|
||||||
|
|
||||||
|
@ -439,7 +451,8 @@ Matches any message whose content is unencrypted and contains the text
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.roomnotif",
|
"rule_id": ".m.rule.roomnotif",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -460,7 +473,8 @@ Definition:
|
||||||
"set_tweak": "highlight"
|
"set_tweak": "highlight"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
####### Default Content Rules
|
####### Default Content Rules
|
||||||
|
|
||||||
|
@ -471,7 +485,8 @@ part of the user's Matrix ID, separated by word boundaries.
|
||||||
|
|
||||||
Definition (as a `content` rule):
|
Definition (as a `content` rule):
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.contains_user_name",
|
"rule_id": ".m.rule.contains_user_name",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -486,7 +501,8 @@ Definition (as a `content` rule):
|
||||||
"set_tweak": "highlight"
|
"set_tweak": "highlight"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
####### Default Underride Rules
|
####### Default Underride Rules
|
||||||
|
|
||||||
|
@ -496,7 +512,8 @@ Matches any incoming VOIP call.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.call",
|
"rule_id": ".m.rule.call",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -514,7 +531,8 @@ Definition:
|
||||||
"value": "ring"
|
"value": "ring"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.encrypted_room_one_to_one`
|
######## `.m.rule.encrypted_room_one_to_one`
|
||||||
|
|
||||||
|
@ -526,7 +544,8 @@ encrypted (in 1:1 rooms) or none.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.encrypted_room_one_to_one",
|
"rule_id": ".m.rule.encrypted_room_one_to_one",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -548,7 +567,8 @@ Definition:
|
||||||
"value": "default"
|
"value": "default"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.room_one_to_one`
|
######## `.m.rule.room_one_to_one`
|
||||||
|
|
||||||
|
@ -556,7 +576,8 @@ Matches any message sent in a room with exactly two members.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.room_one_to_one",
|
"rule_id": ".m.rule.room_one_to_one",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -578,7 +599,8 @@ Definition:
|
||||||
"value": "default"
|
"value": "default"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.message`
|
######## `.m.rule.message`
|
||||||
|
|
||||||
|
@ -586,7 +608,8 @@ Matches all chat messages.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.message",
|
"rule_id": ".m.rule.message",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -600,7 +623,8 @@ Definition:
|
||||||
"actions": [
|
"actions": [
|
||||||
"notify"
|
"notify"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
######## `.m.rule.encrypted`
|
######## `.m.rule.encrypted`
|
||||||
|
|
||||||
|
@ -611,7 +635,8 @@ either matches *all* events that are encrypted (in group rooms) or none.
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"rule_id": ".m.rule.encrypted",
|
"rule_id": ".m.rule.encrypted",
|
||||||
"default": true,
|
"default": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -625,7 +650,8 @@ Definition:
|
||||||
"actions": [
|
"actions": [
|
||||||
"notify"
|
"notify"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### Push Rules: API
|
##### Push Rules: API
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ before delivering them to clients.
|
||||||
Receipts are sent across federation as EDUs with type `m.receipt`. The
|
Receipts are sent across federation as EDUs with type `m.receipt`. The
|
||||||
format of the EDUs are:
|
format of the EDUs are:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
<room_id>: {
|
<room_id>: {
|
||||||
<receipt_type>: {
|
<receipt_type>: {
|
||||||
<user_id>: { <content> }
|
<user_id>: { <content> }
|
||||||
|
@ -76,7 +77,8 @@ format of the EDUs are:
|
||||||
...
|
...
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
These are always sent as deltas to previously sent receipts. Currently
|
These are always sent as deltas to previously sent receipts. Currently
|
||||||
only a single `<receipt_type>` should be used: `m.read`.
|
only a single `<receipt_type>` should be used: `m.read`.
|
||||||
|
|
|
@ -114,7 +114,8 @@ Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`:
|
||||||
|
|
||||||
`org.example.some.secret`:
|
`org.example.some.secret`:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
"encrypted": {
|
"encrypted": {
|
||||||
"key_id_1": {
|
"key_id_1": {
|
||||||
"ciphertext": "base64+encoded+encrypted+data",
|
"ciphertext": "base64+encoded+encrypted+data",
|
||||||
|
@ -126,25 +127,30 @@ Some secret is encrypted using keys with ID `key_id_1` and `key_id_2`:
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
and the key descriptions for the keys would be:
|
and the key descriptions for the keys would be:
|
||||||
|
|
||||||
`m.secret_storage.key.key_id_1`:
|
`m.secret_storage.key.key_id_1`:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
"name": "Some key",
|
"name": "Some key",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
// ... other properties according to algorithm
|
// ... other properties according to algorithm
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`m.secret_storage.key.key_id_2`:
|
`m.secret_storage.key.key_id_2`:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
"name": "Some other key",
|
"name": "Some other key",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
// ... other properties according to algorithm
|
// ... other properties according to algorithm
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
###### `m.secret_storage.v1.aes-hmac-sha2`
|
###### `m.secret_storage.v1.aes-hmac-sha2`
|
||||||
|
|
||||||
|
@ -247,16 +253,19 @@ correctly entered the key, clients should:
|
||||||
For example, the `m.secret_storage.key.key_id` for a key using this
|
For example, the `m.secret_storage.key.key_id` for a key using this
|
||||||
algorithm could look like:
|
algorithm could look like:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"name": "m.default",
|
"name": "m.default",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
"iv": "random+data",
|
"iv": "random+data",
|
||||||
"mac": "mac+of+encrypted+zeros"
|
"mac": "mac+of+encrypted+zeros"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
and data encrypted using this algorithm could look like this:
|
and data encrypted using this algorithm could look like this:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"encrypted": {
|
"encrypted": {
|
||||||
"key_id": {
|
"key_id": {
|
||||||
"iv": "16+bytes+base64",
|
"iv": "16+bytes+base64",
|
||||||
|
@ -264,7 +273,8 @@ and data encrypted using this algorithm could look like this:
|
||||||
"mac": "base64+encoded+mac"
|
"mac": "base64+encoded+mac"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
###### Key representation
|
###### Key representation
|
||||||
|
|
||||||
|
@ -339,7 +349,8 @@ in the `iterations` parameter.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
"passphrase": {
|
"passphrase": {
|
||||||
"algorithm": "m.pbkdf2",
|
"algorithm": "m.pbkdf2",
|
||||||
"salt": "MmMsAlty",
|
"salt": "MmMsAlty",
|
||||||
|
@ -347,7 +358,8 @@ Example:
|
||||||
"bits": 256
|
"bits": 256
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Sharing
|
#### Sharing
|
||||||
|
|
||||||
|
@ -407,12 +419,14 @@ previous request. It is sent as an unencrypted to-device event.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"name": "org.example.some.secret",
|
"name": "org.example.some.secret",
|
||||||
"action": "request",
|
"action": "request",
|
||||||
"requesting_device_id": "ABCDEFG",
|
"requesting_device_id": "ABCDEFG",
|
||||||
"request_id": "randomly_generated_id_9573"
|
"request_id": "randomly_generated_id_9573"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
###### `m.secret.send`
|
###### `m.secret.send`
|
||||||
|
|
||||||
|
@ -444,7 +458,9 @@ an `m.secret.request` event. It must be encrypted as an
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"request_id": "randomly_generated_id_9573",
|
"request_id": "randomly_generated_id_9573",
|
||||||
"secret": "ThisIsASecretDon'tTellAnyone"
|
"secret": "ThisIsASecretDon'tTellAnyone"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -125,7 +125,8 @@ This module adds the following properties to the \_ response:
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"next_batch": "s72595_4483_1934",
|
"next_batch": "s72595_4483_1934",
|
||||||
"rooms": {"leave": {}, "join": {}, "invite": {}},
|
"rooms": {"leave": {}, "join": {}, "invite": {}},
|
||||||
"to_device": {
|
"to_device": {
|
||||||
|
@ -140,4 +141,5 @@ Example response:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -69,10 +69,12 @@ communication, and all API calls use a Content-Type of
|
||||||
Any errors which occur at the Matrix API level MUST return a "standard
|
Any errors which occur at the Matrix API level MUST return a "standard
|
||||||
error response". This is a JSON object which looks like:
|
error response". This is a JSON object which looks like:
|
||||||
|
|
||||||
{
|
```json
|
||||||
|
{
|
||||||
"errcode": "<error code>",
|
"errcode": "<error code>",
|
||||||
"error": "<error message>"
|
"error": "<error message>"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The `error` string will be a human-readable error message, usually a
|
The `error` string will be a human-readable error message, usually a
|
||||||
sentence explaining what went wrong. The `errcode` string will be a
|
sentence explaining what went wrong. The `errcode` string will be a
|
||||||
|
|
|
@ -248,7 +248,8 @@ and any query parameters if present, but should not include the leading
|
||||||
|
|
||||||
Step 1 sign JSON:
|
Step 1 sign JSON:
|
||||||
|
|
||||||
{
|
```
|
||||||
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"uri": "/target",
|
"uri": "/target",
|
||||||
"origin": "origin.hs.example.com",
|
"origin": "origin.hs.example.com",
|
||||||
|
@ -259,7 +260,8 @@ Step 1 sign JSON:
|
||||||
"ed25519:key1": "ABCDEF..."
|
"ed25519:key1": "ABCDEF..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The server names in the JSON above are the server names for each
|
The server names in the JSON above are the server names for each
|
||||||
homeserver involved. Delegation from the [server name resolution
|
homeserver involved. Delegation from the [server name resolution
|
||||||
|
@ -277,7 +279,8 @@ Step 2 add Authorization header:
|
||||||
|
|
||||||
Example python code:
|
Example python code:
|
||||||
|
|
||||||
def authorization_headers(origin_name, origin_signing_key,
|
```py
|
||||||
|
def authorization_headers(origin_name, origin_signing_key,
|
||||||
destination_name, request_method, request_target,
|
destination_name, request_method, request_target,
|
||||||
content=None):
|
content=None):
|
||||||
request_json = {
|
request_json = {
|
||||||
|
@ -302,6 +305,7 @@ Example python code:
|
||||||
))
|
))
|
||||||
|
|
||||||
return ("Authorization", authorization_headers)
|
return ("Authorization", authorization_headers)
|
||||||
|
```
|
||||||
|
|
||||||
### Response Authentication
|
### Response Authentication
|
||||||
|
|
||||||
|
@ -1121,7 +1125,8 @@ SHA-256.
|
||||||
|
|
||||||
### Example code
|
### Example code
|
||||||
|
|
||||||
def hash_and_sign_event(event_object, signing_key, signing_name):
|
```py
|
||||||
|
def hash_and_sign_event(event_object, signing_key, signing_name):
|
||||||
# First we need to hash the event object.
|
# First we need to hash the event object.
|
||||||
content_hash = compute_content_hash(event_object)
|
content_hash = compute_content_hash(event_object)
|
||||||
event_object["hashes"] = {"sha256": encode_unpadded_base64(content_hash)}
|
event_object["hashes"] = {"sha256": encode_unpadded_base64(content_hash)}
|
||||||
|
@ -1140,7 +1145,7 @@ SHA-256.
|
||||||
# Copy the signatures from the stripped event to the original event.
|
# Copy the signatures from the stripped event to the original event.
|
||||||
event_object["signatures"] = signed_object["signatures"]
|
event_object["signatures"] = signed_object["signatures"]
|
||||||
|
|
||||||
def compute_content_hash(event_object):
|
def compute_content_hash(event_object):
|
||||||
# take a copy of the event before we remove any keys.
|
# take a copy of the event before we remove any keys.
|
||||||
event_object = dict(event_object)
|
event_object = dict(event_object)
|
||||||
|
|
||||||
|
@ -1164,6 +1169,7 @@ SHA-256.
|
||||||
event_json_bytes = encode_canonical_json(event_object)
|
event_json_bytes = encode_canonical_json(event_object)
|
||||||
|
|
||||||
return hashlib.sha256(event_json_bytes)
|
return hashlib.sha256(event_json_bytes)
|
||||||
|
```
|
||||||
|
|
||||||
## Security considerations
|
## Security considerations
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue