diff --git a/cypress/e2e/crypto/verification.spec.ts b/cypress/e2e/crypto/verification.spec.ts index d9b4bb6e53..0bc6c71034 100644 --- a/cypress/e2e/crypto/verification.spec.ts +++ b/cypress/e2e/crypto/verification.spec.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import jsQR from "jsqr"; + import type { VerificationRequest, Verifier } from "matrix-js-sdk/src/crypto-api/verification"; import { CypressBot } from "../../support/bot"; import { HomeserverInstance } from "../../plugins/utils/homeserver"; @@ -21,6 +23,24 @@ import { emitPromise } from "../../support/util"; import { checkDeviceIsCrossSigned, doTwoWaySasVerification, logIntoElement, waitForVerificationRequest } from "./utils"; import { getToast } from "../../support/toasts"; +/** Render a data URL and return the rendered image data */ +async function renderQRCode(dataUrl: string): Promise { + // create a new image and set the source to the data url + const img = new Image(); + await new Promise((r) => { + img.onload = r; + img.src = dataUrl; + }); + + // draw the image on a canvas + const myCanvas = new OffscreenCanvas(256, 256); + const ctx = myCanvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + + // read the image data + return ctx.getImageData(0, 0, myCanvas.width, myCanvas.height); +} + describe("Device verification", () => { let aliceBotClient: CypressBot; let homeserver: HomeserverInstance; @@ -91,6 +111,49 @@ describe("Device verification", () => { checkDeviceIsCrossSigned(); }); + it("Verify device during login with QR code", () => { + logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password); + + // Launch the verification request between alice and the bot + initiateAliceVerificationRequest(); + + cy.get(".mx_InfoDialog").within(() => { + cy.get('[alt="QR Code"]').then((qrCode) => { + /* the bot scans the QR code */ + cy.get("@verificationRequest") + .then(async (request: VerificationRequest) => { + // because I don't know how to scrape the imagedata from the cypress browser window, + // we extract the data url and render it to a new canvas. + const imageData = await renderQRCode(qrCode.attr("src")); + + // now we can decode the QR code... + const result = jsQR(imageData.data, imageData.width, imageData.height); + + // ... and feed it into the verification request. + return await request.scanQRCode(new Uint8Array(result.binaryData)); + }) + .as("verifier"); + }); + + // Confirm that the bot user scanned successfully + cy.findByText("Almost there! Is your other device showing the same shield?"); + cy.findByRole("button", { name: "Yes" }).click(); + + cy.findByRole("button", { name: "Got it" }).click(); + }); + + // wait for the bot to see we have finished + cy.get("@verifier").then(async (verifier) => { + await verifier.verify(); + }); + + // the bot uploads the signatures asynchronously, so wait for that to happen + cy.wait(1000); + + // Check that our device is now cross-signed + checkDeviceIsCrossSigned(); + }); + it("Verify device during login with Security Phrase", () => { logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password); diff --git a/package.json b/package.json index df29cd7e50..44546c535c 100644 --- a/package.json +++ b/package.json @@ -203,6 +203,7 @@ "jest-environment-jsdom": "^29.2.2", "jest-mock": "^29.2.2", "jest-raw-loader": "^1.0.1", + "jsqr": "^1.4.0", "matrix-mock-request": "^2.5.0", "matrix-web-i18n": "^1.4.0", "mocha-junit-reporter": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index 73988d9a89..d16f2d5092 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6497,6 +6497,11 @@ jsprim@^2.0.2: json-schema "0.4.0" verror "1.10.0" +jsqr@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.4.0.tgz#8efb8d0a7cc6863cb6d95116b9069123ce9eb2d1" + integrity sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A== + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea"