diff --git a/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.test.ts b/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.test.ts index 95d88f7b8b9..c08a5f00e7e 100644 --- a/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.test.ts +++ b/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.test.ts @@ -257,4 +257,22 @@ describe("MatrixCryptoBootstrapper", () => { ); expect(verificationRequest.accept).toHaveBeenCalledTimes(1); }); + + it("registers verification listeners only once across repeated bootstrap calls", async () => { + const deps = createBootstrapperDeps(); + const crypto = createCryptoApi({ + getDeviceVerificationStatus: vi.fn(async () => ({ + isVerified: () => true, + })), + }); + const bootstrapper = new MatrixCryptoBootstrapper( + deps as unknown as MatrixCryptoBootstrapperDeps, + ); + + await bootstrapper.bootstrap(crypto); + await bootstrapper.bootstrap(crypto); + + expect(crypto.on).toHaveBeenCalledTimes(1); + expect(deps.decryptBridge.bindCryptoRetrySignals).toHaveBeenCalledTimes(1); + }); }); diff --git a/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.ts b/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.ts index 6c756aa32d9..4a737dab7d4 100644 --- a/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.ts +++ b/extensions/matrix-js/src/matrix/sdk/crypto-bootstrap.ts @@ -34,6 +34,8 @@ export type MatrixCryptoBootstrapResult = { }; export class MatrixCryptoBootstrapper { + private verificationHandlerRegistered = false; + constructor(private readonly deps: MatrixCryptoBootstrapperDeps) {} async bootstrap( @@ -41,6 +43,9 @@ export class MatrixCryptoBootstrapper { options: MatrixCryptoBootstrapOptions = {}, ): Promise { const strict = options.strict === true; + // Register verification listeners before expensive bootstrap work so incoming requests + // are not missed during startup. + this.registerVerificationRequestHandler(crypto); await this.bootstrapSecretStorage(crypto, strict); const crossSigning = await this.bootstrapCrossSigning(crypto, { forceResetCrossSigning: options.forceResetCrossSigning === true, @@ -48,7 +53,6 @@ export class MatrixCryptoBootstrapper { }); await this.bootstrapSecretStorage(crypto, strict); const ownDeviceVerified = await this.ensureOwnDeviceTrust(crypto, strict); - this.registerVerificationRequestHandler(crypto); return { crossSigningReady: crossSigning.ready, crossSigningPublished: crossSigning.published, @@ -209,6 +213,11 @@ export class MatrixCryptoBootstrapper { } private registerVerificationRequestHandler(crypto: MatrixCryptoBootstrapApi): void { + if (this.verificationHandlerRegistered) { + return; + } + this.verificationHandlerRegistered = true; + // Auto-accept incoming verification requests from other users/devices. crypto.on(CryptoEvent.VerificationRequestReceived, async (request) => { const verificationRequest = request as MatrixVerificationRequestLike;