diff --git a/playwright/e2e/crypto/backups.spec.ts b/playwright/e2e/crypto/backups.spec.ts index baff4cd61c..91c103702b 100644 --- a/playwright/e2e/crypto/backups.spec.ts +++ b/playwright/e2e/crypto/backups.spec.ts @@ -31,7 +31,7 @@ test.describe("Encryption state after registration", () => { await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!"); await app.settings.openUserSettings("Security & Privacy"); - expect(page.getByText("This session is backing up your keys.")).toBeVisible(); + await expect(page.getByText("This session is backing up your keys.")).toBeVisible(); }); test("user is prompted to set up recovery", async ({ page, mailhogClient, app }) => { diff --git a/playwright/e2e/oidc/oidc-native.spec.ts b/playwright/e2e/oidc/oidc-native.spec.ts index f40c445fab..60c5bbf025 100644 --- a/playwright/e2e/oidc/oidc-native.spec.ts +++ b/playwright/e2e/oidc/oidc-native.spec.ts @@ -18,7 +18,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { test.slow(); // trace recording takes a while here test("can register the oauth2 client and an account", async ({ context, page, homeserver, mailhogClient, mas }) => { - const tokenUri = `http://${mas.getHost()}:${mas.getMappedPort(8080)}/oauth2/token`; + const tokenUri = `${mas.baseUrl}/oauth2/token`; const tokenApiPromise = page.waitForRequest( (request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code", ); @@ -44,15 +44,15 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { // Assert MAS sees the session as OIDC Native const newPage = await newPagePromise; - await newPage.getByText("Sessions").click(); + await newPage.getByText("Devices").click(); await newPage.getByText(deviceId).click(); await expect(newPage.getByText("Element")).toBeVisible(); - await expect(newPage.getByText("oauth2_session:")).toBeVisible(); await expect(newPage.getByText("http://localhost:8080/")).toBeVisible(); + await expect(newPage).toHaveURL(/\/oauth2_session/); await newPage.close(); // Assert logging out revokes both tokens - const revokeUri = `http://${mas.getHost()}:${mas.getMappedPort(8080)}/oauth2/revoke`; + const revokeUri = `${mas.baseUrl}/oauth2/revoke`; const revokeAccessTokenPromise = page.waitForRequest( (request) => request.url() === revokeUri && request.postDataJSON()["token_type_hint"] === "access_token", ); diff --git a/playwright/plugins/homeserver/synapse/masHomeserver.ts b/playwright/plugins/homeserver/synapse/masHomeserver.ts index 446a0577f8..cb144fcaee 100644 --- a/playwright/plugins/homeserver/synapse/masHomeserver.ts +++ b/playwright/plugins/homeserver/synapse/masHomeserver.ts @@ -6,12 +6,14 @@ 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 { Fixtures } from "@playwright/test"; +import { Fixtures, PlaywrightTestArgs } from "@playwright/test"; import { Services } from "../../../services.ts"; +import { Fixtures as BaseFixtures } from "../../../element-web-test.ts"; import { MatrixAuthenticationServiceContainer } from "../../../testcontainers/mas.ts"; -export const masHomeserver: Fixtures = { +type Fixture = PlaywrightTestArgs & Services & BaseFixtures; +export const masHomeserver: Fixtures = { mas: async ({ _homeserver: homeserver, logger, network, postgres, mailhog }, use) => { const config = { clients: [ @@ -24,7 +26,7 @@ export const masHomeserver: Fixtures = { matrix: { homeserver: "localhost", secret: "AnotherRandomSecret", - endpoint: "http://synapse:8008", + endpoint: "http://homeserver:8008", }, }; @@ -43,144 +45,12 @@ export const masHomeserver: Fixtures = { experimental_features: { msc3861: { enabled: true, - issuer: "http://mas:8080/", - issuer_metadata: { - "issuer": `http://${container.getHost()}:${container.getMappedPort(8080)}/`, - "authorization_endpoint": "http://mas:8080/authorize", - "token_endpoint": "http://mas:8080/oauth2/token", - "jwks_uri": "http://mas:8080/oauth2/keys.json", - "registration_endpoint": "http://mas:8080/oauth2/registration", - "scopes_supported": ["openid", "email"], - "response_types_supported": ["code", "id_token", "code id_token"], - "response_modes_supported": ["form_post", "query", "fragment"], - "grant_types_supported": [ - "authorization_code", - "refresh_token", - "client_credentials", - "urn:ietf:params:oauth:grant-type:device_code", - ], - "token_endpoint_auth_methods_supported": [ - "client_secret_basic", - "client_secret_post", - "client_secret_jwt", - "private_key_jwt", - "none", - ], - "token_endpoint_auth_signing_alg_values_supported": [ - "HS256", - "HS384", - "HS512", - "RS256", - "RS384", - "RS512", - "PS256", - "PS384", - "PS512", - "ES256", - "ES384", - "ES256K", - ], - "revocation_endpoint": "http://mas:8080/oauth2/revoke", - "revocation_endpoint_auth_methods_supported": [ - "client_secret_basic", - "client_secret_post", - "client_secret_jwt", - "private_key_jwt", - "none", - ], - "revocation_endpoint_auth_signing_alg_values_supported": [ - "HS256", - "HS384", - "HS512", - "RS256", - "RS384", - "RS512", - "PS256", - "PS384", - "PS512", - "ES256", - "ES384", - "ES256K", - ], - "introspection_endpoint": "http://mas:8080/oauth2/introspect", - "introspection_endpoint_auth_methods_supported": [ - "client_secret_basic", - "client_secret_post", - "client_secret_jwt", - "private_key_jwt", - "none", - ], - "introspection_endpoint_auth_signing_alg_values_supported": [ - "HS256", - "HS384", - "HS512", - "RS256", - "RS384", - "RS512", - "PS256", - "PS384", - "PS512", - "ES256", - "ES384", - "ES256K", - ], - "code_challenge_methods_supported": ["plain", "S256"], - "userinfo_endpoint": "http://mas:8080/oauth2/userinfo", - "subject_types_supported": ["public"], - "id_token_signing_alg_values_supported": [ - "RS256", - "RS384", - "RS512", - "ES256", - "ES384", - "PS256", - "PS384", - "PS512", - "ES256K", - ], - "userinfo_signing_alg_values_supported": [ - "RS256", - "RS384", - "RS512", - "ES256", - "ES384", - "PS256", - "PS384", - "PS512", - "ES256K", - ], - "display_values_supported": ["page"], - "claim_types_supported": ["normal"], - "claims_supported": [ - "iss", - "sub", - "aud", - "iat", - "exp", - "nonce", - "auth_time", - "at_hash", - "c_hash", - ], - "claims_parameter_supported": false, - "request_parameter_supported": false, - "request_uri_parameter_supported": false, - "prompt_values_supported": ["none", "login", "create"], - "device_authorization_endpoint": "http://mas:8080/oauth2/device", - "org.matrix.matrix-authentication-service.graphql_endpoint": "http://mas:8080/graphql", - "account_management_uri": "http://mas:8080/account/", - "account_management_actions_supported": [ - "org.matrix.profile", - "org.matrix.sessions_list", - "org.matrix.session_view", - "org.matrix.session_end", - ], - }, + issuer: `http://mas:8080/`, + introspection_endpoint: "http://mas:8080/oauth2/introspect", client_id: config.clients[0].client_id, client_auth_method: config.clients[0].client_auth_method, client_secret: config.clients[0].client_secret, admin_token: config.matrix.secret, - account_management_url: `http://${container.getHost()}:${container.getMappedPort(8080)}/account`, }, }, }); @@ -188,4 +58,26 @@ export const masHomeserver: Fixtures = { await use(container); await container.stop(); }, + + config: async ({ homeserver, context, mas }, use) => { + const issuer = `${mas.baseUrl}/`; + const wellKnown = { + "m.homeserver": { + base_url: homeserver.baseUrl, + }, + "org.matrix.msc2965.authentication": { + issuer, + account: `${issuer}account`, + }, + }; + + // Ensure org.matrix.msc2965.authentication is in well-known + await context.route("https://localhost/.well-known/matrix/client", async (route) => { + await route.fulfill({ json: wellKnown }); + }); + + await use({ + default_server_config: wellKnown, + }); + }, }; diff --git a/playwright/services.ts b/playwright/services.ts index 0e95e91fbe..c05cb2d29d 100644 --- a/playwright/services.ts +++ b/playwright/services.ts @@ -12,6 +12,7 @@ import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers import { StartedSynapseContainer, SynapseConfigOptions, SynapseContainer } from "./testcontainers/synapse.ts"; import { ContainerLogger } from "./testcontainers/utils.ts"; +import { StartedMatrixAuthenticationServiceContainer } from "./testcontainers/mas.ts"; export interface Services { logger: ContainerLogger; @@ -25,7 +26,7 @@ export interface Services { synapseConfigOptions: SynapseConfigOptions; _homeserver: SynapseContainer; homeserver: StartedSynapseContainer; - mas?: StartedTestContainer; + mas?: StartedMatrixAuthenticationServiceContainer; } export const test = base.extend({ diff --git a/playwright/testcontainers/mas.ts b/playwright/testcontainers/mas.ts index 79c354da4d..2eadaafac6 100644 --- a/playwright/testcontainers/mas.ts +++ b/playwright/testcontainers/mas.ts @@ -5,7 +5,7 @@ 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 { GenericContainer, StartedTestContainer, Wait } from "testcontainers"; +import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers"; import { StartedPostgreSqlContainer } from "@testcontainers/postgresql"; import * as YAML from "yaml"; @@ -133,6 +133,7 @@ const DEFAULT_CONFIG = { algorithm: "argon2id", }, ], + minimum_complexity: 0, }, policy: { wasm_module: "/usr/local/share/mas-cli/policy.wasm", @@ -158,6 +159,9 @@ const DEFAULT_CONFIG = { imprint: null, logo_uri: null, }, + account: { + password_registration_enabled: true, + }, experimental: { access_token_ttl: 300, compat_token_ttl: 300, @@ -168,7 +172,7 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { private config: typeof DEFAULT_CONFIG; constructor(db: StartedPostgreSqlContainer) { - super("ghcr.io/matrix-org/matrix-authentication-service:0.8.0"); + super("ghcr.io/element-hq/matrix-authentication-service:0.12.0"); this.config = deepCopy(DEFAULT_CONFIG); this.config.database.username = db.getUsername(); @@ -187,7 +191,7 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { return this; } - public override async start(): Promise { + public override async start(): Promise { const port = await getFreePort(); this.config.http.public_base = `http://localhost:${port}/`; @@ -203,6 +207,15 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { }, ]); - return super.start(); + return new StartedMatrixAuthenticationServiceContainer(await super.start(), `http://localhost:${port}`); + } +} + +export class StartedMatrixAuthenticationServiceContainer extends AbstractStartedContainer { + constructor( + container: StartedTestContainer, + public readonly baseUrl: string, + ) { + super(container); } }