From 60a5bb631f93b685a040e7f592914f43cae2f870 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 26 Sep 2025 15:47:41 -0300 Subject: [PATCH 1/3] feat(federation): add verifyMatrixIds method and API endpoint for Matrix ID verification --- apps/meteor/ee/server/api/federation.ts | 31 ++++++++++++++ .../federation-matrix/src/FederationMatrix.ts | 41 +++++++++++++++++++ .../src/types/IFederationMatrixService.ts | 1 + 3 files changed, 73 insertions(+) diff --git a/apps/meteor/ee/server/api/federation.ts b/apps/meteor/ee/server/api/federation.ts index 278282ed0ec1b..456db9ebbd9c1 100644 --- a/apps/meteor/ee/server/api/federation.ts +++ b/apps/meteor/ee/server/api/federation.ts @@ -1,13 +1,44 @@ import type { IFederationMatrixService } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; +import { ajv } from '@rocket.chat/rest-typings'; import type express from 'express'; import { WebApp } from 'meteor/webapp'; +import { API } from '../../../app/api/server'; import { isRunningMs } from '../../../server/lib/isRunningMs'; const logger = new Logger('FederationRoutes'); export async function registerFederationRoutes(federationService: IFederationMatrixService): Promise { + API.v1.get( + '/federation/matrixIds.verify', + { + authRequired: true, + query: ajv.compile<{ + matrixIds: string[]; + }>({ + type: 'object', + properties: { + matrixIds: { type: 'array', items: { type: 'string' } }, + }, + }), + response: { + 200: ajv.compile({ + type: 'object', + properties: { + results: { type: 'array', items: { type: 'string' } }, + }, + }), + }, + }, + async function () { + const { matrixIds } = this.queryParams; + return API.v1.success({ + results: await federationService.verifyMatrixIds(matrixIds), + }); + }, + ); + if (isRunningMs()) { return; } diff --git a/ee/packages/federation-matrix/src/FederationMatrix.ts b/ee/packages/federation-matrix/src/FederationMatrix.ts index ea0f33eb5dfb6..17fde40b78d5f 100644 --- a/ee/packages/federation-matrix/src/FederationMatrix.ts +++ b/ee/packages/federation-matrix/src/FederationMatrix.ts @@ -944,4 +944,45 @@ export class FederationMatrix extends ServiceClass implements IFederationMatrixS void this.homeserverServices.edu.sendTypingNotification(room.federation.mrid, userMui, isTyping); } + + async verifyMatrixIds(matrixIds: string[]): Promise<{ [key: string]: string }> { + const results = Object.fromEntries( + await Promise.all( + matrixIds.map(async (matrixId) => { + const [userId, homeserverUrl] = matrixId.split(':'); + + if (homeserverUrl === this.serverName) { + const user = await Users.findOneByUsername(userId.slice(1)); + return [matrixId, user ? 'VERIFIED' : 'UNVERIFIED']; + } + + if (!homeserverUrl) { + return [matrixId, 'UNABLE_TO_VERIFY']; + } + try { + const result = await this.homeserverServices.request.get< + | { + avatar_url: string; + displayname: string; + } + | { + errcode: string; + error: string; + } + >(homeserverUrl, `/_matrix/federation/v1/query/profile`, { user_id: matrixId }); + + if ('errcode' in result && result.errcode === 'M_NOT_FOUND') { + return [matrixId, 'UNVERIFIED']; + } + + return [matrixId, 'VERIFIED']; + } catch (e) { + return [matrixId, 'UNABLE_TO_VERIFY']; + } + }), + ), + ); + + return results; + } } diff --git a/packages/core-services/src/types/IFederationMatrixService.ts b/packages/core-services/src/types/IFederationMatrixService.ts index 893bca7be5f3c..c44cb226dd695 100644 --- a/packages/core-services/src/types/IFederationMatrixService.ts +++ b/packages/core-services/src/types/IFederationMatrixService.ts @@ -27,4 +27,5 @@ export interface IFederationMatrixService { ): Promise; inviteUsersToRoom(room: IRoomFederated, usersUserName: string[], inviter: IUser): Promise; notifyUserTyping(rid: string, user: string, isTyping: boolean): Promise; + verifyMatrixIds(matrixIds: string[]): Promise<{ [key: string]: string }>; } From d983a85bb133bd77fc61ff6f84c23b4eee1b38fd Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 26 Sep 2025 16:07:53 -0300 Subject: [PATCH 2/3] fix(federation): ensure federation service is registered before verifying Matrix IDs --- apps/meteor/ee/server/api/federation.ts | 53 ++++++++++++++----------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/apps/meteor/ee/server/api/federation.ts b/apps/meteor/ee/server/api/federation.ts index 456db9ebbd9c1..e0b99cd81852c 100644 --- a/apps/meteor/ee/server/api/federation.ts +++ b/apps/meteor/ee/server/api/federation.ts @@ -9,36 +9,43 @@ import { isRunningMs } from '../../../server/lib/isRunningMs'; const logger = new Logger('FederationRoutes'); -export async function registerFederationRoutes(federationService: IFederationMatrixService): Promise { - API.v1.get( - '/federation/matrixIds.verify', - { - authRequired: true, - query: ajv.compile<{ - matrixIds: string[]; +let federationService: IFederationMatrixService | undefined; +API.v1.get( + '/federation/matrixIds.verify', + { + authRequired: true, + query: ajv.compile<{ + matrixIds: string[]; + }>({ + type: 'object', + properties: { + matrixIds: { type: 'array', items: { type: 'string' } }, + }, + }), + response: { + 200: ajv.compile<{ + results: { [key: string]: string }; }>({ type: 'object', properties: { - matrixIds: { type: 'array', items: { type: 'string' } }, + results: { type: 'object', additionalProperties: { type: 'string' } }, }, }), - response: { - 200: ajv.compile({ - type: 'object', - properties: { - results: { type: 'array', items: { type: 'string' } }, - }, - }), - }, - }, - async function () { - const { matrixIds } = this.queryParams; - return API.v1.success({ - results: await federationService.verifyMatrixIds(matrixIds), - }); }, - ); + }, + async function () { + const { matrixIds } = this.queryParams; + if (!federationService) { + throw new Error('Federation service not registered'); + } + return API.v1.success({ + results: await federationService.verifyMatrixIds(matrixIds), + }); + }, +); +export async function registerFederationRoutes(f: IFederationMatrixService): Promise { + federationService = f; if (isRunningMs()) { return; } From 6ec745827a6e644051b19be25c4dec85f8d399b0 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 26 Sep 2025 17:03:05 -0300 Subject: [PATCH 3/3] Update ee/packages/federation-matrix/src/FederationMatrix.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- ee/packages/federation-matrix/src/FederationMatrix.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ee/packages/federation-matrix/src/FederationMatrix.ts b/ee/packages/federation-matrix/src/FederationMatrix.ts index 17fde40b78d5f..b185cc89376cf 100644 --- a/ee/packages/federation-matrix/src/FederationMatrix.ts +++ b/ee/packages/federation-matrix/src/FederationMatrix.ts @@ -949,7 +949,13 @@ export class FederationMatrix extends ServiceClass implements IFederationMatrixS const results = Object.fromEntries( await Promise.all( matrixIds.map(async (matrixId) => { - const [userId, homeserverUrl] = matrixId.split(':'); + // Split only on the first ':' (after the leading '@') so we keep any port in the homeserver + const separatorIndex = matrixId.indexOf(':', 1); + if (separatorIndex === -1) { + return [matrixId, 'UNABLE_TO_VERIFY']; + } + const userId = matrixId.slice(0, separatorIndex); + const homeserverUrl = matrixId.slice(separatorIndex + 1); if (homeserverUrl === this.serverName) { const user = await Users.findOneByUsername(userId.slice(1));