You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
Use the v2 (hashed) lookup for identity server queries
Fixes https://github.com/vector-im/riot-web/issues/10556 Implements [MSC2134](https://github.com/matrix-org/matrix-doc/pull/2134) with assistance from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) for auth. Note: this also changes all identity server requests to use JSON as a request body. URL encoded forms were allowed in v1 but deprecated in favour of JSON. v2 APIs do not allow URL encoded forms.
This commit is contained in:
161
src/base-apis.js
161
src/base-apis.js
@@ -1857,6 +1857,93 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the V2 hashing information from the identity server. Primarily useful for
|
||||||
|
* lookups.
|
||||||
|
* @param identityAccessToken The access token for the identity server.
|
||||||
|
* @returns {Promise<object>} The hashing information for the identity server.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.getIdentityHashDetails = function(identityAccessToken) {
|
||||||
|
return this._http.idServerRequest(
|
||||||
|
undefined, "GET", "/hash_details",
|
||||||
|
null, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a hashed lookup of addresses against the identity server. This is
|
||||||
|
* only supported on identity servers which have at least the version 2 API.
|
||||||
|
* @param addressPairs An array of 2 element arrays. The first element of each
|
||||||
|
* pair is the address, the second is the 3PID medium. Eg: ["email@example.org",
|
||||||
|
* "email"]
|
||||||
|
* @param identityAccessToken The access token for the identity server.
|
||||||
|
* @returns {Promise<{address, mxid}[]>} A collection of address mappings to
|
||||||
|
* found MXIDs. Results where no user could be found will not be listed.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.identityHashedLookup = async function(
|
||||||
|
addressPairs, // [["email@example.org", "email"], ["10005550000", "msisdn"]]
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
|
const params = {
|
||||||
|
// addresses: ["email@example.org", "10005550000"],
|
||||||
|
// algorithm: "sha256",
|
||||||
|
// pepper: "abc123"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get hash information first before trying to do a lookup
|
||||||
|
const hashes = await this.getIdentityHashDetails();
|
||||||
|
if (!hashes || !hashes['lookup_pepper'] || !hashes['algorithms']) {
|
||||||
|
throw new Error("Unsupported identity server: bad response");
|
||||||
|
}
|
||||||
|
|
||||||
|
params['pepper'] = hashes['lookup_pepper'];
|
||||||
|
|
||||||
|
const localMapping = {
|
||||||
|
// hashed identifier => plain text address
|
||||||
|
// For use in this function's return format
|
||||||
|
};
|
||||||
|
|
||||||
|
// When picking an algorithm, we pick the hashed over no hashes
|
||||||
|
if (hashes['algorithms'].includes('sha256')) {
|
||||||
|
// Abuse the olm hashing
|
||||||
|
const olmutil = new global.Olm.Utility();
|
||||||
|
params["addresses"] = addressPairs.map(p => {
|
||||||
|
const hashed = olmutil.sha256(`${p[0]} ${p[1]} ${params['pepper']}`);
|
||||||
|
localMapping[hashed] = p[0];
|
||||||
|
return hashed;
|
||||||
|
});
|
||||||
|
params["algorithm"] = "sha256";
|
||||||
|
} else if (hashes['algorithms'].includes('none')) {
|
||||||
|
params["addresses"] = addressPairs.map(p => {
|
||||||
|
const unhashed = `${p[0]} ${p[1]}`;
|
||||||
|
localMapping[unhashed] = p[0];
|
||||||
|
return unhashed;
|
||||||
|
});
|
||||||
|
params["algorithm"] = "none";
|
||||||
|
} else {
|
||||||
|
throw new Error("Unsupported identity server: unknown hash algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this._http.idServerRequest(
|
||||||
|
undefined, "POST", "/lookup",
|
||||||
|
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response || !response['mappings']) return []; // no results
|
||||||
|
|
||||||
|
const foundAddresses = [/* {address: "plain@example.org", mxid} */];
|
||||||
|
for (const hashed of Object.keys(response['mappings'])) {
|
||||||
|
const mxid = response['mappings'][hashed];
|
||||||
|
const plainAddress = localMapping[hashed];
|
||||||
|
if (!plainAddress) {
|
||||||
|
throw new Error("Identity server returned more results than expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
foundAddresses.push({address: plainAddress, mxid});
|
||||||
|
}
|
||||||
|
return foundAddresses;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the public Matrix ID mapping for a given 3rd party
|
* Looks up the public Matrix ID mapping for a given 3rd party
|
||||||
* identifier from the Identity Server
|
* identifier from the Identity Server
|
||||||
@@ -1878,31 +1965,51 @@ MatrixBaseApis.prototype.lookupThreePid = async function(
|
|||||||
callback,
|
callback,
|
||||||
identityAccessToken,
|
identityAccessToken,
|
||||||
) {
|
) {
|
||||||
const params = {
|
|
||||||
medium: medium,
|
|
||||||
address: address,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this._http.idServerRequest(
|
// Note: we're using the V2 API by calling this function, but our
|
||||||
undefined, "GET", "/lookup",
|
// function contract requires a V1 response. We therefore have to
|
||||||
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
// convert it manually.
|
||||||
|
const response = await this.identityHashedLookup(
|
||||||
|
[[address, medium]], identityAccessToken,
|
||||||
);
|
);
|
||||||
|
const result = response.find(p => p.address === address);
|
||||||
|
if (!result) {
|
||||||
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
|
if (callback) callback(null, {});
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapping = {
|
||||||
|
address,
|
||||||
|
medium,
|
||||||
|
mxid: result.mxid,
|
||||||
|
|
||||||
|
// We can't reasonably fill these parameters:
|
||||||
|
// not_before
|
||||||
|
// not_after
|
||||||
|
// ts
|
||||||
|
// signatures
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Fold callback into above call once v1 path below is removed
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
if (callback) callback(null, response);
|
if (callback) callback(null, mapping);
|
||||||
return response;
|
return mapping;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.cors === "rejected" || err.httpStatus === 404) {
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
// Fall back to deprecated v1 API for now
|
// Fall back to deprecated v1 API for now
|
||||||
// TODO: Remove this path once v2 is only supported version
|
// TODO: Remove this path once v2 is only supported version
|
||||||
// See https://github.com/vector-im/riot-web/issues/10443
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
|
const params = {
|
||||||
|
medium: medium,
|
||||||
|
address: address,
|
||||||
|
};
|
||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
callback, "GET", "/lookup",
|
callback, "GET", "/lookup",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, httpApi.PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (callback) callback(err);
|
if (callback) callback(err, undefined);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1922,20 +2029,38 @@ MatrixBaseApis.prototype.bulkLookupThreePids = async function(
|
|||||||
query,
|
query,
|
||||||
identityAccessToken,
|
identityAccessToken,
|
||||||
) {
|
) {
|
||||||
const params = {
|
|
||||||
threepids: query,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this._http.idServerRequest(
|
// Note: we're using the V2 API by calling this function, but our
|
||||||
undefined, "POST", "/bulk_lookup", JSON.stringify(params),
|
// function contract requires a V1 response. We therefore have to
|
||||||
httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
// convert it manually.
|
||||||
|
const response = await this.identityHashedLookup(
|
||||||
|
// We have to reverse the query order to get [address, medium] pairs
|
||||||
|
query.map(p => [p[1], p[0]]), identityAccessToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const v1results = [];
|
||||||
|
for (const mapping of response) {
|
||||||
|
const originalQuery = query.find(p => p[1] === mapping.address);
|
||||||
|
if (!originalQuery) {
|
||||||
|
throw new Error("Identity sever returned unexpected results");
|
||||||
|
}
|
||||||
|
|
||||||
|
v1results.push([
|
||||||
|
originalQuery[0], // medium
|
||||||
|
mapping.address,
|
||||||
|
mapping.mxid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {threepids: v1results};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.cors === "rejected" || err.httpStatus === 404) {
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
// Fall back to deprecated v1 API for now
|
// Fall back to deprecated v1 API for now
|
||||||
// TODO: Remove this path once v2 is only supported version
|
// TODO: Remove this path once v2 is only supported version
|
||||||
// See https://github.com/vector-im/riot-web/issues/10443
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
|
const params = {
|
||||||
|
threepids: query,
|
||||||
|
};
|
||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
undefined, "POST", "/bulk_lookup", JSON.stringify(params),
|
undefined, "POST", "/bulk_lookup", JSON.stringify(params),
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
if (method == 'GET') {
|
if (method == 'GET') {
|
||||||
opts.qs = params;
|
opts.qs = params;
|
||||||
} else if (typeof params === "object") {
|
} else if (typeof params === "object") {
|
||||||
opts.form = params;
|
opts.json = params;
|
||||||
} else if (typeof params === "string") {
|
} else if (typeof params === "string") {
|
||||||
// Assume the caller has serialised the body to JSON
|
// Assume the caller has serialised the body to JSON
|
||||||
opts.body = params;
|
opts.body = params;
|
||||||
|
|||||||
Reference in New Issue
Block a user