|
|
|
# MSC2140: Terms of Service API for Identity Servers and Integration Managers
|
|
|
|
|
|
|
|
MSC1692 introduces a method for homeservers to require that users read and
|
|
|
|
agree to certain documents before being permitted to use the service. This
|
|
|
|
proposal introduces a corresponding method that can be used with Identity
|
|
|
|
Servers and Integration Managers.
|
|
|
|
|
|
|
|
The challenge here is that Identity Servers do not require any kind of user
|
|
|
|
login to access the service and so are unable to track what users have agreed
|
|
|
|
to what terms in the way that Homeservers do. We thereforce cannot re-use the
|
|
|
|
same method for Identity Servers without fundamentally changing the Identity
|
|
|
|
Service API.
|
|
|
|
|
|
|
|
Requirements for this proposal are:
|
|
|
|
* ISs and IMs should be able to give multiple documents a user must agree to
|
|
|
|
abide by
|
|
|
|
* Each document shoud be versioned
|
|
|
|
* ISs and IMs must be able to prevent users from using the service if they
|
|
|
|
have not provided agreement.
|
|
|
|
* A user should only have to agree to each version of each document once for
|
|
|
|
their Matrix ID, ie. having agreed to a set of terms in one client, they
|
|
|
|
should not have to agree to them again when using a different client.
|
|
|
|
|
|
|
|
## Proposal
|
|
|
|
|
|
|
|
Throuhgout this proposal, $prefix will be used to refer to the prefix of the
|
|
|
|
API in question, ie. `/_matrix/identity/api/v1` for the IS API and
|
|
|
|
`/_matrix/integrations/v1` for the IM API.
|
|
|
|
|
|
|
|
This proposal introduces:
|
|
|
|
* The `$prefix/terms` endpoint
|
|
|
|
* The `m.third_party_terms` section in account data
|
|
|
|
* The `X-TERMS-TOKEN` HTTP header
|
|
|
|
|
|
|
|
### Terms API
|
|
|
|
|
|
|
|
New API endpoints will be introduced:
|
|
|
|
|
|
|
|
#### `GET $prefix/terms`:
|
|
|
|
This returns a set of documents that the user must agree to abide by in order
|
|
|
|
to use the service. Its response is similar to the structure used in the
|
|
|
|
`m.terms` UI auth flow of the Client/Server API:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"policies": {
|
|
|
|
"terms_of_service": {
|
|
|
|
"version": "2.0",
|
|
|
|
"en": {
|
|
|
|
"name": "Terms of Service",
|
|
|
|
"url": "https://example.org/somewhere/terms-2.0-en.html"
|
|
|
|
},
|
|
|
|
"fr": {
|
|
|
|
"name": "Conditions d'utilisation",
|
|
|
|
"url": "https://example.org/somewhere/terms-2.0-fr.html"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Each document (ie. key/value pair in the 'policies' object) MUST be
|
|
|
|
uniquely identified by its URL. It is therefore strongly recommended
|
|
|
|
that the URL contains the version number of the document. The name
|
|
|
|
and version keys, however, are used only to provide a human-readable
|
|
|
|
description of the document to the user.
|
|
|
|
|
|
|
|
In the IM API, the client should provide authentication for this endpoint.
|
|
|
|
|
|
|
|
#### `POST $prefix/terms`:
|
|
|
|
Requests to this endpoint have a single key, `user_accepts` whose value is
|
|
|
|
a list of URLs (given by the `url` field in the GET response) of documents that
|
|
|
|
the user has agreed to:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"user_accepts": ["https://example.org/somewhere/terms-2.0-en.html"]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
In the IM API, the client should provide authentication for this endpoint.
|
|
|
|
|
|
|
|
The clients MUST include the correct URL for the language of the document that
|
|
|
|
was presented to the user and they agreed to. How servers store or serialise
|
|
|
|
acceptance into the `acceptance_token` is not defined, eg. they may internally
|
|
|
|
transform all URLs to the URL of the English-language version of each document
|
|
|
|
if the server deems it appropriate to do so. Servers should accept agreement of
|
|
|
|
any one language of each document as sufficient, regardless of what language a
|
|
|
|
client is operating in: users should not have to re-consent to documents if
|
|
|
|
they change their client to a different language.
|
|
|
|
|
|
|
|
The response MAY contain a `acceptance_token` which, if given, is an
|
|
|
|
opaque string that the client must store for user in subsequent requests
|
|
|
|
to any endpoint to the same server.
|
|
|
|
|
|
|
|
If the server has stored the fact that the user has agreed to these terms,
|
|
|
|
(which implies the user is authenticated) it can supply no `acceptance_token`.
|
|
|
|
The server may instead choose to supply an `acceptance_token`, for example if,
|
|
|
|
as in the IS API, the user is unauthenticated and therefore the server is
|
|
|
|
unable to store the fact a user has agreed to a set of terms.
|
|
|
|
|
|
|
|
The `acceptance_token` is opaque and it is up to the server how it computes it,
|
|
|
|
but the server must be able to given an `acceptance_token`, compute whether it
|
|
|
|
constitutes agreement to a given set of terms. For example, the simplest (but
|
|
|
|
most verbose) implemenation would be to make the `acceptance_token` the JSON
|
|
|
|
array of documents as provided in the request. A smarter implementation may be
|
|
|
|
a simple hash, or even cryptograhic hash if desired.
|
|
|
|
|
|
|
|
### Third-Party Terms Account Data
|
|
|
|
|
|
|
|
This proposal also defines the `m.third_party_terms` section in User Account
|
|
|
|
Data in the client/server API that clients SHOULD use to track what sets of
|
|
|
|
terms the user has consented to. This has an array of URLs under the 'accepted'
|
|
|
|
key to which the user has agreed to.
|
|
|
|
|
|
|
|
An `m.third_party_terms` section therefore resembles the following:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"accepted": [
|
|
|
|
"https://example.org/somewhere/terms-1.2-en.html",
|
|
|
|
"https://example.org/somewhere/privacy-1.2-en.html"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Whenever a client submits a `POST $prefix/terms` request to an IS or IM, it
|
|
|
|
SHOULD update this account data section adding any the URLs of any additional
|
|
|
|
documents that the user agreed to to this list.
|
|
|
|
|
|
|
|
### Terms Acceptance in the API
|
|
|
|
|
|
|
|
Any request to any endpoint in the IS and IM APIs, with the exception of
|
|
|
|
`/_matrix/identity/api/v1` may return a `M_TERMS_NOT_SIGNED` errcode. This
|
|
|
|
indicates that the user must agree to (new) terms in order to use or continue
|
|
|
|
to use the service.
|
|
|
|
|
|
|
|
The client uses the `GET $prefix/terms` endpoint to get the latest set of terms
|
|
|
|
that must be agreed to. It then cross-references this set of documents against
|
|
|
|
the `m.third_party_terms` account data and presents to the user any documents
|
|
|
|
that they have not already agreed to, along with UI for them to indicate their
|
|
|
|
agreement. Once the user has indicated their agreement, then, and only then,
|
|
|
|
must the client use the `POST $prefix/terms` API to signal to the server the
|
|
|
|
set of documents that the user has agreed to.
|
|
|
|
|
|
|
|
If the server returns an `acceptance_token`, the client should include this
|
|
|
|
token in the `X-TERMS-TOKEN` HTTP header in all subsequent requests to an
|
|
|
|
endpoint on the API with the exception of `/_matrix/identity/api/v1`.
|
|
|
|
|
|
|
|
Both making the `POST $prefix/terms` request and providing an `X-TERMS-TOKEN`
|
|
|
|
header signal that the user consents to the terms contained within the
|
|
|
|
corresponding documents. That is to say, if a client or user obtains an
|
|
|
|
acceptance token via means other than a response to the `POST $perfix/terms`
|
|
|
|
API, inclusion of the acceptance token in an `X-TERMS-TOKEN` header in a
|
|
|
|
request still constitutes agreement to the terms in the corresponding
|
|
|
|
documents.
|
|
|
|
|
|
|
|
## Tradeoffs
|
|
|
|
|
|
|
|
This introduces a different way of accepting terms from the client/server API
|
|
|
|
which uses User-Interactive Authentication. In the client/server API, the use
|
|
|
|
of UI auth allows terms acceptance to be integrated into the registration flow
|
|
|
|
in a simple and backwards-compatible way. Indtroducing the UI Auth mechanism
|
|
|
|
into these other APIs would add significant complexity, so this functionality
|
|
|
|
has been provided with simpler, dedicated endpoints.
|
|
|
|
|
|
|
|
## Potential issues
|
|
|
|
|
|
|
|
If the server does not authentcate users, some mechanism is required to track
|
|
|
|
users agreement to terms. The introduction of an extra HTTP header on all
|
|
|
|
requests adds overhead to every request and complexity to the client to add a
|
|
|
|
custom header.
|
|
|
|
|
|
|
|
|
|
|
|
## Security considerations
|
|
|
|
|
|
|
|
The `acceptance_token` is, in effect, a cookie and could be used to identify
|
|
|
|
users of the service. Users of the Integration manager must be authenticated
|
|
|
|
anyway, so this is irrelevant for the IM API. It could allow an Identity Server
|
|
|
|
to identify users where it may otherwise not be able to do so (if a client was
|
|
|
|
careful to mask other identifying HTTP headers). Given most requests to the IS
|
|
|
|
API, by their nature, include 3pids which, even if hashed, will make users
|
|
|
|
easily identifiable, this probably does not add any significant concern.
|
|
|
|
|
|
|
|
It is assumed that once servers publish a given version of a document at a
|
|
|
|
given URL, the contents of that URL will not change. This could be mitigated by
|
|
|
|
identifying documents based on a hash of their contents rather than their URLs.
|
|
|
|
Agreement to terms in the client/server API makes this assumption, so this
|
|
|
|
proposal aims to be consistent.
|
|
|
|
|
|
|
|
|
|
|
|
## Conclusion
|
|
|
|
|
|
|
|
This proposal adds an error response to all endpoints on the API and a custom
|
|
|
|
HTTP header on all requests that is used to signal agreement to a set of terms
|
|
|
|
and conditions. The use of the header is only necessary if the server has no
|
|
|
|
other means of tracking acceptance of terms per-user. The IS API is not
|
|
|
|
authenticated so ISes will have no choice but to use the header. The IM API is
|
|
|
|
authenticated so IMs may either use the header or store acceptance per-user.
|
|
|
|
|
|
|
|
A separate endpoint is specified with a GET request for retrieving the set
|
|
|
|
of terms required and a POST to indicate that the user consents to those
|
|
|
|
terms.
|