Prevent skipping forced verification after logging in with OIDC (#30141)
Pass the freshLogin parameter along to doSetLoggedIn when restoring a session, instead of hard-coding it to always be false.
This commit is contained in:
@@ -11,6 +11,9 @@ import { type Page } from "@playwright/test";
|
||||
|
||||
import { expect } from "../../element-web-test";
|
||||
|
||||
/**
|
||||
* Click through registering a new user in the MAS UI.
|
||||
*/
|
||||
export async function registerAccountMas(
|
||||
page: Page,
|
||||
mailpit: MailpitClient,
|
||||
@@ -42,3 +45,17 @@ export async function registerAccountMas(
|
||||
await expect(page.getByText("Allow access to your account?")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Click through entering username and password into the MAS login prompt.
|
||||
*/
|
||||
export async function logInAccountMas(page: Page, username: string, password: string): Promise<void> {
|
||||
await expect(page.getByText("Please sign in to continue:")).toBeVisible();
|
||||
|
||||
await page.getByRole("textbox", { name: "Username" }).fill(username);
|
||||
await page.getByRole("textbox", { name: "Password", exact: true }).fill(password);
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
await expect(page.getByText("Allow access to your account?")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type Config, CONFIG_JSON } from "@element-hq/element-web-playwright-common";
|
||||
import { type Browser, type Page } from "@playwright/test";
|
||||
import { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/HomeserverContainer";
|
||||
|
||||
import { test, expect } from "../../element-web-test.ts";
|
||||
import { registerAccountMas } from ".";
|
||||
import { logInAccountMas, registerAccountMas } from ".";
|
||||
import { ElementAppPage } from "../../pages/ElementAppPage.ts";
|
||||
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
|
||||
|
||||
@@ -101,4 +105,154 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
expect(localStorageKeys).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
test("can log in to an existing MAS account", { tag: "@screenshot" }, async ({ page, mailpitClient }, testInfo) => {
|
||||
// Register an account with MAS
|
||||
await page.goto("/#/login");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
const userId = `alice_${testInfo.testId}`;
|
||||
await registerAccountMas(page, mailpitClient, userId, `${userId}@email.com`, "Pa$sW0rD!");
|
||||
await expect(page.getByText("Welcome")).toBeVisible();
|
||||
|
||||
// Log out
|
||||
await page.getByRole("button", { name: "User menu" }).click();
|
||||
await expect(page.getByText(userId, { exact: true })).toBeVisible();
|
||||
|
||||
// Allow the outstanding requests queue to settle before logging out
|
||||
await page.waitForTimeout(2000);
|
||||
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
|
||||
await expect(page).toHaveURL(/\/#\/login$/);
|
||||
|
||||
// Log in again
|
||||
await page.goto("/#/login");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// We should be in (we see an error because we have no recovery key).
|
||||
await expect(page.getByText("Unable to verify this device")).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("with force_verification on", () => {
|
||||
test.use({
|
||||
config: {
|
||||
force_verification: true,
|
||||
},
|
||||
});
|
||||
|
||||
test("verify dialog cannot be dismissed", { tag: "@screenshot" }, async ({ page, mailpitClient }, testInfo) => {
|
||||
// Register an account with MAS
|
||||
await page.goto("/#/login");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
const userId = `alice_${testInfo.testId}`;
|
||||
await registerAccountMas(page, mailpitClient, userId, `${userId}@email.com`, "Pa$sW0rD!");
|
||||
await expect(page.getByText("Welcome")).toBeVisible();
|
||||
|
||||
// Log out
|
||||
await page.getByRole("button", { name: "User menu" }).click();
|
||||
await expect(page.getByText(userId, { exact: true })).toBeVisible();
|
||||
await page.waitForTimeout(2000);
|
||||
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
|
||||
await expect(page).toHaveURL(/\/#\/login$/);
|
||||
|
||||
// Log in again
|
||||
await page.goto("/#/login");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// We should be being warned that we need to verify (but we can't)
|
||||
await expect(page.getByText("Unable to verify this device")).toBeVisible();
|
||||
|
||||
// And there should be no way to close this prompt
|
||||
await expect(page.getByRole("button", { name: "Skip verification for now" })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test(
|
||||
"continues to show verification prompt after cancelling device verification",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ browser, config, homeserver, page, mailpitClient }, testInfo) => {
|
||||
// Register an account with MAS
|
||||
await page.goto("/#/login");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
const userId = `alice_${testInfo.testId}`;
|
||||
const password = "Pa$sW0rD!";
|
||||
await registerAccountMas(page, mailpitClient, userId, `${userId}@email.com`, password);
|
||||
await expect(page.getByText("Welcome")).toBeVisible();
|
||||
|
||||
// Log in an additional account, and verify it.
|
||||
//
|
||||
// This means that when we log out and in again, we are offered
|
||||
// to verify using another device.
|
||||
const otherContext = await newContext(browser, config, homeserver);
|
||||
const otherDevicePage = await otherContext.newPage();
|
||||
await otherDevicePage.goto("/#/login");
|
||||
await otherDevicePage.getByRole("button", { name: "Continue" }).click();
|
||||
await logInAccountMas(otherDevicePage, userId, password);
|
||||
await verifyUsingOtherDevice(otherDevicePage, page);
|
||||
await otherDevicePage.close();
|
||||
|
||||
// Log out
|
||||
await page.getByRole("button", { name: "User menu" }).click();
|
||||
await expect(page.getByText(userId, { exact: true })).toBeVisible();
|
||||
await page.waitForTimeout(2000);
|
||||
await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Sign out" }).click();
|
||||
await expect(page).toHaveURL(/\/#\/login$/);
|
||||
|
||||
// Log in again
|
||||
await page.goto("/#/login");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
// We should be in, and not able to dismiss the verify dialog
|
||||
await expect(page.getByText("Verify this device")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Skip verification for now" })).not.toBeVisible();
|
||||
|
||||
// When we start verifying with another device
|
||||
await page.getByRole("button", { name: "Verify with another device" }).click();
|
||||
|
||||
// And then cancel it
|
||||
await page.getByRole("button", { name: "Close dialog" }).click();
|
||||
|
||||
// Then we should still be at the unskippable verify prompt
|
||||
await expect(page.getByText("Verify this device")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Skip verification for now" })).not.toBeVisible();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Perform interactive emoji verification for a new device.
|
||||
*/
|
||||
async function verifyUsingOtherDevice(deviceToVerifyPage: Page, alreadyVerifiedDevicePage: Page) {
|
||||
await deviceToVerifyPage.getByRole("button", { name: "Verify with another device" }).click();
|
||||
await alreadyVerifiedDevicePage.getByRole("button", { name: "Verify session" }).click();
|
||||
await alreadyVerifiedDevicePage.getByRole("button", { name: "Start" }).click();
|
||||
await alreadyVerifiedDevicePage.getByRole("button", { name: "They match" }).click();
|
||||
await deviceToVerifyPage.getByRole("button", { name: "They match" }).click();
|
||||
await alreadyVerifiedDevicePage.getByRole("button", { name: "Got it" }).click();
|
||||
await deviceToVerifyPage.getByRole("button", { name: "Got it" }).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new browser context which serves up the default config plus what you supplied, and sets m.homeserver to the
|
||||
* supplied homeserver's URL.
|
||||
*/
|
||||
async function newContext(browser: Browser, config: Partial<Partial<Config>>, homeserver: StartedHomeserverContainer) {
|
||||
const otherContext = await browser.newContext();
|
||||
await otherContext.route(`http://localhost:8080/config.json*`, async (route) => {
|
||||
const json = {
|
||||
...CONFIG_JSON,
|
||||
...config,
|
||||
default_server_config: {
|
||||
"m.homeserver": {
|
||||
base_url: homeserver.baseUrl,
|
||||
},
|
||||
},
|
||||
};
|
||||
await route.fulfill({ json });
|
||||
});
|
||||
return otherContext;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user