From 9b093f7569b88068b9cf740f5e25f40770084e54 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 29 Jul 2019 13:15:17 +0100 Subject: [PATCH 1/5] Add first pass of IS v2 API with authentication This only updates the `/lookup` API so far. It also doesn't handle falling back to v1. --- src/base-apis.js | 36 ++++++++++++++++++++++++++++++++++-- src/http-api.js | 14 +++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 83cd35b16..23721c2f3 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1701,6 +1701,27 @@ 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. * @@ -1767,22 +1788,33 @@ MatrixBaseApis.prototype.submitMsisdnToken = function(sid, clientSecret, token) /** * 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} isAccessToken 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 = function( + medium, + address, + callback, + isAccessToken, +) { const params = { medium: medium, address: address, }; + + // TODO: Testing only - add fallback to v1 return this._http.idServerRequest( callback, "GET", "/lookup", - params, httpApi.PREFIX_IDENTITY_V1, + params, httpApi.PREFIX_IDENTITY_V2, isAccessToken, ); }; 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( From 91416bdbb22c67acbfa111ab2b2635d09c05beb0 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 29 Jul 2019 14:44:15 +0100 Subject: [PATCH 2/5] Add IS v1 API fallback for lookup --- src/base-apis.js | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 23721c2f3..7858baacc 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"); @@ -1800,7 +1801,7 @@ MatrixBaseApis.prototype.submitMsisdnToken = function(sid, clientSecret, token) * exists * @return {module:http-api.MatrixError} Rejects: with an error response. */ -MatrixBaseApis.prototype.lookupThreePid = function( +MatrixBaseApis.prototype.lookupThreePid = async function( medium, address, callback, @@ -1811,11 +1812,26 @@ MatrixBaseApis.prototype.lookupThreePid = function( address: address, }; - // TODO: Testing only - add fallback to v1 - return this._http.idServerRequest( - callback, "GET", "/lookup", - params, httpApi.PREFIX_IDENTITY_V2, isAccessToken, - ); + try { + const response = await this._http.idServerRequest( + undefined, "GET", "/lookup", + params, httpApi.PREFIX_IDENTITY_V2, isAccessToken, + ); + // 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 + 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; + } }; From 6f042a2142c589591d3dace83218a93af1afa1d5 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 29 Jul 2019 14:55:37 +0100 Subject: [PATCH 3/5] Add IS v2 support to other IS APIs This adds v2 support with fallback to other IS APIs in the SDK. --- src/base-apis.js | 79 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 7858baacc..085db1e11 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1726,7 +1726,7 @@ MatrixBaseApis.prototype.registerWithIdentityServer = function(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 @@ -1739,22 +1739,49 @@ 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} isAccessToken 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, + isAccessToken, +) { 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, isAccessToken, + ); + // 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 + 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; + } }; /** @@ -1768,22 +1795,43 @@ 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} isAccessToken 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, + isAccessToken, +) { 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, isAccessToken, + ); + } 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 + 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; + } }; /** @@ -1823,6 +1871,7 @@ MatrixBaseApis.prototype.lookupThreePid = async function( } 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 logger.warn("IS doesn't support v2, falling back to deprecated v1"); return await this._http.idServerRequest( callback, "GET", "/lookup", From 516dff06ee30ad6ea9f7078d49a87fbcf056c5a6 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 30 Jul 2019 10:06:52 +0100 Subject: [PATCH 4/5] Rename isAccessToken to identityAccessToken --- src/base-apis.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/base-apis.js b/src/base-apis.js index 085db1e11..4a62496f4 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1739,8 +1739,8 @@ 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} isAccessToken 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. @@ -1752,7 +1752,7 @@ MatrixBaseApis.prototype.requestEmailToken = async function( sendAttempt, nextLink, callback, - isAccessToken, + identityAccessToken, ) { const params = { client_secret: clientSecret, @@ -1764,7 +1764,7 @@ MatrixBaseApis.prototype.requestEmailToken = async function( try { const response = await this._http.idServerRequest( undefined, "POST", "/validate/email/requestToken", - params, httpApi.PREFIX_IDENTITY_V2, isAccessToken, + params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken, ); // TODO: Fold callback into above call once v1 path below is removed if (callback) callback(null, response); @@ -1796,8 +1796,8 @@ MatrixBaseApis.prototype.requestEmailToken = async function( * @param {string} clientSecret A secret binary string generated by the client. * This must be the same value submitted in the requestToken call. * @param {string} msisdnToken The MSISDN token, as enetered by the user. - * @param {string} isAccessToken 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: Object, currently with no parameters. * @return {module:http-api.MatrixError} Rejects: with an error response. @@ -1807,7 +1807,7 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function( sid, clientSecret, msisdnToken, - isAccessToken, + identityAccessToken, ) { const params = { sid: sid, @@ -1818,7 +1818,7 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function( try { return await this._http.idServerRequest( undefined, "POST", "/validate/msisdn/submitToken", - params, httpApi.PREFIX_IDENTITY_V2, isAccessToken, + params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken, ); } catch (err) { if (err.cors === "rejected" || err.httpStatus === 404) { @@ -1841,8 +1841,8 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function( * @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} isAccessToken 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: A threepid mapping * object or the empty object if no mapping @@ -1853,7 +1853,7 @@ MatrixBaseApis.prototype.lookupThreePid = async function( medium, address, callback, - isAccessToken, + identityAccessToken, ) { const params = { medium: medium, @@ -1863,7 +1863,7 @@ MatrixBaseApis.prototype.lookupThreePid = async function( try { const response = await this._http.idServerRequest( undefined, "GET", "/lookup", - params, httpApi.PREFIX_IDENTITY_V2, isAccessToken, + params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken, ); // TODO: Fold callback into above call once v1 path below is removed if (callback) callback(null, response); From 50c590ae265cb271342d76f4dee18b73d782d6a4 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 30 Jul 2019 10:38:53 +0100 Subject: [PATCH 5/5] Note cleanup issue --- src/base-apis.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/base-apis.js b/src/base-apis.js index 4a62496f4..988d3738c 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1773,6 +1773,7 @@ MatrixBaseApis.prototype.requestEmailToken = async function( 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", @@ -1824,6 +1825,7 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function( 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", @@ -1872,6 +1874,7 @@ MatrixBaseApis.prototype.lookupThreePid = async function( 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",