diff --git a/src/base-apis.js b/src/base-apis.js index 83cd35b16..988d3738c 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -17,8 +17,6 @@ limitations under the License. */ "use strict"; -import { SERVICE_TYPES } from './service-types'; - /** * This is an internal module. MatrixBaseApis is currently only meant to be used * by {@link client~MatrixClient}. @@ -26,6 +24,9 @@ import { SERVICE_TYPES } from './service-types'; * @module base-apis */ +import { SERVICE_TYPES } from './service-types'; +import logger from './logger'; + const httpApi = require("./http-api"); const utils = require("./utils"); @@ -1701,10 +1702,31 @@ MatrixBaseApis.prototype.getKeyChanges = function(oldToken, newToken) { // Identity Server Operations // ========================== +/** + * Register with an Identity Server using the OpenID token from the user's + * Homeserver, which can be retrieved via + * {@link module:client~MatrixClient#getOpenIdToken}. + * + * Note that the `/account/register` endpoint (as well as IS authentication in + * general) was added as part of the v2 API version. + * + * @param {object} hsOpenIdToken + * @return {module:client.Promise} Resolves: with object containing an Identity + * Server access token. + * @return {module:http-api.MatrixError} Rejects: with an error response. + */ +MatrixBaseApis.prototype.registerWithIdentityServer = function(hsOpenIdToken) { + const uri = this.idBaseUrl + httpApi.PREFIX_IDENTITY_V2 + "/account/register"; + return this._http.requestOtherUrl( + undefined, "POST", uri, + null, hsOpenIdToken, + ); +}; + /** * Requests an email verification token directly from an Identity Server. * - * Note that the Home Server offers APIs to proxy this API for specific + * Note that the Homeserver offers APIs to proxy this API for specific * situations, allowing for better feedback to the user. * * @param {string} email The email address to request a token for @@ -1717,22 +1739,50 @@ MatrixBaseApis.prototype.getKeyChanges = function(oldToken, newToken) { * @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 ID server is set + * @throws Error if no Identity Server is set */ -MatrixBaseApis.prototype.requestEmailToken = function(email, clientSecret, - sendAttempt, nextLink, callback) { +MatrixBaseApis.prototype.requestEmailToken = async function( + email, + clientSecret, + sendAttempt, + nextLink, + callback, + identityAccessToken, +) { const params = { client_secret: clientSecret, email: email, send_attempt: sendAttempt, next_link: nextLink, }; - return this._http.idServerRequest( - callback, "POST", "/validate/email/requestToken", - params, httpApi.PREFIX_IDENTITY_V1, - ); + + try { + const response = await this._http.idServerRequest( + undefined, "POST", "/validate/email/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/email/requestToken", + params, httpApi.PREFIX_IDENTITY_V1, + ); + } + if (callback) callback(err); + throw err; + } }; /** @@ -1746,44 +1796,94 @@ MatrixBaseApis.prototype.requestEmailToken = function(email, clientSecret, * @param {string} sid The sid given in the response to requestToken * @param {string} clientSecret A secret binary string generated by the client. * This must be the same value submitted in the requestToken call. - * @param {string} token The token, as enetered by the user. + * @param {string} msisdnToken The MSISDN token, as enetered by the user. + * @param {string} identityAccessToken The `access_token` field of the Identity + * Server `/account/register` response (see {@link registerWithIdentityServer}). * * @return {module:client.Promise} Resolves: Object, currently with no parameters. * @return {module:http-api.MatrixError} Rejects: with an error response. * @throws Error if No ID server is set */ -MatrixBaseApis.prototype.submitMsisdnToken = function(sid, clientSecret, token) { +MatrixBaseApis.prototype.submitMsisdnToken = async function( + sid, + clientSecret, + msisdnToken, + identityAccessToken, +) { const params = { sid: sid, client_secret: clientSecret, - token: token, + token: msisdnToken, }; - return this._http.idServerRequest( - undefined, "POST", "/validate/msisdn/submitToken", - params, httpApi.PREFIX_IDENTITY_V1, - ); + + try { + return await this._http.idServerRequest( + undefined, "POST", "/validate/msisdn/submitToken", + params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken, + ); + } 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( + undefined, "POST", "/validate/msisdn/submitToken", + params, httpApi.PREFIX_IDENTITY_V1, + ); + } + throw err; + } }; /** * Looks up the public Matrix ID mapping for a given 3rd party * identifier from the Identity Server + * * @param {string} medium The medium of the threepid, eg. 'email' * @param {string} address The textual address of the threepid * @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: A threepid mapping * object or the empty object if no mapping * exists * @return {module:http-api.MatrixError} Rejects: with an error response. */ -MatrixBaseApis.prototype.lookupThreePid = function(medium, address, callback) { +MatrixBaseApis.prototype.lookupThreePid = async function( + medium, + address, + callback, + identityAccessToken, +) { const params = { medium: medium, address: address, }; - return this._http.idServerRequest( - callback, "GET", "/lookup", - params, httpApi.PREFIX_IDENTITY_V1, - ); + + try { + const response = await this._http.idServerRequest( + undefined, "GET", "/lookup", + 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, "GET", "/lookup", + params, httpApi.PREFIX_IDENTITY_V1, + ); + } + if (callback) callback(err); + throw err; + } }; diff --git a/src/http-api.js b/src/http-api.js index 374c00644..9271b9bae 100644 --- a/src/http-api.js +++ b/src/http-api.js @@ -374,7 +374,14 @@ module.exports.MatrixHttpApi.prototype = { return this.uploads; }, - idServerRequest: function(callback, method, path, params, prefix) { + idServerRequest: function( + callback, + method, + path, + params, + prefix, + accessToken, + ) { const fullUri = this.opts.idBaseUrl + prefix + path; if (callback !== undefined && !utils.isFunction(callback)) { @@ -395,6 +402,11 @@ module.exports.MatrixHttpApi.prototype = { } else { opts.form = params; } + if (accessToken) { + opts.headers = { + Authorization: `Bearer ${accessToken}`, + }; + } const defer = Promise.defer(); this.opts.request(