From ec9a618bf32b6777edbf792a0aefd856c10151f9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 3 Oct 2018 13:32:46 -0600 Subject: [PATCH 01/13] Terms of service API proposal --- proposals/1692-terms-api.md | 180 ++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 proposals/1692-terms-api.md diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md new file mode 100644 index 00000000000..e35b2e9ace1 --- /dev/null +++ b/proposals/1692-terms-api.md @@ -0,0 +1,180 @@ +# Terms of service / privacy policy API + +Homeservers may wish to force users to accept a set of policies or otherwise have +users be aware of changes to the terms of service, privacy policy, or other document. +This proposal describes a "Terms API" that gives homeservers the option of enforcing +a terms of service (or other legal document) upon a user before they can use the service. + + +# General principles and motivation + +* The homeserver should be able to support multiple documents (ie: a TOS, privacy policy, + and acceptable use policy). +* The policies should be versioned. +* The homeserver should be able to prevent use of the service if a given version of a document + has not been accepted. + +The primary use of this functionality is to address a user experience issue in most clients +where they are not aware of the terms of service until after they've registered. Given the +terms of service can change at any time, having a versioned set of documents is required to +ensure everyone has accepted the updated terms of service. Homeservers should additionally +be able to decide if the given change to their terms of service requires everyone to accept +the new terms or if no action is required by users. + +The version for a policy should be arbitrary and potentially non-linear, similar to room +versions. The acceptable range of characters for a version is `[a-zA-Z0-9.-]`. + + +# UI authentication changes + +This API makes changes to the registration and login flows for UI auth, and makes use of UI +auth at a later stage in this proposal. + + +## Registration + +During registration it may be important to the homeserver that the user accepts a given policy. +This is described as a `m.login.terms` authentication type. The parameters for this login type +are: + +```json +{ + "policies": [ + { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms.html", + "version": "1.2" + }, + { + "name": "Privacy Policy", + "url": "https://example.org/somewhere/privacy.html", + "version": "1.2" + } + ] +} +``` + +The `name` of a policy is human-readable name for the document. The `url` may point to any +location. The `version` is provided for convenience to the client. The `name` must also be +unique amongst the policies. + +Policies supplied via this method are implied to be required and therefore blocking for the +registration to continue. + +The client is not required to supply any additional information in the auth dict to complete +this stage. The client should present the user with a checkbox to accept each policy with a +link to said policy, or otherwise rely on the homeserver's fallback. + + +## Login + +Similar to registration, the homeserver may provide a required `m.login.terms` stage with the +same information. Because the homeserver is not aware of who is trying to authenticate until +after UI auth is started, the homeserver should present the latest version of each policy in +the login terms stage, and have the login terms stage be one of the last stages in the flow. +Once the homeserver is reasonably certain about who is authorizing themselves, the homeserver +should determine if the user has already accepted the policies they need to. If they have, +the homeserver should append the stage to the `completed` array, or by skipping to the end of +the UI auth process if possible. Under the current specified API, this is already possible +for a homeserver to do and therefore should not require changes to clients. + + +# Asking for acceptance after login/registration + +The homeserver should maintain a read-only `m.terms` account data event with `content` similar +to: + +```json +{ + "accepted": [ + { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms.html", + "version": "1.2" + }, + { + "name": "Privacy Policy", + "url": "https://example.org/somewhere/privacy.html", + "version": "1.2" + } + ], + "pending": [ + { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-2.html", + "version": "2.0", + "required": true + }, + { + "name": "Code of Conduct", + "url": "https://example.org/somewhere/code-of-conduct.html", + "version": "1.0", + "required": false + } + ] +} +``` + +In this example, the user has already accepted the Terms of Service v1.2 and Privacy Policy v1.2, +but has not signed the new Terms of Service or accepted the Code of Conduct. This provides homeservers +with a way to communicate new policies as well as updates to policies. Because the `name` is unique +amongst policies, the client can determine whether a policy is being updated or introduced. The +homeserver will push this account data event to the client as it would for any other account data +event. + +The `required` boolean indicates whether the homeserver is going to prevent use of the account (without +logging the user out) by responding with a 403 `M_TERMS_NOT_SIGNED` error. The error will include an +additional `policy` property like in the following example: + +```json +{ + "errcode": "M_TERMS_NOT_SIGNED", + "error": "Please sign the terms of service", + "policy": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-2.html", + "version": "2.0" + } +} +``` + +The homeserver should not prevent the use of `/sync` (and similar endpoints) but may withhold any +event that is not an update to the `m.terms` account data event. This is to ensure that clients +are not left in the dark when another client for the user accepts the terms of service, or when +the user accepts the terms elsewhere. + +In addition, the homeserver should not prevent the use of the special "terms acceptance" API +described in the next section. + + +## Terms acceptance API + +One way to accept the terms of service is to force the user to log in again, however that is +likely to be less than desireable for most users. Instead, the client may make a request to +`/_matrix/client/r0/terms` using the user-interactive authentication approach with a valid +access token. The homeserver may decide which additional stages it wants the user to complete +but must at least include the `m.login.terms` stage in every flow. The behaviour of the stage +is exactly the same as how it works for registering an account: the policies the homeserver +wants the user to accept are provided as the parameters for the stage, which should include +policies that are `"required": false` in the user's account data. This is because the same +API is used by the client to acknowledge a non-blocking policy (such as the Code of Conduct +in the prior example). + + +# Why use account data and not a special /sync property or polling API? + +The user's account data was picked because (almost) every client is aware of the concept and +requires few changes to support it. Many clients additionally have optimized the lookup for +account data via caching, making the information essentially always available. If +[MSC1339](https://github.com/matrix-org/matrix-doc/issues/1339) were to be approved, this +lookup becomes even easier. + +Clients are almost certainly going to want to show the information somewhere in their UI, +sometimes due to a legal requirement. The information should therefore be easily accessible. + +A special key in `/sync` was not picked because it leads to clients having to support yet +another field on the sync response. It would also require clients that don't sync to sync, +which should not be the case (although currently they would until MSC1339 lands anyways). + +A dedicated API for the client to poll against was also not picked due to similar reasons. +The client shouldn't have to poll yet another API just for policies. From 98360c1d19efd2010105287da6f3be0988f9d485 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 12 Oct 2018 17:28:19 -0600 Subject: [PATCH 02/13] Alter structures to support multiple languages This also introduces an ID for clients to internally reference the policies. --- proposals/1692-terms-api.md | 135 ++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 45 deletions(-) diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md index e35b2e9ace1..3a61f058726 100644 --- a/proposals/1692-terms-api.md +++ b/proposals/1692-terms-api.md @@ -34,29 +34,42 @@ auth at a later stage in this proposal. ## Registration During registration it may be important to the homeserver that the user accepts a given policy. -This is described as a `m.login.terms` authentication type. The parameters for this login type -are: +This is described as a `m.login.terms` authentication type. The parameters for this authentication +type are: ```json { - "policies": [ - { - "name": "Terms of Service", - "url": "https://example.org/somewhere/terms.html", - "version": "1.2" + "policies": { + "terms_of_service": { + "version": "1.2", + "en": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-1.2-en.html" + }, + "fr": { + "name": "Conditions d'utilisation", + "url": "https://example.org/somewhere/terms-1.2-fr.html" + } }, - { - "name": "Privacy Policy", - "url": "https://example.org/somewhere/privacy.html", - "version": "1.2" + "privacy_policy": { + "version": "1.2", + "en": { + "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" + } } - ] + } } ``` -The `name` of a policy is human-readable name for the document. The `url` may point to any -location. The `version` is provided for convenience to the client. The `name` must also be -unique amongst the policies. +Policies have a unique identifer represented by the key under `policies`. The `version` is +provided as a convience to the client, and is alongside the different language options for +each of the policies. The `name` of a policy is human-readable name for the document. The +`url` may point to any location. The implicit ID may only contain characters in `[a-zA-Z0-9_]`. Policies supplied via this method are implied to be required and therefore blocking for the registration to continue. @@ -86,32 +99,56 @@ to: ```json { - "accepted": [ - { - "name": "Terms of Service", - "url": "https://example.org/somewhere/terms.html", - "version": "1.2" + "accepted": { + "terms_of_service": { + "version": "1.2", + "en": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-1.2-en.html" + }, + "fr": { + "name": "Conditions d'utilisation", + "url": "https://example.org/somewhere/terms-1.2-fr.html" + } }, - { - "name": "Privacy Policy", - "url": "https://example.org/somewhere/privacy.html", - "version": "1.2" + "privacy_policy": { + "version": "1.2", + "en": { + "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" + } } - ], - "pending": [ - { - "name": "Terms of Service", - "url": "https://example.org/somewhere/terms-2.html", + }, + "pending": { + "terms_of_service": { "version": "2.0", - "required": true + "required": true, + "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" + } }, - { - "name": "Code of Conduct", - "url": "https://example.org/somewhere/code-of-conduct.html", + "code_of_conduct": { "version": "1.0", - "required": false + "required": false, + "en": { + "name": "Code of Conduct", + "url": "https://example.org/somewhere/code-of-conduct-1.0-en.html" + }, + "fr": { + "name": "Code de conduite", + "url": "https://example.org/somewhere/code-of-conduct-1.0-fr.html" + } } - ] + } } ``` @@ -124,16 +161,24 @@ event. The `required` boolean indicates whether the homeserver is going to prevent use of the account (without logging the user out) by responding with a 403 `M_TERMS_NOT_SIGNED` error. The error will include an -additional `policy` property like in the following example: +additional `policies` property like in the following example: ```json { "errcode": "M_TERMS_NOT_SIGNED", "error": "Please sign the terms of service", - "policy": { - "name": "Terms of Service", - "url": "https://example.org/somewhere/terms-2.html", - "version": "2.0" + "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" + } + } } } ``` @@ -153,11 +198,11 @@ One way to accept the terms of service is to force the user to log in again, how likely to be less than desireable for most users. Instead, the client may make a request to `/_matrix/client/r0/terms` using the user-interactive authentication approach with a valid access token. The homeserver may decide which additional stages it wants the user to complete -but must at least include the `m.login.terms` stage in every flow. The behaviour of the stage -is exactly the same as how it works for registering an account: the policies the homeserver -wants the user to accept are provided as the parameters for the stage, which should include -policies that are `"required": false` in the user's account data. This is because the same -API is used by the client to acknowledge a non-blocking policy (such as the Code of Conduct +but must at least include the `m.login.terms` stage in every flow. The authentication type's +behaviour is exactly the same as how it works for registering an account: the policies the +homeserver wants the user to accept are provided as the parameters for the stage, which should +include policies that are `"required": false` in the user's account data. This is because the +same API is used by the client to acknowledge a non-blocking policy (such as the Code of Conduct in the prior example). From 53c705c48bed111f39473efe6d5ac1290622aff3 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 12 Oct 2018 17:28:36 -0600 Subject: [PATCH 03/13] Alter the login process to reflect login's lack of UI auth --- proposals/1692-terms-api.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md index 3a61f058726..4f670cf26f0 100644 --- a/proposals/1692-terms-api.md +++ b/proposals/1692-terms-api.md @@ -81,16 +81,33 @@ link to said policy, or otherwise rely on the homeserver's fallback. ## Login -Similar to registration, the homeserver may provide a required `m.login.terms` stage with the -same information. Because the homeserver is not aware of who is trying to authenticate until -after UI auth is started, the homeserver should present the latest version of each policy in -the login terms stage, and have the login terms stage be one of the last stages in the flow. -Once the homeserver is reasonably certain about who is authorizing themselves, the homeserver -should determine if the user has already accepted the policies they need to. If they have, -the homeserver should append the stage to the `completed` array, or by skipping to the end of -the UI auth process if possible. Under the current specified API, this is already possible -for a homeserver to do and therefore should not require changes to clients. - +Given the Client-Server specification doesn't use UI auth on the login routes, mimicking the +process used for registration is more difficult. The homeserver would not be aware of who is +logging in prior to the user submitting their details (eg: token, password, etc) and therefore +cannot provide exact details on which policies the user must accept as part of the initial login +process. Instead, the login endpoints should be brought closer to how UI auth works using the +following process: + +1. The client requests the login flows (unchanged behaviour) - policies are not referenced yet. +2. The client submits the user's details (eg: password) to the homeserver (unchanged behaviour). +3. If the credentials are valid, the homeserver checks if the user has any required policies to + accept. If the user does not have any pending policies, skip to step 6. +4. The homeserver responds with a 401 as per the UI auth requirements (an `errcode` of `M_FORBIDDEN` + and `error` saying something like `"Please accept the terms and conditions"`). The `completed` + stages would have the user's already-accepted step listed (eg: `m.login.password`). The + `m.login.terms` authentication type shares the same data structure as the one used during + registration. +5. The client submits the request again as if it were following the UI auth requirements (ie: for + the purpose of `m.login.terms`, submitting an empty auth dict). +6. The homeserver logs the user in by generating a token and returning it to the client as per + the specification. + +This process is chosen to begin introducing the UI auth mechanism to login without breaking clients. +In the future, the login endpoints should be migrated fully to use UI auth instead of a different +approach. By nature of having the homeserver return a 401 when the user must accept terms before +continuing, older clients will not be able to work around the limitation. Modern clients would be +expected to support the UI auth flow (after the initial password submission), therefore allowing +the user to actually log in. # Asking for acceptance after login/registration From 9c8100b539b5655cf8ef8b82677d76b799c8d6b7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 21 Jun 2019 14:29:30 -0600 Subject: [PATCH 04/13] Add a note about MSC2140 --- proposals/1692-terms-api.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md index 4f670cf26f0..52d2d5ab298 100644 --- a/proposals/1692-terms-api.md +++ b/proposals/1692-terms-api.md @@ -176,6 +176,14 @@ amongst policies, the client can determine whether a policy is being updated or homeserver will push this account data event to the client as it would for any other account data event. +*Note*: [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) requires the client to treat +URLs for policies returned by this API to be de-duplicated. The expectation is that clients will +continue to use this terms of service API to manage approval of policies with their homeserver and +consider duplicated policies from the identity server/integration manager where appropriate. Ultimately, +the client may end up making several requests to all 3 servers despite only rendering a single +checkbox for the user to click. This only applies to a post-MSC2140 world and does not otherwise +affect this proposal. + The `required` boolean indicates whether the homeserver is going to prevent use of the account (without logging the user out) by responding with a 403 `M_TERMS_NOT_SIGNED` error. The error will include an additional `policies` property like in the following example: From e963a947cf455151fff699b0bbaf826403267f43 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 21 Jun 2019 14:35:04 -0600 Subject: [PATCH 05/13] Add a flag to the sync response to indicate the sync is withheld --- proposals/1692-terms-api.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md index 52d2d5ab298..9c1fcf3d426 100644 --- a/proposals/1692-terms-api.md +++ b/proposals/1692-terms-api.md @@ -209,9 +209,13 @@ additional `policies` property like in the following example: ``` The homeserver should not prevent the use of `/sync` (and similar endpoints) but may withhold any -event that is not an update to the `m.terms` account data event. This is to ensure that clients -are not left in the dark when another client for the user accepts the terms of service, or when -the user accepts the terms elsewhere. +event that is not an update to the `m.terms` account data event. If a server is witholding events, +it MUST set `withheld: true` as a top level field in the sync response. This is to ensure +that clients are not left in the dark when another client for the user accepts the terms of service, +or when the user accepts the terms elsewhere. + +*Note*: `withheld` was chosen over `limited` due to `limited` already having special meaning in +the context of sync. In addition, the homeserver should not prevent the use of the special "terms acceptance" API described in the next section. From cfb2bc00a56391cb5bcb7ab93537daadde137641 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 21 Jun 2019 14:35:15 -0600 Subject: [PATCH 06/13] Use the soft logout MSC --- proposals/1692-terms-api.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md index 9c1fcf3d426..70ffc007848 100644 --- a/proposals/1692-terms-api.md +++ b/proposals/1692-terms-api.md @@ -185,13 +185,17 @@ checkbox for the user to click. This only applies to a post-MSC2140 world and do affect this proposal. The `required` boolean indicates whether the homeserver is going to prevent use of the account (without -logging the user out) by responding with a 403 `M_TERMS_NOT_SIGNED` error. The error will include an -additional `policies` property like in the following example: +logging the user out) by responding with a 401 `M_TERMS_NOT_SIGNED` error, including setting a `soft_logout` +flag as per [MSC1466](https://github.com/matrix-org/matrix-doc/issues/1466). Clients should not log +out the user if they understand the error code, instead opting to prompt the user to accept the new +policies required by the homeserver. The error will include an additional `policies` property like in +the following example: ```json { "errcode": "M_TERMS_NOT_SIGNED", "error": "Please sign the terms of service", + "soft_logout": true, "policies": { "terms_of_service": { "version": "2.0", From ca3cbb938826c25d3c6d73ea0c954e45870fd310 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Feb 2021 21:06:30 -0700 Subject: [PATCH 07/13] Fix headings --- proposals/1692-terms-api.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md index 70ffc007848..0f276620477 100644 --- a/proposals/1692-terms-api.md +++ b/proposals/1692-terms-api.md @@ -5,8 +5,7 @@ users be aware of changes to the terms of service, privacy policy, or other docu This proposal describes a "Terms API" that gives homeservers the option of enforcing a terms of service (or other legal document) upon a user before they can use the service. - -# General principles and motivation +## General principles and motivation * The homeserver should be able to support multiple documents (ie: a TOS, privacy policy, and acceptable use policy). @@ -24,14 +23,12 @@ the new terms or if no action is required by users. The version for a policy should be arbitrary and potentially non-linear, similar to room versions. The acceptable range of characters for a version is `[a-zA-Z0-9.-]`. - -# UI authentication changes +## UI authentication changes This API makes changes to the registration and login flows for UI auth, and makes use of UI auth at a later stage in this proposal. - -## Registration +### Registration During registration it may be important to the homeserver that the user accepts a given policy. This is described as a `m.login.terms` authentication type. The parameters for this authentication @@ -78,8 +75,7 @@ The client is not required to supply any additional information in the auth dict this stage. The client should present the user with a checkbox to accept each policy with a link to said policy, or otherwise rely on the homeserver's fallback. - -## Login +### Login Given the Client-Server specification doesn't use UI auth on the login routes, mimicking the process used for registration is more difficult. The homeserver would not be aware of who is @@ -109,7 +105,7 @@ continuing, older clients will not be able to work around the limitation. Modern expected to support the UI auth flow (after the initial password submission), therefore allowing the user to actually log in. -# Asking for acceptance after login/registration +## Asking for acceptance after login/registration The homeserver should maintain a read-only `m.terms` account data event with `content` similar to: @@ -224,8 +220,7 @@ the context of sync. In addition, the homeserver should not prevent the use of the special "terms acceptance" API described in the next section. - -## Terms acceptance API +### Terms acceptance API One way to accept the terms of service is to force the user to log in again, however that is likely to be less than desireable for most users. Instead, the client may make a request to @@ -238,8 +233,7 @@ include policies that are `"required": false` in the user's account data. This i same API is used by the client to acknowledge a non-blocking policy (such as the Code of Conduct in the prior example). - -# Why use account data and not a special /sync property or polling API? +## Why use account data and not a special /sync property or polling API? The user's account data was picked because (almost) every client is aware of the concept and requires few changes to support it. Many clients additionally have optimized the lookup for From 4d8ff12ac27c8c5117b9c13fda60056ae44f053a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Feb 2021 21:13:24 -0700 Subject: [PATCH 08/13] Move non-registration bits out --- proposals/1692-terms-api.md | 252 ------------------------ proposals/1692-terms-at-registration.md | 76 +++++++ 2 files changed, 76 insertions(+), 252 deletions(-) delete mode 100644 proposals/1692-terms-api.md create mode 100644 proposals/1692-terms-at-registration.md diff --git a/proposals/1692-terms-api.md b/proposals/1692-terms-api.md deleted file mode 100644 index 0f276620477..00000000000 --- a/proposals/1692-terms-api.md +++ /dev/null @@ -1,252 +0,0 @@ -# Terms of service / privacy policy API - -Homeservers may wish to force users to accept a set of policies or otherwise have -users be aware of changes to the terms of service, privacy policy, or other document. -This proposal describes a "Terms API" that gives homeservers the option of enforcing -a terms of service (or other legal document) upon a user before they can use the service. - -## General principles and motivation - -* The homeserver should be able to support multiple documents (ie: a TOS, privacy policy, - and acceptable use policy). -* The policies should be versioned. -* The homeserver should be able to prevent use of the service if a given version of a document - has not been accepted. - -The primary use of this functionality is to address a user experience issue in most clients -where they are not aware of the terms of service until after they've registered. Given the -terms of service can change at any time, having a versioned set of documents is required to -ensure everyone has accepted the updated terms of service. Homeservers should additionally -be able to decide if the given change to their terms of service requires everyone to accept -the new terms or if no action is required by users. - -The version for a policy should be arbitrary and potentially non-linear, similar to room -versions. The acceptable range of characters for a version is `[a-zA-Z0-9.-]`. - -## UI authentication changes - -This API makes changes to the registration and login flows for UI auth, and makes use of UI -auth at a later stage in this proposal. - -### Registration - -During registration it may be important to the homeserver that the user accepts a given policy. -This is described as a `m.login.terms` authentication type. The parameters for this authentication -type are: - -```json -{ - "policies": { - "terms_of_service": { - "version": "1.2", - "en": { - "name": "Terms of Service", - "url": "https://example.org/somewhere/terms-1.2-en.html" - }, - "fr": { - "name": "Conditions d'utilisation", - "url": "https://example.org/somewhere/terms-1.2-fr.html" - } - }, - "privacy_policy": { - "version": "1.2", - "en": { - "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" - } - } - } -} -``` - -Policies have a unique identifer represented by the key under `policies`. The `version` is -provided as a convience to the client, and is alongside the different language options for -each of the policies. The `name` of a policy is human-readable name for the document. The -`url` may point to any location. The implicit ID may only contain characters in `[a-zA-Z0-9_]`. - -Policies supplied via this method are implied to be required and therefore blocking for the -registration to continue. - -The client is not required to supply any additional information in the auth dict to complete -this stage. The client should present the user with a checkbox to accept each policy with a -link to said policy, or otherwise rely on the homeserver's fallback. - -### Login - -Given the Client-Server specification doesn't use UI auth on the login routes, mimicking the -process used for registration is more difficult. The homeserver would not be aware of who is -logging in prior to the user submitting their details (eg: token, password, etc) and therefore -cannot provide exact details on which policies the user must accept as part of the initial login -process. Instead, the login endpoints should be brought closer to how UI auth works using the -following process: - -1. The client requests the login flows (unchanged behaviour) - policies are not referenced yet. -2. The client submits the user's details (eg: password) to the homeserver (unchanged behaviour). -3. If the credentials are valid, the homeserver checks if the user has any required policies to - accept. If the user does not have any pending policies, skip to step 6. -4. The homeserver responds with a 401 as per the UI auth requirements (an `errcode` of `M_FORBIDDEN` - and `error` saying something like `"Please accept the terms and conditions"`). The `completed` - stages would have the user's already-accepted step listed (eg: `m.login.password`). The - `m.login.terms` authentication type shares the same data structure as the one used during - registration. -5. The client submits the request again as if it were following the UI auth requirements (ie: for - the purpose of `m.login.terms`, submitting an empty auth dict). -6. The homeserver logs the user in by generating a token and returning it to the client as per - the specification. - -This process is chosen to begin introducing the UI auth mechanism to login without breaking clients. -In the future, the login endpoints should be migrated fully to use UI auth instead of a different -approach. By nature of having the homeserver return a 401 when the user must accept terms before -continuing, older clients will not be able to work around the limitation. Modern clients would be -expected to support the UI auth flow (after the initial password submission), therefore allowing -the user to actually log in. - -## Asking for acceptance after login/registration - -The homeserver should maintain a read-only `m.terms` account data event with `content` similar -to: - -```json -{ - "accepted": { - "terms_of_service": { - "version": "1.2", - "en": { - "name": "Terms of Service", - "url": "https://example.org/somewhere/terms-1.2-en.html" - }, - "fr": { - "name": "Conditions d'utilisation", - "url": "https://example.org/somewhere/terms-1.2-fr.html" - } - }, - "privacy_policy": { - "version": "1.2", - "en": { - "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" - } - } - }, - "pending": { - "terms_of_service": { - "version": "2.0", - "required": true, - "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" - } - }, - "code_of_conduct": { - "version": "1.0", - "required": false, - "en": { - "name": "Code of Conduct", - "url": "https://example.org/somewhere/code-of-conduct-1.0-en.html" - }, - "fr": { - "name": "Code de conduite", - "url": "https://example.org/somewhere/code-of-conduct-1.0-fr.html" - } - } - } -} -``` - -In this example, the user has already accepted the Terms of Service v1.2 and Privacy Policy v1.2, -but has not signed the new Terms of Service or accepted the Code of Conduct. This provides homeservers -with a way to communicate new policies as well as updates to policies. Because the `name` is unique -amongst policies, the client can determine whether a policy is being updated or introduced. The -homeserver will push this account data event to the client as it would for any other account data -event. - -*Note*: [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) requires the client to treat -URLs for policies returned by this API to be de-duplicated. The expectation is that clients will -continue to use this terms of service API to manage approval of policies with their homeserver and -consider duplicated policies from the identity server/integration manager where appropriate. Ultimately, -the client may end up making several requests to all 3 servers despite only rendering a single -checkbox for the user to click. This only applies to a post-MSC2140 world and does not otherwise -affect this proposal. - -The `required` boolean indicates whether the homeserver is going to prevent use of the account (without -logging the user out) by responding with a 401 `M_TERMS_NOT_SIGNED` error, including setting a `soft_logout` -flag as per [MSC1466](https://github.com/matrix-org/matrix-doc/issues/1466). Clients should not log -out the user if they understand the error code, instead opting to prompt the user to accept the new -policies required by the homeserver. The error will include an additional `policies` property like in -the following example: - -```json -{ - "errcode": "M_TERMS_NOT_SIGNED", - "error": "Please sign the terms of service", - "soft_logout": true, - "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" - } - } - } -} -``` - -The homeserver should not prevent the use of `/sync` (and similar endpoints) but may withhold any -event that is not an update to the `m.terms` account data event. If a server is witholding events, -it MUST set `withheld: true` as a top level field in the sync response. This is to ensure -that clients are not left in the dark when another client for the user accepts the terms of service, -or when the user accepts the terms elsewhere. - -*Note*: `withheld` was chosen over `limited` due to `limited` already having special meaning in -the context of sync. - -In addition, the homeserver should not prevent the use of the special "terms acceptance" API -described in the next section. - -### Terms acceptance API - -One way to accept the terms of service is to force the user to log in again, however that is -likely to be less than desireable for most users. Instead, the client may make a request to -`/_matrix/client/r0/terms` using the user-interactive authentication approach with a valid -access token. The homeserver may decide which additional stages it wants the user to complete -but must at least include the `m.login.terms` stage in every flow. The authentication type's -behaviour is exactly the same as how it works for registering an account: the policies the -homeserver wants the user to accept are provided as the parameters for the stage, which should -include policies that are `"required": false` in the user's account data. This is because the -same API is used by the client to acknowledge a non-blocking policy (such as the Code of Conduct -in the prior example). - -## Why use account data and not a special /sync property or polling API? - -The user's account data was picked because (almost) every client is aware of the concept and -requires few changes to support it. Many clients additionally have optimized the lookup for -account data via caching, making the information essentially always available. If -[MSC1339](https://github.com/matrix-org/matrix-doc/issues/1339) were to be approved, this -lookup becomes even easier. - -Clients are almost certainly going to want to show the information somewhere in their UI, -sometimes due to a legal requirement. The information should therefore be easily accessible. - -A special key in `/sync` was not picked because it leads to clients having to support yet -another field on the sync response. It would also require clients that don't sync to sync, -which should not be the case (although currently they would until MSC1339 lands anyways). - -A dedicated API for the client to poll against was also not picked due to similar reasons. -The client shouldn't have to poll yet another API just for policies. diff --git a/proposals/1692-terms-at-registration.md b/proposals/1692-terms-at-registration.md new file mode 100644 index 00000000000..1c6a27ac9bd --- /dev/null +++ b/proposals/1692-terms-at-registration.md @@ -0,0 +1,76 @@ +# MSC1692: Terms of service at registration + +Homeservers may wish to force users to accept a set of policies or otherwise have +users be aware of changes to the terms of service, privacy policy, or other document. +This proposal describes a "Terms API" that gives homeservers the option of enforcing +a terms of service (or other legal document) upon a user before they can use the service. + +## General principles and motivation + +* The homeserver should be able to support multiple documents (ie: a TOS, privacy policy, + and acceptable use policy). +* The policies should be versioned. +* The homeserver should be able to prevent use of the service if a given version of a document + has not been accepted. + +The primary use of this functionality is to address a user experience issue in most clients +where they are not aware of the terms of service until after they've registered. Given the +terms of service can change at any time, having a versioned set of documents is required to +ensure everyone has accepted the updated terms of service. Homeservers should additionally +be able to decide if the given change to their terms of service requires everyone to accept +the new terms or if no action is required by users. + +The version for a policy should be arbitrary and potentially non-linear, similar to room +versions. The acceptable range of characters for a version is `[a-zA-Z0-9.-]`. + +## UI authentication changes + +This API makes changes to the registration and login flows for UI auth, and makes use of UI +auth at a later stage in this proposal. + +### Registration + +During registration it may be important to the homeserver that the user accepts a given policy. +This is described as a `m.login.terms` authentication type. The parameters for this authentication +type are: + +```json +{ + "policies": { + "terms_of_service": { + "version": "1.2", + "en": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-1.2-en.html" + }, + "fr": { + "name": "Conditions d'utilisation", + "url": "https://example.org/somewhere/terms-1.2-fr.html" + } + }, + "privacy_policy": { + "version": "1.2", + "en": { + "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" + } + } + } +} +``` + +Policies have a unique identifer represented by the key under `policies`. The `version` is +provided as a convience to the client, and is alongside the different language options for +each of the policies. The `name` of a policy is human-readable name for the document. The +`url` may point to any location. The implicit ID may only contain characters in `[a-zA-Z0-9_]`. + +Policies supplied via this method are implied to be required and therefore blocking for the +registration to continue. + +The client is not required to supply any additional information in the auth dict to complete +this stage. The client should present the user with a checkbox to accept each policy with a +link to said policy, or otherwise rely on the homeserver's fallback. From 895a93f8054abf24c73ad1f01258347e55eb03a3 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Feb 2021 21:16:08 -0700 Subject: [PATCH 09/13] Reference MSC3012 --- proposals/1692-terms-at-registration.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/1692-terms-at-registration.md b/proposals/1692-terms-at-registration.md index 1c6a27ac9bd..fc073ba09b3 100644 --- a/proposals/1692-terms-at-registration.md +++ b/proposals/1692-terms-at-registration.md @@ -5,6 +5,9 @@ users be aware of changes to the terms of service, privacy policy, or other docu This proposal describes a "Terms API" that gives homeservers the option of enforcing a terms of service (or other legal document) upon a user before they can use the service. +**Note**: This proposal used to contain an entire TOS API for clients to interact with. +This functionality has been moved to [MSC3012](https://github.com/matrix-org/matrix-doc/pull/3012). + ## General principles and motivation * The homeserver should be able to support multiple documents (ie: a TOS, privacy policy, From 0f0978886bb525a809ff12f9e91a7326ecab0b3e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Feb 2021 21:20:12 -0700 Subject: [PATCH 10/13] Adjust wording for new scope --- proposals/1692-terms-at-registration.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/1692-terms-at-registration.md b/proposals/1692-terms-at-registration.md index fc073ba09b3..2856ae7131e 100644 --- a/proposals/1692-terms-at-registration.md +++ b/proposals/1692-terms-at-registration.md @@ -2,8 +2,8 @@ Homeservers may wish to force users to accept a set of policies or otherwise have users be aware of changes to the terms of service, privacy policy, or other document. -This proposal describes a "Terms API" that gives homeservers the option of enforcing -a terms of service (or other legal document) upon a user before they can use the service. +This proposal describes how a server can present these documents during registration +for users to accept before gaining access to the service. **Note**: This proposal used to contain an entire TOS API for clients to interact with. This functionality has been moved to [MSC3012](https://github.com/matrix-org/matrix-doc/pull/3012). @@ -21,7 +21,8 @@ where they are not aware of the terms of service until after they've registered. terms of service can change at any time, having a versioned set of documents is required to ensure everyone has accepted the updated terms of service. Homeservers should additionally be able to decide if the given change to their terms of service requires everyone to accept -the new terms or if no action is required by users. +the new terms or if no action is required by users, though this feature is covered by +[MSC3012](https://github.com/matrix-org/matrix-doc/pull/3012) instead of here. The version for a policy should be arbitrary and potentially non-linear, similar to room versions. The acceptable range of characters for a version is `[a-zA-Z0-9.-]`. From 68779a7dae1823815f03a14b1db861ac162e299b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 21 Feb 2024 10:36:04 -0700 Subject: [PATCH 11/13] Rewrite to newer modern standards; address feedback from last FCP --- proposals/1692-terms-at-registration.md | 97 ++++++++++++++----------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/proposals/1692-terms-at-registration.md b/proposals/1692-terms-at-registration.md index 2856ae7131e..ef34a0cc73f 100644 --- a/proposals/1692-terms-at-registration.md +++ b/proposals/1692-terms-at-registration.md @@ -1,42 +1,26 @@ # MSC1692: Terms of service at registration -Homeservers may wish to force users to accept a set of policies or otherwise have -users be aware of changes to the terms of service, privacy policy, or other document. -This proposal describes how a server can present these documents during registration -for users to accept before gaining access to the service. +At registration, homeservers may wish to require the user to accept a given set of policy documents, +such as a terms of service and privacy policy. There may be many different types of documents, all of +which are versioned and presented in (potentially) multiple languages. -**Note**: This proposal used to contain an entire TOS API for clients to interact with. -This functionality has been moved to [MSC3012](https://github.com/matrix-org/matrix-doc/pull/3012). +This proposal covers requiring users to accept the list of documents during registration. Future +improvements could include informing the user *after* registration that a document has changed, which +has been spun out to [MSC3012](https://github.com/matrix-org/matrix-spec-proposals/pull/3012). -## General principles and motivation +## Proposal -* The homeserver should be able to support multiple documents (ie: a TOS, privacy policy, - and acceptable use policy). -* The policies should be versioned. -* The homeserver should be able to prevent use of the service if a given version of a document - has not been accepted. +The [User-Interactive Authentication](https://spec.matrix.org/v1.9/client-server-api/#user-interactive-authentication-api) +API (UIA) is currently used during registration to create a new account. In future, it is expected +that OIDC will be used instead, which can include support for this MSC's principles without needing +to change the Matrix specification itself. As a measure until OIDC is here though, this MSC exists +to fill the need. -The primary use of this functionality is to address a user experience issue in most clients -where they are not aware of the terms of service until after they've registered. Given the -terms of service can change at any time, having a versioned set of documents is required to -ensure everyone has accepted the updated terms of service. Homeservers should additionally -be able to decide if the given change to their terms of service requires everyone to accept -the new terms or if no action is required by users, though this feature is covered by -[MSC3012](https://github.com/matrix-org/matrix-doc/pull/3012) instead of here. +A new `m.login.terms` authentication type is introduced, allowing servers to include it in registration +flows if it desires. Servers which do not require policy acceptance at registration are not required +to support this flow. -The version for a policy should be arbitrary and potentially non-linear, similar to room -versions. The acceptable range of characters for a version is `[a-zA-Z0-9.-]`. - -## UI authentication changes - -This API makes changes to the registration and login flows for UI auth, and makes use of UI -auth at a later stage in this proposal. - -### Registration - -During registration it may be important to the homeserver that the user accepts a given policy. -This is described as a `m.login.terms` authentication type. The parameters for this authentication -type are: +The parameters for the new authentication type look like the following: ```json { @@ -67,14 +51,45 @@ type are: } ``` -Policies have a unique identifer represented by the key under `policies`. The `version` is -provided as a convience to the client, and is alongside the different language options for -each of the policies. The `name` of a policy is human-readable name for the document. The -`url` may point to any location. The implicit ID may only contain characters in `[a-zA-Z0-9_]`. +Each key under `policies` is a "Policy ID", and defined by the server. They are an opaque identifier +(described later in this proposal). Each policy object associated with the policy ID has a required +`version` as a convenience to the client, and is another opaque identifier. All other keys are language +codes to represent the same document. The client picks the language which best suits the user. + +Language codes *should* be [ISO 639-1] codes combined with an [ISO 3166-1] region code, separated by +an underscore. Servers may also wish to use [BCP 47] codes. This recommendation is to ensure maximum +compatibility with existing conventions around language choices in (Matrix) clients. + +[ISO 639-1]: https://en.wikipedia.org/wiki/ISO_639-1 +[ISO 3166-1]: https://en.wikipedia.org/wiki/ISO_3166-1 +[BCP 47]: https://en.wikipedia.org/wiki/IETF_language_tag + +`name` and `url` for each policy document are required, and are arbitrary strings with no maximum +length. `url` *must* be a valid URI with scheme `https://` or `http://`. Insecure HTTP is discouraged. + +If a client encounters an invalid parameter, registration should stop with an error presented to the +user. + +To complete the stage, accepting *all* of the listed documents, the client submits an empty `auth` +dict. The client *should* present the user with a checkbox to accept each policy, including a link +to the provided `url`, or otherwise rely on [fallback auth](https://spec.matrix.org/v1.9/client-server-api/#fallback). + +The server is expected to track which document versions it presented to the user during registration, +if applicable. + +### Opaque identifier + +This definition is inherited from [MSC1597](https://github.com/matrix-org/matrix-spec-proposals/pull/1597). + +> Opaque IDs must be strings consisting entirely of the characters +> `[0-9a-zA-Z._~-]`. Their length must not exceed 255 characters and they must +> not be empty. + +## Unstable prefix -Policies supplied via this method are implied to be required and therefore blocking for the -registration to continue. +Regretfully, this MSC was implemented with *stable* identifiers before an unstable identifiers process +was established. Implementation has existed in some capacity since 2018: https://github.com/matrix-org/synapse/pull/4004 -The client is not required to supply any additional information in the auth dict to complete -this stage. The client should present the user with a checkbox to accept each policy with a -link to said policy, or otherwise rely on the homeserver's fallback. +Noting that the modern MSC process forbids such behaviour, new implementations should use the stable +`m.login.terms` identifier regardless of MSC status. If the MSC changes in a breaking way, a new +identifier *must* be chosen, and *must* include a proper unstable prefix. From f4dcb96907b8be606dbb85ecf9eddccee987d3be Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 19 Mar 2024 13:13:24 -0600 Subject: [PATCH 12/13] Update proposals/1692-terms-at-registration.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- proposals/1692-terms-at-registration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/1692-terms-at-registration.md b/proposals/1692-terms-at-registration.md index ef34a0cc73f..a556ced93ae 100644 --- a/proposals/1692-terms-at-registration.md +++ b/proposals/1692-terms-at-registration.md @@ -87,7 +87,7 @@ This definition is inherited from [MSC1597](https://github.com/matrix-org/matrix ## Unstable prefix -Regretfully, this MSC was implemented with *stable* identifiers before an unstable identifiers process +Regrettably, this MSC was implemented with *stable* identifiers before an unstable identifiers process was established. Implementation has existed in some capacity since 2018: https://github.com/matrix-org/synapse/pull/4004 Noting that the modern MSC process forbids such behaviour, new implementations should use the stable From 940ae7806221e9a0d412da75545c9b15091ac4cb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 1 Apr 2024 15:28:06 -0600 Subject: [PATCH 13/13] Update references --- proposals/1692-terms-at-registration.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/proposals/1692-terms-at-registration.md b/proposals/1692-terms-at-registration.md index a556ced93ae..1fc77d3bd8b 100644 --- a/proposals/1692-terms-at-registration.md +++ b/proposals/1692-terms-at-registration.md @@ -56,13 +56,10 @@ Each key under `policies` is a "Policy ID", and defined by the server. They are `version` as a convenience to the client, and is another opaque identifier. All other keys are language codes to represent the same document. The client picks the language which best suits the user. -Language codes *should* be [ISO 639-1] codes combined with an [ISO 3166-1] region code, separated by -an underscore. Servers may also wish to use [BCP 47] codes. This recommendation is to ensure maximum -compatibility with existing conventions around language choices in (Matrix) clients. - -[ISO 639-1]: https://en.wikipedia.org/wiki/ISO_639-1 -[ISO 3166-1]: https://en.wikipedia.org/wiki/ISO_3166-1 -[BCP 47]: https://en.wikipedia.org/wiki/IETF_language_tag +Language codes *should* be formatted as per [Section 2.2 of RFC 5646](https://datatracker.ietf.org/doc/html/rfc5646#section-2.2), +noting that some implementation *may* use an underscore instead of dash. For example, `en_US` instead +of `en-US`. This recommendation is to ensure maximum compatibility with existing conventions around +language choices in (Matrix) clients. `name` and `url` for each policy document are required, and are arbitrary strings with no maximum length. `url` *must* be a valid URI with scheme `https://` or `http://`. Insecure HTTP is discouraged.