From 2bfa891f0a041c77e0d831accc9622b56b802f8d Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 18 Sep 2019 10:36:05 +0100 Subject: [PATCH 1/7] Add function to check for separate 3PID add and bind This adds a way to test for MSC2290 support on the homeserver with separate add and bind functions. It checks the unstable feature flag as well as the upcoming r0.6.0 spec version. Part of https://github.com/vector-im/riot-web/issues/10839 --- src/client.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/client.js b/src/client.js index a86b723d8..131826e24 100644 --- a/src/client.js +++ b/src/client.js @@ -4227,6 +4227,22 @@ MatrixClient.prototype.doesServerAcceptIdentityAccessToken = async function() { || (unstableFeatures && unstableFeatures["m.id_access_token"]); }; +/** + * Query the server to see if it supports separate 3PID add and bind functions. + * This affects the sequence of API calls clients should use for these operations, + * so it's helpful to be able to check for support. + * @return {Promise} true if separate functions are supported + */ +MatrixClient.prototype.doesServerSupportSeparateAddAndBind = async function() { + const response = await this.getVersions(); + + const versions = response["versions"]; + const unstableFeatures = response["unstable_features"]; + + return (versions && versions.includes("r0.6.0")) + || (unstableFeatures && unstableFeatures["m.separate_add_and_bind"]); +}; + /* * Get if lazy loading members is being used. * @return {boolean} Whether or not members are lazy loaded by this client From 745185e6893d4f57dfcc3cf450ed88c628962958 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 18 Sep 2019 10:38:52 +0100 Subject: [PATCH 2/7] Fix function doc syntax --- src/client.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client.js b/src/client.js index 131826e24..f1e547545 100644 --- a/src/client.js +++ b/src/client.js @@ -4154,7 +4154,7 @@ MatrixClient.prototype.stopClient = function() { global.clearTimeout(this._checkTurnServersTimeoutID); }; -/* +/** * Get the API versions supported by the server, along with any * unstable APIs it supports * @return {Promise} The server /versions response @@ -4174,7 +4174,7 @@ MatrixClient.prototype.getVersions = async function() { return this._serverVersionsCache; }; -/* +/** * Query the server to see if it support members lazy loading * @return {Promise} true if server supports lazy loading */ @@ -4188,7 +4188,7 @@ MatrixClient.prototype.doesServerSupportLazyLoading = async function() { || (unstableFeatures && unstableFeatures["m.lazy_load_members"]); }; -/* +/** * Query the server to see if the `id_server` parameter is required * when registering with an 3pid, adding a 3pid or resetting password. * @return {Promise} true if id_server parameter is required @@ -4211,7 +4211,7 @@ MatrixClient.prototype.doesServerRequireIdServerParam = async function() { } }; -/* +/** * Query the server to see if the `id_access_token` parameter can be safely * passed to the homeserver. Some homeservers may trigger errors if they are not * prepared for the new parameter. @@ -4243,7 +4243,7 @@ MatrixClient.prototype.doesServerSupportSeparateAddAndBind = async function() { || (unstableFeatures && unstableFeatures["m.separate_add_and_bind"]); }; -/* +/** * Get if lazy loading members is being used. * @return {boolean} Whether or not members are lazy loaded by this client */ @@ -4251,7 +4251,7 @@ MatrixClient.prototype.hasLazyLoadMembersEnabled = function() { return !!this._clientOpts.lazyLoadMembers; }; -/* +/** * Set a function which is called when /sync returns a 'limited' response. * It is called with a room ID and returns a boolean. It should return 'true' if the SDK * can SAFELY remove events from this room. It may not be safe to remove events if there From b444aaa67ed18833804345e8208c279f122843c3 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 18 Sep 2019 11:16:23 +0100 Subject: [PATCH 3/7] Add separate add and bind HS APIs Part of https://github.com/vector-im/riot-web/issues/10839 --- src/base-apis.js | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/base-apis.js b/src/base-apis.js index 107bbddbd..3dc8ced2a 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1339,10 +1339,16 @@ MatrixBaseApis.prototype.getThreePids = function(callback) { }; /** + * Add a 3PID to your homeserver account and optionally bind it to an identity + * server as well. An identity server is required as part of the `creds` object. + * + * This API is deprecated, and you should instead use `addThreePidOnly` + * for homeservers that support it. + * * @param {Object} creds * @param {boolean} bind * @param {module:client.callback} callback Optional. - * @return {module:client.Promise} Resolves: TODO + * @return {module:client.Promise} Resolves: on success * @return {module:http-api.MatrixError} Rejects: with an error response. */ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) { @@ -1356,6 +1362,46 @@ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) { ); }; +/** + * Add a 3PID to your homeserver account. This API does not use an identity + * server, as the homeserver is expected to handle 3PID ownership validation. + * + * You can check whether a homeserver supports this API via + * `doesServerSupportSeparateAddAndBind`. + * + * @param {Object} data A object with 3PID validation data from having called + * `account/3pid//requestToken` on the homeserver. + * @return {module:client.Promise} Resolves: on success + * @return {module:http-api.MatrixError} Rejects: with an error response. + */ +MatrixBaseApis.prototype.addThreePidOnly = function(data) { + const path = "/account/3pid/add"; + return this._http.authedRequest( + undefined, "POST", path, null, data, + ); +}; + +/** + * Bind a 3PID for discovery onto an identity server via the homeserver. The + * identity server handles 3PID ownership validation and the homeserver records + * the new binding to track where all 3PIDs for the account are bound. + * + * You can check whether a homeserver supports this API via + * `doesServerSupportSeparateAddAndBind`. + * + * @param {Object} data A object with 3PID validation data from having called + * `validate//requestToken` on the identity server. It should also + * contain `id_server` and `id_access_token` fields as well. + * @return {module:client.Promise} Resolves: on success + * @return {module:http-api.MatrixError} Rejects: with an error response. + */ +MatrixBaseApis.prototype.bindThreePid = function(data) { + const path = "/account/3pid/bind"; + return this._http.authedRequest( + undefined, "POST", path, null, data, + ); +}; + /** * @param {string} medium The threepid medium (eg. 'email') * @param {string} address The threepid address (eg. 'bob@example.com') From f256f04440a157d0ef7da15cbab996dc959c4d8c Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 18 Sep 2019 11:47:37 +0100 Subject: [PATCH 4/7] Add MSISDN validation API on the IS This API has existed for quite a while, but historically we've instead proxied this request via the homeserver. As part of MSC2290 work, we are changing approaches such that we will request tokens directly from the IS when binding for discovery. Part of https://github.com/vector-im/riot-web/issues/10839 --- src/base-apis.js | 81 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 3dc8ced2a..2892d9af7 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1803,10 +1803,11 @@ MatrixBaseApis.prototype.registerWithIdentityServer = function(hsOpenIdToken) { }; /** - * Requests an email verification token directly from an Identity Server. + * Requests an email verification token directly from an identity server. * - * Note that the Homeserver offers APIs to proxy this API for specific - * situations, allowing for better feedback to the user. + * This API is used as part of binding an email for discovery on an identity + * server. The validation data that results should be passed to the + * `bindThreePid` method to complete the binding process. * * @param {string} email The email address to request a token for * @param {string} clientSecret A secret binary string generated by the client. @@ -1818,12 +1819,12 @@ MatrixBaseApis.prototype.registerWithIdentityServer = function(hsOpenIdToken) { * @param {string} nextLink Optional If specified, the client will be redirected * to this link after validation. * @param {module:client.callback} callback Optional. - * @param {string} identityAccessToken The `access_token` field of the Identity - * Server `/account/register` response (see {@link registerWithIdentityServer}). + * @param {string} identityAccessToken The `access_token` field of the identity + * server `/account/register` response (see {@link registerWithIdentityServer}). * * @return {module:client.Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. - * @throws Error if no Identity Server is set + * @throws Error if no identity server is set */ MatrixBaseApis.prototype.requestEmailToken = async function( email, @@ -1864,6 +1865,74 @@ MatrixBaseApis.prototype.requestEmailToken = async function( } }; +/** + * Requests a MSISDN verification token directly from an identity server. + * + * This API is used as part of binding a MSISDN for discovery on an identity + * server. The validation data that results should be passed to the + * `bindThreePid` method to complete the binding process. + * + * @param {string} phoneCountry The ISO 3166-1 alpha-2 code for the country in + * which phoneNumber should be parsed relative to. + * @param {string} phoneNumber The phone number, in national or international + * format + * @param {string} clientSecret A secret binary string generated by the client. + * It is recommended this be around 16 ASCII characters. + * @param {number} sendAttempt If an identity server sees a duplicate request + * with the same sendAttempt, it will not send another SMS. + * To request another SMS to be sent, use a larger value for + * the sendAttempt param as was used in the previous request. + * @param {string} nextLink Optional If specified, the client will be redirected + * to this link after validation. + * @param {module:client.callback} callback Optional. + * @param {string} identityAccessToken The `access_token` field of the Identity + * Server `/account/register` response (see {@link registerWithIdentityServer}). + * + * @return {module:client.Promise} Resolves: TODO + * @return {module:http-api.MatrixError} Rejects: with an error response. + * @throws Error if no identity server is set + */ +MatrixBaseApis.prototype.requestMsisdnToken = async function( + phoneCountry, + phoneNumber, + clientSecret, + sendAttempt, + nextLink, + callback, + identityAccessToken, +) { + const params = { + client_secret: clientSecret, + country: phoneCountry, + phone_number: phoneNumber, + send_attempt: sendAttempt, + next_link: nextLink, + }; + + try { + const response = await this._http.idServerRequest( + undefined, "POST", "/validate/msisdn/requestToken", + params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken, + ); + // TODO: Fold callback into above call once v1 path below is removed + if (callback) callback(null, response); + return response; + } catch (err) { + if (err.cors === "rejected" || err.httpStatus === 404) { + // Fall back to deprecated v1 API for now + // TODO: Remove this path once v2 is only supported version + // See https://github.com/vector-im/riot-web/issues/10443 + logger.warn("IS doesn't support v2, falling back to deprecated v1"); + return await this._http.idServerRequest( + callback, "POST", "/validate/msisdn/requestToken", + params, httpApi.PREFIX_IDENTITY_V1, + ); + } + if (callback) callback(err); + throw err; + } +}; + /** * Submits an MSISDN token to the identity server * From 1ce4f2581152c5960e990bb5f68b310b698db244 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 19 Sep 2019 15:28:35 +0100 Subject: [PATCH 5/7] Use unstable prefix for add and bind --- src/base-apis.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 2892d9af7..3b71f4d5d 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1377,7 +1377,9 @@ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) { MatrixBaseApis.prototype.addThreePidOnly = function(data) { const path = "/account/3pid/add"; return this._http.authedRequest( - undefined, "POST", path, null, data, + undefined, "POST", path, null, data, { + prefix: httpApi.PREFIX_UNSTABLE, + }, ); }; @@ -1398,7 +1400,9 @@ MatrixBaseApis.prototype.addThreePidOnly = function(data) { MatrixBaseApis.prototype.bindThreePid = function(data) { const path = "/account/3pid/bind"; return this._http.authedRequest( - undefined, "POST", path, null, data, + undefined, "POST", path, null, data, { + prefix: httpApi.PREFIX_UNSTABLE, + }, ); }; From 65a1833e1f6662fed7f5e82969065c961aee2ecf Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 19 Sep 2019 15:28:58 +0100 Subject: [PATCH 6/7] Add 3PID unbind API --- src/base-apis.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/base-apis.js b/src/base-apis.js index 3b71f4d5d..49a650700 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1406,6 +1406,26 @@ MatrixBaseApis.prototype.bindThreePid = function(data) { ); }; +/** + * Unbind a 3PID for discovery on an identity server via the homeserver. The + * homeserver removes its record of the binding to keep an updated record of + * where all 3PIDs for the account are bound. + * + * @param {Object} data A object with 3PID validation data from having called + * `validate//requestToken` on the identity server. It should also + * contain `id_server` and `id_access_token` fields as well. + * @return {module:client.Promise} Resolves: on success + * @return {module:http-api.MatrixError} Rejects: with an error response. + */ +MatrixBaseApis.prototype.unbindThreePid = function(data) { + const path = "/account/3pid/unbind"; + return this._http.authedRequest( + undefined, "POST", path, null, data, { + prefix: httpApi.PREFIX_UNSTABLE, + }, + ); +}; + /** * @param {string} medium The threepid medium (eg. 'email') * @param {string} address The threepid address (eg. 'bob@example.com') From b54acffaef9ed7e8a44454b6f8c14a89639ae672 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 19 Sep 2019 17:28:50 +0100 Subject: [PATCH 7/7] Tweak unbind 3PID params --- src/base-apis.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 49a650700..f939e17c5 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1411,14 +1411,19 @@ MatrixBaseApis.prototype.bindThreePid = function(data) { * homeserver removes its record of the binding to keep an updated record of * where all 3PIDs for the account are bound. * - * @param {Object} data A object with 3PID validation data from having called - * `validate//requestToken` on the identity server. It should also - * contain `id_server` and `id_access_token` fields as well. + * @param {string} medium The threepid medium (eg. 'email') + * @param {string} address The threepid address (eg. 'bob@example.com') + * this must be as returned by getThreePids. * @return {module:client.Promise} Resolves: on success * @return {module:http-api.MatrixError} Rejects: with an error response. */ -MatrixBaseApis.prototype.unbindThreePid = function(data) { +MatrixBaseApis.prototype.unbindThreePid = function(medium, address) { const path = "/account/3pid/unbind"; + const data = { + medium, + address, + id_server: this.getIdentityServerUrl(true), + }; return this._http.authedRequest( undefined, "POST", path, null, data, { prefix: httpApi.PREFIX_UNSTABLE,