Skip to content
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

Add separate 3PID add and bind APIs #1038

Merged
merged 7 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 151 additions & 7 deletions src/base-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -1356,6 +1362,75 @@ 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/<medium>/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, {
prefix: httpApi.PREFIX_UNSTABLE,
},
);
};

/**
* 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/<medium>/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, {
prefix: httpApi.PREFIX_UNSTABLE,
},
);
};

/**
* 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 {string} medium The threepid medium (eg. 'email')
* @param {string} address The threepid address (eg. '[email protected]')
* 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(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,
},
);
};

/**
* @param {string} medium The threepid medium (eg. 'email')
* @param {string} address The threepid address (eg. '[email protected]')
Expand Down Expand Up @@ -1757,10 +1832,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.
Expand All @@ -1772,12 +1848,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,
Expand Down Expand Up @@ -1818,6 +1894,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
*
Expand Down
28 changes: 22 additions & 6 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<object>} The server /versions response
Expand All @@ -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<boolean>} true if server supports lazy loading
*/
Expand All @@ -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<boolean>} true if id_server parameter is required
Expand All @@ -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.
Expand All @@ -4227,15 +4227,31 @@ 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<boolean>} 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
*/
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
Expand Down