Simplify registration with email validation (#11398)
This commit is contained in:
parent
beafe686a9
commit
0842559fb2
17 changed files with 437 additions and 55 deletions
93
cypress/e2e/register/email.spec.ts
Normal file
93
cypress/e2e/register/email.spec.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||||
|
import { Mailhog } from "../../support/mailhog";
|
||||||
|
|
||||||
|
describe("Email Registration", () => {
|
||||||
|
let homeserver: HomeserverInstance;
|
||||||
|
let mailhog: Mailhog;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.startMailhog().then((_mailhog) => {
|
||||||
|
mailhog = _mailhog;
|
||||||
|
cy.startHomeserver({
|
||||||
|
template: "email",
|
||||||
|
variables: {
|
||||||
|
SMTP_HOST: "host.docker.internal",
|
||||||
|
SMTP_PORT: _mailhog.instance.smtpPort,
|
||||||
|
},
|
||||||
|
}).then((_homeserver) => {
|
||||||
|
homeserver = _homeserver;
|
||||||
|
|
||||||
|
cy.intercept(
|
||||||
|
{ method: "GET", pathname: "/config.json" },
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
default_server_config: {
|
||||||
|
"m.homeserver": {
|
||||||
|
base_url: homeserver.baseUrl,
|
||||||
|
},
|
||||||
|
"m.identity_server": {
|
||||||
|
base_url: "https://server.invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cy.visit("/#/register");
|
||||||
|
cy.injectAxe();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cy.stopHomeserver(homeserver);
|
||||||
|
cy.stopMailhog(mailhog);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("registers an account and lands on the use case selection screen", () => {
|
||||||
|
cy.findByRole("textbox", { name: "Username" }).should("be.visible");
|
||||||
|
// Hide the server text as it contains the randomly allocated Homeserver port
|
||||||
|
const percyCSS = ".mx_ServerPicker_server { visibility: hidden !important; }";
|
||||||
|
|
||||||
|
cy.findByRole("textbox", { name: "Username" }).type("alice");
|
||||||
|
cy.findByPlaceholderText("Password").type("totally a great password");
|
||||||
|
cy.findByPlaceholderText("Confirm password").type("totally a great password");
|
||||||
|
cy.findByPlaceholderText("Email").type("alice@email.com");
|
||||||
|
cy.findByRole("button", { name: "Register" }).click();
|
||||||
|
|
||||||
|
cy.findByText("Check your email to continue").should("be.visible");
|
||||||
|
cy.percySnapshot("Registration check your email", { percyCSS });
|
||||||
|
cy.checkA11y();
|
||||||
|
|
||||||
|
cy.findByText("An error was encountered when sending the email").should("not.exist");
|
||||||
|
|
||||||
|
cy.waitForPromise(async () => {
|
||||||
|
const messages = await mailhog.api.messages();
|
||||||
|
expect(messages.items).to.have.length(1);
|
||||||
|
expect(messages.items[0].to).to.eq("alice@email.com");
|
||||||
|
const [link] = messages.items[0].text.match(/http.+/);
|
||||||
|
return link;
|
||||||
|
}).as("emailLink");
|
||||||
|
|
||||||
|
cy.get<string>("@emailLink").then((link) => cy.request(link));
|
||||||
|
|
||||||
|
cy.get(".mx_UseCaseSelection_skip", { timeout: 30000 }).should("exist");
|
||||||
|
});
|
||||||
|
});
|
|
@ -26,6 +26,7 @@ import { webserver } from "./webserver";
|
||||||
import { docker } from "./docker";
|
import { docker } from "./docker";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
import { oAuthServer } from "./oauth_server";
|
import { oAuthServer } from "./oauth_server";
|
||||||
|
import { mailhogDocker } from "./mailhog";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Cypress.PluginConfig}
|
* @type {Cypress.PluginConfig}
|
||||||
|
@ -41,4 +42,5 @@ export default function (on: PluginEvents, config: PluginConfigOptions) {
|
||||||
installLogsPrinter(on, {
|
installLogsPrinter(on, {
|
||||||
// printLogsToConsole: "always",
|
// printLogsToConsole: "always",
|
||||||
});
|
});
|
||||||
|
mailhogDocker(on, config);
|
||||||
}
|
}
|
||||||
|
|
91
cypress/plugins/mailhog/index.ts
Normal file
91
cypress/plugins/mailhog/index.ts
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import PluginEvents = Cypress.PluginEvents;
|
||||||
|
import PluginConfigOptions = Cypress.PluginConfigOptions;
|
||||||
|
import { getFreePort } from "../utils/port";
|
||||||
|
import { dockerIp, dockerRun, dockerStop } from "../docker";
|
||||||
|
|
||||||
|
// A cypress plugins to add command to manage an instance of Mailhog in Docker
|
||||||
|
|
||||||
|
export interface Instance {
|
||||||
|
host: string;
|
||||||
|
smtpPort: number;
|
||||||
|
httpPort: number;
|
||||||
|
containerId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const instances = new Map<string, Instance>();
|
||||||
|
|
||||||
|
// Start a synapse instance: the template must be the name of
|
||||||
|
// one of the templates in the cypress/plugins/synapsedocker/templates
|
||||||
|
// directory
|
||||||
|
async function mailhogStart(): Promise<Instance> {
|
||||||
|
const smtpPort = await getFreePort();
|
||||||
|
const httpPort = await getFreePort();
|
||||||
|
|
||||||
|
console.log(`Starting mailhog...`);
|
||||||
|
|
||||||
|
const containerId = await dockerRun({
|
||||||
|
image: "mailhog/mailhog:latest",
|
||||||
|
containerName: `react-sdk-cypress-mailhog`,
|
||||||
|
params: ["--rm", "-p", `${smtpPort}:1025/tcp`, "-p", `${httpPort}:8025/tcp`],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Started mailhog on ports smtp=${smtpPort} http=${httpPort}.`);
|
||||||
|
|
||||||
|
const host = await dockerIp({ containerId });
|
||||||
|
const instance: Instance = { smtpPort, httpPort, containerId, host };
|
||||||
|
instances.set(containerId, instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mailhogStop(id: string): Promise<void> {
|
||||||
|
const synCfg = instances.get(id);
|
||||||
|
|
||||||
|
if (!synCfg) throw new Error("Unknown mailhog ID");
|
||||||
|
|
||||||
|
await dockerStop({
|
||||||
|
containerId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
instances.delete(id);
|
||||||
|
|
||||||
|
console.log(`Stopped mailhog id ${id}.`);
|
||||||
|
// cypress deliberately fails if you return 'undefined', so
|
||||||
|
// return null to signal all is well, and we've handled the task.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Cypress.PluginConfig}
|
||||||
|
*/
|
||||||
|
export function mailhogDocker(on: PluginEvents, config: PluginConfigOptions) {
|
||||||
|
on("task", {
|
||||||
|
mailhogStart,
|
||||||
|
mailhogStop,
|
||||||
|
});
|
||||||
|
|
||||||
|
on("after:spec", async (spec) => {
|
||||||
|
// Cleans up any remaining instances after a spec run
|
||||||
|
for (const synId of instances.keys()) {
|
||||||
|
console.warn(`Cleaning up synapse ID ${synId} after ${spec.name}`);
|
||||||
|
await mailhogStop(synId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -65,6 +65,12 @@ async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Homeserver
|
||||||
hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret);
|
hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret);
|
||||||
hsYaml = hsYaml.replace(/{{PUBLIC_BASEURL}}/g, baseUrl);
|
hsYaml = hsYaml.replace(/{{PUBLIC_BASEURL}}/g, baseUrl);
|
||||||
hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort?.toString());
|
hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort?.toString());
|
||||||
|
if (opts.variables) {
|
||||||
|
for (const key in opts.variables) {
|
||||||
|
hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), String(opts.variables[key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await fse.writeFile(path.join(tempDir, "homeserver.yaml"), hsYaml);
|
await fse.writeFile(path.join(tempDir, "homeserver.yaml"), hsYaml);
|
||||||
|
|
||||||
// now generate a signing key (we could use synapse's config generation for
|
// now generate a signing key (we could use synapse's config generation for
|
||||||
|
|
1
cypress/plugins/synapsedocker/templates/email/README.md
Normal file
1
cypress/plugins/synapsedocker/templates/email/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
A synapse configured to require an email for registration
|
|
@ -0,0 +1,44 @@
|
||||||
|
server_name: "localhost"
|
||||||
|
pid_file: /data/homeserver.pid
|
||||||
|
public_baseurl: "{{PUBLIC_BASEURL}}"
|
||||||
|
listeners:
|
||||||
|
- port: 8008
|
||||||
|
tls: false
|
||||||
|
bind_addresses: ["::"]
|
||||||
|
type: http
|
||||||
|
x_forwarded: true
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- names: [client]
|
||||||
|
compress: false
|
||||||
|
|
||||||
|
database:
|
||||||
|
name: "sqlite3"
|
||||||
|
args:
|
||||||
|
database: ":memory:"
|
||||||
|
|
||||||
|
log_config: "/data/log.config"
|
||||||
|
|
||||||
|
media_store_path: "/data/media_store"
|
||||||
|
uploads_path: "/data/uploads"
|
||||||
|
enable_registration: true
|
||||||
|
registrations_require_3pid:
|
||||||
|
- email
|
||||||
|
registration_shared_secret: "{{REGISTRATION_SECRET}}"
|
||||||
|
report_stats: false
|
||||||
|
macaroon_secret_key: "{{MACAROON_SECRET_KEY}}"
|
||||||
|
form_secret: "{{FORM_SECRET}}"
|
||||||
|
signing_key_path: "/data/localhost.signing.key"
|
||||||
|
|
||||||
|
trusted_key_servers:
|
||||||
|
- server_name: "matrix.org"
|
||||||
|
suppress_key_server_warning: true
|
||||||
|
|
||||||
|
ui_auth:
|
||||||
|
session_timeout: "300s"
|
||||||
|
|
||||||
|
email:
|
||||||
|
smtp_host: "%SMTP_HOST%"
|
||||||
|
smtp_port: %SMTP_PORT%
|
||||||
|
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
|
||||||
|
app_name: my_branded_matrix_server
|
50
cypress/plugins/synapsedocker/templates/email/log.config
Normal file
50
cypress/plugins/synapsedocker/templates/email/log.config
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Log configuration for Synapse.
|
||||||
|
#
|
||||||
|
# This is a YAML file containing a standard Python logging configuration
|
||||||
|
# dictionary. See [1] for details on the valid settings.
|
||||||
|
#
|
||||||
|
# Synapse also supports structured logging for machine readable logs which can
|
||||||
|
# be ingested by ELK stacks. See [2] for details.
|
||||||
|
#
|
||||||
|
# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
|
||||||
|
# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html
|
||||||
|
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
precise:
|
||||||
|
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
# A handler that writes logs to stderr. Unused by default, but can be used
|
||||||
|
# instead of "buffer" and "file" in the logger handlers.
|
||||||
|
console:
|
||||||
|
class: logging.StreamHandler
|
||||||
|
formatter: precise
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
synapse.storage.SQL:
|
||||||
|
# beware: increasing this to DEBUG will make synapse log sensitive
|
||||||
|
# information such as access tokens.
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
twisted:
|
||||||
|
# We send the twisted logging directly to the file handler,
|
||||||
|
# to work around https://github.com/matrix-org/synapse/issues/3471
|
||||||
|
# when using "buffer" logger. Use "console" to log to stderr instead.
|
||||||
|
handlers: [console]
|
||||||
|
propagate: false
|
||||||
|
|
||||||
|
root:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
# Write logs to the `buffer` handler, which will buffer them together in memory,
|
||||||
|
# then write them to a file.
|
||||||
|
#
|
||||||
|
# Replace "buffer" with "console" to log to stderr instead. (Note that you'll
|
||||||
|
# also need to update the configuration for the `twisted` logger above, in
|
||||||
|
# this case.)
|
||||||
|
#
|
||||||
|
handlers: [console]
|
||||||
|
|
||||||
|
disable_existing_loggers: false
|
|
@ -40,6 +40,8 @@ import "./network";
|
||||||
import "./composer";
|
import "./composer";
|
||||||
import "./proxy";
|
import "./proxy";
|
||||||
import "./axe";
|
import "./axe";
|
||||||
|
import "./mailhog";
|
||||||
|
import "./promise";
|
||||||
|
|
||||||
installLogsCollector({
|
installLogsCollector({
|
||||||
// specify the types of logs to collect (and report to the node console at the end of the test)
|
// specify the types of logs to collect (and report to the node console at the end of the test)
|
||||||
|
|
|
@ -28,6 +28,9 @@ export interface StartHomeserverOpts {
|
||||||
|
|
||||||
/** Port of an OAuth server to configure the homeserver to use */
|
/** Port of an OAuth server to configure the homeserver to use */
|
||||||
oAuthServerPort?: number;
|
oAuthServerPort?: number;
|
||||||
|
|
||||||
|
/** Additional variables to inject into the configuration template **/
|
||||||
|
variables?: Record<string, string | number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
54
cypress/support/mailhog.ts
Normal file
54
cypress/support/mailhog.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import mailhog from "mailhog";
|
||||||
|
|
||||||
|
import Chainable = Cypress.Chainable;
|
||||||
|
import { Instance } from "../plugins/mailhog";
|
||||||
|
|
||||||
|
export interface Mailhog {
|
||||||
|
api: mailhog.API;
|
||||||
|
instance: Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
namespace Cypress {
|
||||||
|
interface Chainable {
|
||||||
|
startMailhog(): Chainable<Mailhog>;
|
||||||
|
stopMailhog(instance: Mailhog): Chainable<void>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cypress.Commands.add("startMailhog", (): Chainable<Mailhog> => {
|
||||||
|
return cy.task<Instance>("mailhogStart", { log: false }).then((x) => {
|
||||||
|
Cypress.log({ name: "startHomeserver", message: `Started mailhog instance ${x.containerId}` });
|
||||||
|
return {
|
||||||
|
api: mailhog({
|
||||||
|
host: "localhost",
|
||||||
|
port: x.httpPort,
|
||||||
|
}),
|
||||||
|
instance: x,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("stopMailhog", (mailhog: Mailhog): Chainable<void> => {
|
||||||
|
return cy.task("mailhogStop", mailhog.instance.containerId);
|
||||||
|
});
|
58
cypress/support/promise.ts
Normal file
58
cypress/support/promise.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import Chainable = Cypress.Chainable;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
namespace Cypress {
|
||||||
|
interface Chainable {
|
||||||
|
/**
|
||||||
|
* Utility wrapper around promises to help control flow in tests
|
||||||
|
* Calls `fn` function `tries` times, with a sleep of `interval` between calls.
|
||||||
|
* Ensure you do not rely on any effects of calling any `cy.*` functions within the body of `fn`
|
||||||
|
* as the calls will not happen until after waitForPromise returns.
|
||||||
|
* @param fn the function to retry
|
||||||
|
* @param tries the number of tries to call it
|
||||||
|
* @param interval the time interval between tries
|
||||||
|
*/
|
||||||
|
waitForPromise(fn: () => Promise<unknown>, tries?: number, interval?: number): Chainable<unknown>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForPromise(fn: () => Promise<unknown>, tries = 10, interval = 1000): Chainable<unknown> {
|
||||||
|
return cy.then(
|
||||||
|
() =>
|
||||||
|
new Cypress.Promise(async (resolve, reject) => {
|
||||||
|
for (let i = 0; i < tries; i++) {
|
||||||
|
try {
|
||||||
|
const v = await fn();
|
||||||
|
resolve(v);
|
||||||
|
} catch {
|
||||||
|
await new Cypress.Promise((resolve) => setTimeout(resolve, interval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reject();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cypress.Commands.add("waitForPromise", waitForPromise);
|
||||||
|
|
||||||
|
export {};
|
|
@ -208,6 +208,7 @@
|
||||||
"jest-mock": "^29.2.2",
|
"jest-mock": "^29.2.2",
|
||||||
"jest-raw-loader": "^1.0.1",
|
"jest-raw-loader": "^1.0.1",
|
||||||
"jsqr": "^1.4.0",
|
"jsqr": "^1.4.0",
|
||||||
|
"mailhog": "^4.16.0",
|
||||||
"matrix-mock-request": "^2.5.0",
|
"matrix-mock-request": "^2.5.0",
|
||||||
"matrix-web-i18n": "^1.4.0",
|
"matrix-web-i18n": "^1.4.0",
|
||||||
"mocha-junit-reporter": "^2.2.0",
|
"mocha-junit-reporter": "^2.2.0",
|
||||||
|
|
|
@ -176,8 +176,6 @@ interface IProps {
|
||||||
initialScreenAfterLogin?: IScreen;
|
initialScreenAfterLogin?: IScreen;
|
||||||
// displayname, if any, to set on the device when logging in/registering.
|
// displayname, if any, to set on the device when logging in/registering.
|
||||||
defaultDeviceDisplayName?: string;
|
defaultDeviceDisplayName?: string;
|
||||||
// A function that makes a registration URL
|
|
||||||
makeRegistrationUrl: (params: QueryDict) => string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -2004,13 +2002,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.setState({ serverConfig });
|
this.setState({ serverConfig });
|
||||||
};
|
};
|
||||||
|
|
||||||
private makeRegistrationUrl = (params: QueryDict): string => {
|
|
||||||
if (this.props.startingFragmentQueryParams?.referrer) {
|
|
||||||
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
|
||||||
}
|
|
||||||
return this.props.makeRegistrationUrl(params);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After registration or login, we run various post-auth steps before entering the app
|
* After registration or login, we run various post-auth steps before entering the app
|
||||||
* proper, such setting up cross-signing or verifying the new session.
|
* proper, such setting up cross-signing or verifying the new session.
|
||||||
|
@ -2121,7 +2112,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
idSid={this.state.register_id_sid}
|
idSid={this.state.register_id_sid}
|
||||||
email={email}
|
email={email}
|
||||||
brand={this.props.config.brand}
|
brand={this.props.config.brand}
|
||||||
makeRegistrationUrl={this.makeRegistrationUrl}
|
|
||||||
onLoggedIn={this.onRegisterFlowComplete}
|
onLoggedIn={this.onRegisterFlowComplete}
|
||||||
onLoginClick={this.onLoginClick}
|
onLoginClick={this.onLoginClick}
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
|
|
|
@ -73,15 +73,7 @@ interface IProps {
|
||||||
// - The user's password, if available and applicable (may be cached in memory
|
// - The user's password, if available and applicable (may be cached in memory
|
||||||
// for a short time so the user is not required to re-enter their password
|
// for a short time so the user is not required to re-enter their password
|
||||||
// for operations like uploading cross-signing keys).
|
// for operations like uploading cross-signing keys).
|
||||||
onLoggedIn(params: IMatrixClientCreds, password: string): void;
|
onLoggedIn(params: IMatrixClientCreds, password: string): Promise<void>;
|
||||||
makeRegistrationUrl(params: {
|
|
||||||
/* eslint-disable camelcase */
|
|
||||||
client_secret: string;
|
|
||||||
hs_url: string;
|
|
||||||
is_url?: string;
|
|
||||||
session_id: string;
|
|
||||||
/* eslint-enable camelcase */
|
|
||||||
}): string;
|
|
||||||
// registration shouldn't know or care how login is done.
|
// registration shouldn't know or care how login is done.
|
||||||
onLoginClick(): void;
|
onLoginClick(): void;
|
||||||
onServerConfigChange(config: ValidatedServerConfig): void;
|
onServerConfigChange(config: ValidatedServerConfig): void;
|
||||||
|
@ -302,17 +294,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
): Promise<IRequestTokenResponse> => {
|
): Promise<IRequestTokenResponse> => {
|
||||||
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
||||||
return this.state.matrixClient.requestRegisterEmailToken(
|
return this.state.matrixClient.requestRegisterEmailToken(emailAddress, clientSecret, sendAttempt);
|
||||||
emailAddress,
|
|
||||||
clientSecret,
|
|
||||||
sendAttempt,
|
|
||||||
this.props.makeRegistrationUrl({
|
|
||||||
client_secret: clientSecret,
|
|
||||||
hs_url: this.state.matrixClient.getHomeserverUrl(),
|
|
||||||
is_url: this.state.matrixClient.getIdentityServerUrl(),
|
|
||||||
session_id: sessionId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUIAuthFinished: InteractiveAuthCallback<RegisterResponse> = async (success, response): Promise<void> => {
|
private onUIAuthFinished: InteractiveAuthCallback<RegisterResponse> = async (success, response): Promise<void> => {
|
||||||
|
@ -401,9 +383,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
const hasAccessToken = Boolean(accessToken);
|
const hasAccessToken = Boolean(accessToken);
|
||||||
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
|
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
|
||||||
// don’t log in if we found a session for a different user
|
// don’t log in if we found a session for a different user
|
||||||
if (!hasEmail && hasAccessToken && !newState.differentLoggedInUserId) {
|
if (hasAccessToken && !newState.differentLoggedInUserId) {
|
||||||
// we'll only try logging in if we either have no email to verify at all or we're the client that verified
|
|
||||||
// the email, not the client that started the registration flow
|
|
||||||
await this.props.onLoggedIn(
|
await this.props.onLoggedIn(
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
|
|
|
@ -131,7 +131,6 @@ describe("<MatrixChat />", () => {
|
||||||
},
|
},
|
||||||
onNewScreen: jest.fn(),
|
onNewScreen: jest.fn(),
|
||||||
onTokenLoginCompleted: jest.fn(),
|
onTokenLoginCompleted: jest.fn(),
|
||||||
makeRegistrationUrl: jest.fn(),
|
|
||||||
realQueryParams: {},
|
realQueryParams: {},
|
||||||
};
|
};
|
||||||
const getComponent = (props: Partial<ComponentProps<typeof MatrixChat>> = {}) =>
|
const getComponent = (props: Partial<ComponentProps<typeof MatrixChat>> = {}) =>
|
||||||
|
|
|
@ -75,7 +75,6 @@ describe("Registration", function () {
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
defaultDeviceDisplayName: "test-device-display-name",
|
defaultDeviceDisplayName: "test-device-display-name",
|
||||||
makeRegistrationUrl: jest.fn(),
|
|
||||||
onLoggedIn: jest.fn(),
|
onLoggedIn: jest.fn(),
|
||||||
onLoginClick: jest.fn(),
|
onLoginClick: jest.fn(),
|
||||||
onServerConfigChange: jest.fn(),
|
onServerConfigChange: jest.fn(),
|
||||||
|
|
49
yarn.lock
49
yarn.lock
|
@ -1389,9 +1389,9 @@
|
||||||
integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==
|
integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==
|
||||||
|
|
||||||
"@cypress/request@^2.88.11":
|
"@cypress/request@^2.88.11":
|
||||||
version "2.88.11"
|
version "2.88.12"
|
||||||
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.11.tgz#5a4c7399bc2d7e7ed56e92ce5acb620c8b187047"
|
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590"
|
||||||
integrity sha512-M83/wfQ1EkspjkE2lNWNV5ui2Cv7UCv1swW1DqljahbzLVWltcsexQh8jYtuS/vzFXP+HySntGM83ZXA9fn17w==
|
integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==
|
||||||
dependencies:
|
dependencies:
|
||||||
aws-sign2 "~0.7.0"
|
aws-sign2 "~0.7.0"
|
||||||
aws4 "^1.8.0"
|
aws4 "^1.8.0"
|
||||||
|
@ -1408,7 +1408,7 @@
|
||||||
performance-now "^2.1.0"
|
performance-now "^2.1.0"
|
||||||
qs "~6.10.3"
|
qs "~6.10.3"
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
tough-cookie "~2.5.0"
|
tough-cookie "^4.1.3"
|
||||||
tunnel-agent "^0.6.0"
|
tunnel-agent "^0.6.0"
|
||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
|
|
||||||
|
@ -2615,16 +2615,16 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85"
|
||||||
integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==
|
integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==
|
||||||
|
|
||||||
"@types/node@^14.14.31":
|
|
||||||
version "14.18.54"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.54.tgz#fc304bd66419030141fa997dc5a9e0e374029ae8"
|
|
||||||
integrity sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw==
|
|
||||||
|
|
||||||
"@types/node@^16":
|
"@types/node@^16":
|
||||||
version "16.18.39"
|
version "16.18.39"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.39.tgz#aa39a1a87a40ef6098ee69689a1acb0c1b034832"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.39.tgz#aa39a1a87a40ef6098ee69689a1acb0c1b034832"
|
||||||
integrity sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ==
|
integrity sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ==
|
||||||
|
|
||||||
|
"@types/node@^16.18.39":
|
||||||
|
version "16.18.40"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.40.tgz#968d64746d20cac747a18ca982c0f1fe518c031c"
|
||||||
|
integrity sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
|
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
|
||||||
|
@ -4039,13 +4039,13 @@ cypress-terminal-report@^5.3.2:
|
||||||
tv4 "^1.3.0"
|
tv4 "^1.3.0"
|
||||||
|
|
||||||
cypress@^12.0.0:
|
cypress@^12.0.0:
|
||||||
version "12.17.2"
|
version "12.17.3"
|
||||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.2.tgz#040ac55de1e811f6e037d231a2869d5ab8c29c85"
|
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.3.tgz#1e2b19037236fc60e4a4b3a14f0846be17a1fc0e"
|
||||||
integrity sha512-hxWAaWbqQBzzMuadSGSuQg5PDvIGOovm6xm0hIfpCVcORsCAj/gF2p0EvfnJ4f+jK2PCiDgP6D2eeE9/FK4Mjg==
|
integrity sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cypress/request" "^2.88.11"
|
"@cypress/request" "^2.88.11"
|
||||||
"@cypress/xvfb" "^1.2.4"
|
"@cypress/xvfb" "^1.2.4"
|
||||||
"@types/node" "^14.14.31"
|
"@types/node" "^16.18.39"
|
||||||
"@types/sinonjs__fake-timers" "8.1.1"
|
"@types/sinonjs__fake-timers" "8.1.1"
|
||||||
"@types/sizzle" "^2.3.2"
|
"@types/sizzle" "^2.3.2"
|
||||||
arch "^2.2.0"
|
arch "^2.2.0"
|
||||||
|
@ -5775,7 +5775,7 @@ iconv-lite@0.4.24:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
iconv-lite@0.6.3:
|
iconv-lite@0.6.3, iconv-lite@^0.6:
|
||||||
version "0.6.3"
|
version "0.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||||
|
@ -7078,6 +7078,13 @@ lz-string@^1.4.4:
|
||||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
|
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
|
||||||
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||||
|
|
||||||
|
mailhog@^4.16.0:
|
||||||
|
version "4.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mailhog/-/mailhog-4.16.0.tgz#1ad4dda104505399f3f17824737a962696e7d240"
|
||||||
|
integrity sha512-wXrGik+0MaAy4dbYTImxa8niX9a4aRpZTzC/b1GzCvQs09khhs0aKZgHjgScakI4Y18WInDvvF48hhEz9ifN4g==
|
||||||
|
optionalDependencies:
|
||||||
|
iconv-lite "^0.6"
|
||||||
|
|
||||||
make-dir@^2.0.0, make-dir@^2.1.0:
|
make-dir@^2.0.0, make-dir@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||||
|
@ -8051,7 +8058,7 @@ proxy-from-env@1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
|
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
|
||||||
integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
|
integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
|
||||||
|
|
||||||
psl@^1.1.28, psl@^1.1.33:
|
psl@^1.1.33:
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
||||||
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
|
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
|
||||||
|
@ -9269,13 +9276,15 @@ tough-cookie@^4.1.2:
|
||||||
universalify "^0.2.0"
|
universalify "^0.2.0"
|
||||||
url-parse "^1.5.3"
|
url-parse "^1.5.3"
|
||||||
|
|
||||||
tough-cookie@~2.5.0:
|
tough-cookie@^4.1.3:
|
||||||
version "2.5.0"
|
version "4.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
|
||||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
|
||||||
dependencies:
|
dependencies:
|
||||||
psl "^1.1.28"
|
psl "^1.1.33"
|
||||||
punycode "^2.1.1"
|
punycode "^2.1.1"
|
||||||
|
universalify "^0.2.0"
|
||||||
|
url-parse "^1.5.3"
|
||||||
|
|
||||||
tr46@^1.0.1:
|
tr46@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
|
Loading…
Reference in a new issue