-
Notifications
You must be signed in to change notification settings - Fork 399
MSC2140: Terms of Service for ISes and IMs #2140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
23af87e
32c7fc6
cf48030
276e2b6
d4ca0c2
9ca3ccc
a63e442
4ba9b2a
2555801
8ae4755
abb4071
2c09580
6f374dc
0dae2d5
9e0d8b9
5709427
af691b5
1d75828
ba7047c
4edf826
6273868
58cf083
2694bb1
21b9eaf
b5326de
10a6a59
f95197b
4be283c
83bb386
45d6309
786d5bc
fe14d3c
8af35be
2d11217
5374030
f02e4c2
d00dfb7
03e6ab0
7f65364
ac6b9bd
79dbad2
10858bf
4c72c37
e28f7aa
d15c9df
1a66934
30dcc28
bf8a1e5
701d340
9bb6ad8
f474b31
6e061b1
25a47af
a1de6ff
d9269b0
e4bdc28
12377fb
6d00673
4073d94
6931541
8bd9d7c
4ea8f64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,335 @@ | ||
# MSC2140: Terms of Service API for Identity Servers and Integration Managers | ||
|
||
[MSC1692](https://github.com/matrix-org/matrix-doc/issues/1692) 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. | ||
|
||
Requirements for this proposal are: | ||
* ISes and IMs should be able to give multiple documents a user must agree to | ||
abide by | ||
* Each document shoud be versioned | ||
* ISes and IMs must, for each request that they handle, know that the user | ||
making the request has agreed to their data being used. This need not be | ||
absolute proof (we will always have to trust that the client actually | ||
showed the document to the user) but it must be reasonably demonstrable that | ||
the user has given informed consent for the client to use that service. | ||
* ISes 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. | ||
* Documents should be de-duplicated between services. If two or more services | ||
are hosted by the same organisation, the organisation should have the | ||
option to give their users a single document that encompasses both services | ||
(bearing in mind that the user must be able to opt-out of components of a | ||
service whilst still being able to use the service without that component). | ||
|
||
Identity Servers do not currently 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. | ||
|
||
## Proposal | ||
|
||
Throuhgout this proposal, $prefix will be used to refer to the prefix of the | ||
API in question, ie. `/_matrix/identity/v2` for the IS API and | ||
`/_matrix/integrations/v1` for the IM API. | ||
|
||
Note the removal of the `/api` prefix and migration to v2 in the IS API | ||
following convention from | ||
[MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). | ||
|
||
This proposal introduces: | ||
* A v2 API prefix, with authentication, for the Identity Service | ||
* The `$prefix/terms` endpoint | ||
* The `m.accepted_terms` section in account data | ||
* `POST /_matrix/client/r0/account/3pid/unbind` endpoints on the client/server | ||
API | ||
|
||
This proposal removes: | ||
* The `bind_email` and `bind_msisdn` on the Homeserver `/register` endpoint | ||
|
||
This proposal relies on both Integration Managers and Identity Servers being | ||
able to identify users by their MXID and store the fact that a given MXID has | ||
indicated that they accept the terms given. Integration Managers already | ||
identify users in this way by authenticating them using the OpenID endpoint on | ||
the Homeserver. This proposal introduces the same mechanism to Identity Servers | ||
and adds authentication across the Identity Service API. | ||
|
||
### IS API Authentication | ||
|
||
All current endpoints within `/_matrix/identity/api/v1/` will be duplicated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we also want to drop lookups done without using the hashing aware endpoints? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, in reality we'll probably end up having non-hashed on v1 and hashed on v2, then deprecating non-hashed and v1 at the same time. I didn't wamt to tie this too much to the hashing msc so not sure whether to add that here or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. v2 supports non-hashed so there's no reason to keep v1 around. |
||
into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of | ||
lookups. Authentication is still expected on MSC2134's proposed endpoints. | ||
Support for `application/x-form-www-urlencoded` parameters in requests will | ||
be dropped from all endpoints. | ||
|
||
Any request to any endpoint within `/_matrix/identity/v2`, with the exception | ||
of: | ||
* `/_matrix/identity/v2` | ||
* `/_matrix/identity/v2/pubkey/*` | ||
* The new `$prefix/account/register` endpoint | ||
* The new `GET /_matrix/identity/v2/terms` | ||
* `$prefix/account/logout` | ||
|
||
...may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. | ||
This indicates that the user must authenticate with OpenID and supply a valid | ||
`access_token`. | ||
|
||
Clients authenticate either via an `Authorization` header with a `Bearer` token | ||
or an `access_token` query parameter. | ||
|
||
The existing endpoints under `/_matrix/identity/api/v1/` continue to be | ||
unauthenticated but will be deprecated. ISes may support the old v1 API for as | ||
long as they wish. Once ISes remove support for the old APIs, those endpoints | ||
must return HTTP Status 404. Clients must update to use the v2 API as soon as | ||
possible. | ||
|
||
OpenID authentication in the IS API will work the same as in the Integration Manager | ||
API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961). | ||
|
||
When clients supply an identity server to the Homeserver in order for the | ||
dbkr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Homeserver to make calls to the IS on its behalf, it must also supply its | ||
access token for the Identity Server alongside in the `id_access_token` key of | ||
the same JSON object. That is, in the main request object for `requestToken` | ||
and `/_matrix/client/r0/rooms/{roomId}/invite` requests and in the | ||
`threepidCreds` object when supplying 3PID credentials (eg. in the | ||
`m.email.identity` UI auth stage). The server must also relay | ||
`M_TERMS_NOT_SIGNED` errors back to the client. Exceptions to this are any | ||
requests where the only IS operation the Homeserver may perform is unbinding, | ||
ie. `/_matrix/client/r0/account/deactivate` and | ||
`/_matrix/client/r0/account/3pid/delete`, in which case the unbind will be | ||
authenticated by a signed request from the Homeserver. | ||
|
||
### HS Register API | ||
|
||
The `bind_email` and `bind_msisdn` options to `/_matrix/client/r0/register` in | ||
the client/server API will be removed. Due to the fact that | ||
`/_matrix/identity/v2/3pid/bind` requires authentication, it will no longer be | ||
possible for the Homeserver to bind 3PIDs as part of the registration process. | ||
|
||
### IS Register API | ||
|
||
The following new APIs will be introduced to support OpenID auth as per | ||
[MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961): | ||
|
||
* `/_matrix/identity/v2/account/register` | ||
* `/_matrix/identity/v2/account` | ||
* `/_matrix/identity/v2/account/logout` | ||
|
||
Note again the removal of the `/api` prefix and migration to v2 following | ||
convention from | ||
[MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). | ||
|
||
### 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": { | ||
dbkr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"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" | ||
} | ||
}, | ||
"privacy_policy": { | ||
"version": "1.2", | ||
"en": { | ||
erikjohnston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"name": "Privacy Policy", | ||
"url": "https://example.org/somewhere/privacy-1.2-en.html" | ||
}, | ||
"fr": { | ||
"name": "Politique de confidentialité", | ||
"url": "https://example.org/somewhere/privacy-1.2-fr.html" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Each document (ie. key/value pair in the 'policies' object) MUST be | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uniquely identified by its URL. It is therefore strongly recommended | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to uniquely identify by tuple of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly - otoh I sort of like forcing the version to be in the URL for general URL transparency. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I'm just a little nervous that if we end up having three or more different services, each with multiple policies, with some churn of versions of the years, plus an increasing number of languages, then we'll end up with quite a lot of different stuff to check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Travis points out that we discussed this kind of thing in #2140 (comment) as eventually the conclusion was that de-duping by URL is probably the lesser evil. |
||
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. | ||
|
||
This endpoint does *not* require authentication. | ||
|
||
#### `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"] | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
``` | ||
|
||
This endpoint requires authentication. | ||
|
||
The clients MUST include the correct URL for the language of the document that | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
was presented to the user and they agreed to. 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 server responds with an empty JSON object. The server must not assume that | ||
the client will agree to all documents in a single request. | ||
|
||
### Accepted Terms Account Data | ||
|
||
This proposal also defines the `m.accepted_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' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we have a dictionary There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might also be useful to know the version of the document the user saw too, so could have: {
"accepted": [
{
"url": "https://example.org/somewhere/terms-1.2-en.html",
"version": "1.2",
"id": "terms_of_service",
},
{
"url": "https://example.org/somewhere/privacy-1.2-en.html",
"version": "1.2",
"id": "privacy_policy",
}
]
} ...maybe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this MSC, the policy ID feels like it is not meant to be trusted and could change at any time, so I am wary of making any assumptions about it. Maybe the MSC should explicitly state something like: "The terms response contains these policy IDs largely to match similar behaviour from the homeserver, but don't assume anything about them, they may change at any time, etc. The URL is the thing that matters." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the homeserver version being MSC1692 where the policy ID actually means something. The homeserver version supports optional and non-optional changes to policies whereas this MSC does not (for valid reasons on both fronts, I believe). |
||
key to which the user has agreed to. | ||
|
||
An `m.accepted_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 or | ||
completes an `m.terms` flow on the HS (or as soon as possible afterwards, ie. | ||
after registration is complete), 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 | ||
|
||
Before any requests are made to an Identity Server or Integration Manager, | ||
the client must use the `GET $prefix/terms` endpoint to fetch the set of | ||
documents that the user must agree to in order to use the service. | ||
|
||
It then cross-references this set of documents against the `m.accepted_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. If there are no | ||
such documents (ie. if the `policies` dict is empty or the user has already | ||
agreed to all documents) the client proceeds to perform the OpenID | ||
registration. If there are new terms documents, the client prompts the user for | ||
agreement, then once the user has indicated their agreement, it adds these URLs | ||
to `m.accepted_terms` account data and then proceeds with OpenID | ||
authentication, getting a token from the Homeserver and submitting this to the | ||
service using the `register` endpoint. | ||
|
||
Having done this, if the user agreed to any new documents, it performs a `POST | ||
$prefix/terms` request to signal to the server the set of documents that the | ||
user has agreed to. | ||
|
||
Any request to any endpoint in the IM API, and the `/_matrix/identity/v2/` | ||
namespace of the IS API, with the exception of `/_matrix/identity/v2` itself, | ||
may return: | ||
|
||
* `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that | ||
the user must authenticate with OpenID and supply a valid `access_token`. | ||
* `M_TERMS_NOT_SIGNED` errcode with HTTP status code 403. This indicates | ||
that the user must agree to (new) terms in order to use or continue to | ||
use the service. | ||
|
||
The `/_matrix/identity/v2/3pid/unbind` endpoint must not return either of these | ||
errors if the request has a valid signature from a Homeserver, and is being authenticated as such. | ||
|
||
In summary, the process for using a service that has not previously been used | ||
in the current login session is: | ||
|
||
* `GET $prefix/terms` | ||
* Compare result with `m.accepted_terms` account data, get set of documents | ||
pending agreement. | ||
* If non-empty, show this set of documents to the user and wait for the user | ||
to indicate their agreement. | ||
* Add the newly agreed documents to `m.accepted_terms`. | ||
* On success, or if there were no documents pending agreement, get an OpenID | ||
token from the Homeserver and submit this token to the `register` endpoint. | ||
Store the resulting access token. | ||
* If the set of documents pending agreement was non-empty, Perform a | ||
`POST $prefix/terms` request to the service with these documents. | ||
|
||
### `POST /_matrix/client/r0/account/3pid/unbind` | ||
|
||
A client uses this client/server API endpoint to request that the Homeserver | ||
removes the given 3PID from the given Identity Server, or all Identity Servers. | ||
Takes the same parameters as | ||
`POST /_matrix/client/r0/account/3pid/delete`, ie. `id_server`, `medium`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we just deprecate the delete endpoint? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's still needed for clients to remove the 3pid from the HS though? |
||
`address` and the newly added `is_token`. | ||
|
||
Returns the same as `POST /_matrix/client/r0/account/3pid/delete`. | ||
|
||
Clients may add IS bindings for 3PIDs that already exist on the user's | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default state going forward (in Riot at least) will be that users first add a 3PID to the HS only. Then, in a separate step, they can also choose to share it with the IS as well. It would be ideal if we could bind to the IS via the HS for a 3PID already known to HS (which is what will soon be happening quite often) in a single operation. Currently though, using the Since these CS APIs are authed, the HS knows whether the 3PID is in use by the authed user. It would be best to change the spec to allow re-adding an in use 3PID if it's in use by the current user. In the short term, Riot will work around this by using two step operation of removing the 3PID from the HS and then re-adding with bind true. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several different user flows involving 3PIDs, including:
It would be best from a data flow and privacy perspective if flows 1 - 3 could all be done using the HS only, possibly delegating to a token sending server if needed. matrix-org/synapse#5751 is heading in that direction. For case 4 of sharing a 3PID for discovery, we currently proxy via the HS to the IS for this, mainly so that the HS has a record of 3PID associations that can be unbound when an account is deactivated. With v2 IS APIs and terms requirements, it becomes more complex for the HS to do this proxying. The user may need to agree terms with the IS, but the client isn't communicating with the IS directly. @dbkr has written up several different options of how we could move forward. Option 4 there:
seems appealing, but it's so far unclear how to resolve the authentication issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment, we are leaning towards option 2:
@anoadragon453 is planning to MSC this change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MSC2229 now describes this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this proposal should be updated to reference the new MSC as the suggested way of rebinding? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (in general, please link to the proposal PR instead of the markdown, as people delete branches sometimes: #2229 ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm struggling to follow if this thread has been resolved or not? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the technical problem is solved by #2229. It would be nice to mention that in this proposal, so I believe that's the only action left to resolve this thread. |
||
Homeserver account by using the `POST /_matrix/client/r0/account/3pid` | ||
to re-add the 3PID. | ||
|
||
## Tradeoffs | ||
|
||
The Identity Service API previously did not require authentication, and OpenID | ||
is reasonably complex, adding a significant burden to both clients and servers. | ||
A custom HTTP header was also considered that could be added to assert that the | ||
client agrees to a particular set of terms. We decided against this in favour | ||
of re-using existing primitives that already exist in the Matrix ecosystem. | ||
Custom HTTP headers are not used anywhere else within Matrix. This also gives a | ||
very simple and natural way for ISes to enforce that users may only bind 3PIDs | ||
to their own MXIDs. | ||
|
||
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. Another option here would be to use | ||
UI Auth on the register endpoint. This would also not allow users to register | ||
before accepting the terms. However, this would then make the OpenID | ||
registration process different and non-standard. | ||
|
||
The `m.accepted_terms` section contains only URLs of the documents that | ||
have been agreed to. This loses information like the name and version of | ||
the document, but: | ||
* It would be up to the clients to copy this information correctly into | ||
account data. | ||
* Having just the URLs makes it much easier for clients to make a list | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
of URLs and find documents not already agreed to. | ||
|
||
## Potential issues | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This change deprecates all v1 endpoints and so will require clients to update | ||
to continue working. | ||
|
||
## Security considerations | ||
|
||
Requiring authentication on the IS API means it will no longer be possible to | ||
use it anonymously. | ||
|
||
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. | ||
dbkr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. |
Uh oh!
There was an error while loading. Please reload this page.