2015-11-30 18:11:04 +00:00
|
|
|
|
/*
|
2024-09-09 14:57:16 +01:00
|
|
|
|
Copyright 2024 New Vector Ltd.
|
2021-04-06 12:26:50 +01:00
|
|
|
|
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2024-09-09 14:57:16 +01:00
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2015-11-30 18:11:04 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
2023-08-09 08:18:41 +01:00
|
|
|
|
import {
|
|
|
|
|
|
AuthType,
|
|
|
|
|
|
createClient,
|
|
|
|
|
|
IAuthData,
|
2024-03-25 12:21:02 +00:00
|
|
|
|
AuthDict,
|
2023-08-09 08:18:41 +01:00
|
|
|
|
IInputs,
|
|
|
|
|
|
MatrixError,
|
|
|
|
|
|
IRegisterRequestParams,
|
|
|
|
|
|
IRequestTokenResponse,
|
|
|
|
|
|
MatrixClient,
|
2023-08-15 16:00:17 +01:00
|
|
|
|
SSOFlow,
|
|
|
|
|
|
SSOAction,
|
2023-08-23 10:04:25 +01:00
|
|
|
|
RegisterResponse,
|
2023-08-09 08:18:41 +01:00
|
|
|
|
} from "matrix-js-sdk/src/matrix";
|
2022-05-13 16:10:22 +02:00
|
|
|
|
import React, { Fragment, ReactNode } from "react";
|
2021-10-22 17:23:32 -05:00
|
|
|
|
import classNames from "classnames";
|
|
|
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
2020-11-19 14:57:57 +00:00
|
|
|
|
|
2023-04-27 09:05:31 +01:00
|
|
|
|
import { _t } from "../../../languageHandler";
|
|
|
|
|
|
import { adminContactStrings, messageForResourceLimitError, resourceLimitStrings } from "../../../utils/ErrorUtils";
|
2022-07-14 15:03:34 +02:00
|
|
|
|
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
2019-06-13 16:24:09 +01:00
|
|
|
|
import * as Lifecycle from "../../../Lifecycle";
|
2021-07-03 11:57:05 +02:00
|
|
|
|
import { IMatrixClientCreds, MatrixClientPeg } from "../../../MatrixClientPeg";
|
2019-12-12 19:31:52 -07:00
|
|
|
|
import AuthPage from "../../views/auth/AuthPage";
|
2023-10-12 10:44:46 +13:00
|
|
|
|
import Login, { OidcNativeFlow } from "../../../Login";
|
2020-05-13 20:41:41 -06:00
|
|
|
|
import dis from "../../../dispatcher/dispatcher";
|
2020-11-24 12:09:11 +00:00
|
|
|
|
import SSOButtons from "../../views/elements/SSOButtons";
|
2020-11-25 09:46:56 +00:00
|
|
|
|
import ServerPicker from "../../views/elements/ServerPicker";
|
2021-07-03 12:24:07 +02:00
|
|
|
|
import RegistrationForm from "../../views/auth/RegistrationForm";
|
2023-01-12 13:25:14 +00:00
|
|
|
|
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
|
2021-07-03 12:24:07 +02:00
|
|
|
|
import AuthBody from "../../views/auth/AuthBody";
|
|
|
|
|
|
import AuthHeader from "../../views/auth/AuthHeader";
|
2022-05-20 19:14:17 +02:00
|
|
|
|
import InteractiveAuth, { InteractiveAuthCallback } from "../InteractiveAuth";
|
2021-07-03 12:24:07 +02:00
|
|
|
|
import Spinner from "../../views/elements/Spinner";
|
2022-05-13 16:10:22 +02:00
|
|
|
|
import { AuthHeaderDisplay } from "./header/AuthHeaderDisplay";
|
|
|
|
|
|
import { AuthHeaderProvider } from "./header/AuthHeaderProvider";
|
2022-05-20 19:14:17 +02:00
|
|
|
|
import SettingsStore from "../../../settings/SettingsStore";
|
2022-07-14 15:03:34 +02:00
|
|
|
|
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
|
2023-10-12 10:44:46 +13:00
|
|
|
|
import { Features } from "../../../settings/Settings";
|
|
|
|
|
|
import { startOidcLogin } from "../../../utils/oidc/authorize";
|
2022-05-20 19:14:17 +02:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
const debuglog = (...args: any[]): void => {
|
2022-05-20 19:14:17 +02:00
|
|
|
|
if (SettingsStore.getValue("debug_registration")) {
|
|
|
|
|
|
logger.log.call(console, "Registration debuglog:", ...args);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2020-11-19 14:57:57 +00:00
|
|
|
|
|
2024-09-13 12:12:04 +01:00
|
|
|
|
export interface MobileRegistrationResponse {
|
|
|
|
|
|
user_id: string;
|
|
|
|
|
|
home_server: string;
|
|
|
|
|
|
access_token: string;
|
|
|
|
|
|
device_id: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-19 14:57:57 +00:00
|
|
|
|
interface IProps {
|
|
|
|
|
|
serverConfig: ValidatedServerConfig;
|
2023-04-13 08:52:57 +01:00
|
|
|
|
defaultDeviceDisplayName?: string;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
email?: string;
|
|
|
|
|
|
brand?: string;
|
|
|
|
|
|
clientSecret?: string;
|
|
|
|
|
|
sessionId?: string;
|
|
|
|
|
|
idSid?: string;
|
2020-11-24 12:09:11 +00:00
|
|
|
|
fragmentAfterLogin?: string;
|
2024-09-13 12:12:04 +01:00
|
|
|
|
mobileRegister?: boolean;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
// Called when the user has logged in. Params:
|
|
|
|
|
|
// - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken
|
|
|
|
|
|
// - 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 operations like uploading cross-signing keys).
|
2023-08-15 16:14:53 +01:00
|
|
|
|
onLoggedIn(params: IMatrixClientCreds, password: string): Promise<void>;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
// registration shouldn't know or care how login is done.
|
|
|
|
|
|
onLoginClick(): void;
|
|
|
|
|
|
onServerConfigChange(config: ValidatedServerConfig): void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface IState {
|
2023-03-07 10:45:55 +00:00
|
|
|
|
// true if we're waiting for the user to complete
|
2020-11-19 14:57:57 +00:00
|
|
|
|
busy: boolean;
|
|
|
|
|
|
errorText?: ReactNode;
|
|
|
|
|
|
// We remember the values entered by the user because
|
|
|
|
|
|
// the registration form will be unmounted during the
|
|
|
|
|
|
// course of registration, but if there's an error we
|
|
|
|
|
|
// want to bring back the registration form with the
|
|
|
|
|
|
// values the user entered still in it. We can keep
|
|
|
|
|
|
// them in this component's state since this component
|
|
|
|
|
|
// persist for the duration of the registration process.
|
2023-03-07 10:45:55 +00:00
|
|
|
|
formVals: Record<string, string | undefined>;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
// user-interactive auth
|
|
|
|
|
|
// If we've been given a session ID, we're resuming
|
|
|
|
|
|
// straight back into UI auth
|
|
|
|
|
|
doingUIAuth: boolean;
|
|
|
|
|
|
// If set, we've registered but are not going to log
|
|
|
|
|
|
// the user in to their new account automatically.
|
|
|
|
|
|
completedNoSignin: boolean;
|
2023-03-07 10:45:55 +00:00
|
|
|
|
flows:
|
|
|
|
|
|
| {
|
|
|
|
|
|
stages: string[];
|
|
|
|
|
|
}[]
|
|
|
|
|
|
| null;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
// We perform liveliness checks later, but for now suppress the errors.
|
|
|
|
|
|
// We also track the server dead errors independently of the regular errors so
|
|
|
|
|
|
// that we can render it differently, and override any other error the user may
|
|
|
|
|
|
// be seeing.
|
|
|
|
|
|
serverIsAlive: boolean;
|
|
|
|
|
|
serverErrorIsFatal: boolean;
|
2021-04-06 12:26:50 +01:00
|
|
|
|
serverDeadError?: ReactNode;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
|
|
|
|
|
|
// Our matrix client - part of state because we can't render the UI auth
|
|
|
|
|
|
// component without it.
|
|
|
|
|
|
matrixClient?: MatrixClient;
|
|
|
|
|
|
// The user ID we've just registered
|
|
|
|
|
|
registeredUsername?: string;
|
|
|
|
|
|
// if a different user ID to the one we just registered is logged in,
|
|
|
|
|
|
// this is the user ID that's logged in.
|
|
|
|
|
|
differentLoggedInUserId?: string;
|
2020-11-24 12:09:11 +00:00
|
|
|
|
// the SSO flow definition, this is fetched from /login as that's the only
|
|
|
|
|
|
// place it is exposed.
|
2023-08-15 16:00:17 +01:00
|
|
|
|
ssoFlow?: SSOFlow;
|
2023-10-12 10:44:46 +13:00
|
|
|
|
// the OIDC native login flow, when supported and enabled
|
|
|
|
|
|
// if present, must be used for registration
|
|
|
|
|
|
oidcNativeFlow?: OidcNativeFlow;
|
2020-11-19 14:57:57 +00:00
|
|
|
|
}
|
2019-01-29 12:09:07 -06:00
|
|
|
|
|
2020-11-19 14:57:57 +00:00
|
|
|
|
export default class Registration extends React.Component<IProps, IState> {
|
2022-04-20 19:04:42 +01:00
|
|
|
|
private readonly loginLogic: Login;
|
|
|
|
|
|
// `replaceClient` tracks latest serverConfig to spot when it changes under the async method which fetches flows
|
2023-05-10 08:41:55 +01:00
|
|
|
|
private latestServerConfig?: ValidatedServerConfig;
|
2023-10-12 10:44:46 +13:00
|
|
|
|
// cache value from settings store
|
|
|
|
|
|
private oidcNativeFlowEnabled = false;
|
2020-11-24 12:09:11 +00:00
|
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
|
public constructor(props: IProps) {
|
2020-08-29 12:14:16 +01:00
|
|
|
|
super(props);
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2020-08-29 12:14:16 +01:00
|
|
|
|
this.state = {
|
2015-11-30 18:11:04 +00:00
|
|
|
|
busy: false,
|
|
|
|
|
|
errorText: null,
|
2016-07-06 15:22:06 +01:00
|
|
|
|
formVals: {
|
|
|
|
|
|
email: this.props.email,
|
|
|
|
|
|
},
|
2017-02-24 11:41:23 +00:00
|
|
|
|
doingUIAuth: Boolean(this.props.sessionId),
|
2018-09-04 18:26:09 +01:00
|
|
|
|
flows: null,
|
2019-06-13 16:24:09 +01:00
|
|
|
|
completedNoSignin: false,
|
2019-06-04 23:41:59 -06:00
|
|
|
|
serverIsAlive: true,
|
2019-06-10 19:28:32 -06:00
|
|
|
|
serverErrorIsFatal: false,
|
2019-06-04 23:41:59 -06:00
|
|
|
|
serverDeadError: "",
|
2015-11-30 18:11:04 +00:00
|
|
|
|
};
|
2020-11-24 12:09:11 +00:00
|
|
|
|
|
2023-10-12 10:44:46 +13:00
|
|
|
|
// only set on a config level, so we don't need to watch
|
|
|
|
|
|
this.oidcNativeFlowEnabled = SettingsStore.getValue(Features.OidcNativeFlow);
|
|
|
|
|
|
|
|
|
|
|
|
const { hsUrl, isUrl, delegatedAuthentication } = this.props.serverConfig;
|
2020-11-24 12:09:11 +00:00
|
|
|
|
this.loginLogic = new Login(hsUrl, isUrl, null, {
|
|
|
|
|
|
defaultDeviceDisplayName: "Element login check", // We shouldn't ever be used
|
2023-10-12 10:44:46 +13:00
|
|
|
|
// if native OIDC is enabled in the client pass the server's delegated auth settings
|
|
|
|
|
|
delegatedAuthentication: this.oidcNativeFlowEnabled ? delegatedAuthentication : undefined,
|
2020-11-24 12:09:11 +00:00
|
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
public componentDidMount(): void {
|
2020-11-19 14:57:57 +00:00
|
|
|
|
this.replaceClient(this.props.serverConfig);
|
2022-04-29 12:42:40 +03:00
|
|
|
|
//triggers a confirmation dialog for data loss before page unloads/refreshes
|
|
|
|
|
|
window.addEventListener("beforeunload", this.unloadCallback);
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
public componentWillUnmount(): void {
|
2022-04-29 12:42:40 +03:00
|
|
|
|
window.removeEventListener("beforeunload", this.unloadCallback);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-07 10:45:55 +00:00
|
|
|
|
private unloadCallback = (event: BeforeUnloadEvent): string | undefined => {
|
2022-04-29 12:42:40 +03:00
|
|
|
|
if (this.state.doingUIAuth) {
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
event.returnValue = "";
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2019-05-02 23:04:06 -06:00
|
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
|
public componentDidUpdate(prevProps: IProps): void {
|
2022-11-18 09:22:43 +00:00
|
|
|
|
if (
|
|
|
|
|
|
prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
|
|
|
|
|
|
prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
|
|
|
|
|
|
) {
|
|
|
|
|
|
this.replaceClient(this.props.serverConfig);
|
|
|
|
|
|
}
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private async replaceClient(serverConfig: ValidatedServerConfig): Promise<void> {
|
2022-04-20 19:04:42 +01:00
|
|
|
|
this.latestServerConfig = serverConfig;
|
|
|
|
|
|
const { hsUrl, isUrl } = serverConfig;
|
|
|
|
|
|
|
2019-01-29 23:47:35 -06:00
|
|
|
|
this.setState({
|
|
|
|
|
|
errorText: null,
|
2019-06-17 18:47:20 +01:00
|
|
|
|
serverDeadError: null,
|
|
|
|
|
|
serverErrorIsFatal: false,
|
2022-04-20 19:04:42 +01:00
|
|
|
|
// busy while we do live-ness check (we need to avoid trying to render
|
2019-06-11 12:02:14 +01:00
|
|
|
|
// the UI auth component while we don't have a matrix client)
|
|
|
|
|
|
busy: true,
|
2019-01-29 23:47:35 -06:00
|
|
|
|
});
|
2019-06-04 23:41:59 -06:00
|
|
|
|
|
|
|
|
|
|
// Do a liveliness check on the URLs
|
|
|
|
|
|
try {
|
2022-04-20 19:04:42 +01:00
|
|
|
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl);
|
|
|
|
|
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
2019-06-17 16:27:35 +01:00
|
|
|
|
this.setState({
|
|
|
|
|
|
serverIsAlive: true,
|
|
|
|
|
|
serverErrorIsFatal: false,
|
|
|
|
|
|
});
|
2019-06-04 23:41:59 -06:00
|
|
|
|
} catch (e) {
|
2022-04-20 19:04:42 +01:00
|
|
|
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
2019-06-11 12:10:44 +01:00
|
|
|
|
this.setState({
|
|
|
|
|
|
busy: false,
|
|
|
|
|
|
...AutoDiscoveryUtils.authComponentStateForError(e, "register"),
|
|
|
|
|
|
});
|
2019-06-10 19:28:32 -06:00
|
|
|
|
if (this.state.serverErrorIsFatal) {
|
|
|
|
|
|
return; // Server is dead - do not continue.
|
|
|
|
|
|
}
|
2019-06-04 23:41:59 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 14:45:14 -06:00
|
|
|
|
const cli = createClient({
|
2019-08-16 11:57:32 +01:00
|
|
|
|
baseUrl: hsUrl,
|
|
|
|
|
|
idBaseUrl: isUrl,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2020-11-24 12:09:11 +00:00
|
|
|
|
this.loginLogic.setHomeserverUrl(hsUrl);
|
|
|
|
|
|
this.loginLogic.setIdentityServerUrl(isUrl);
|
2023-10-12 10:44:46 +13:00
|
|
|
|
// if native OIDC is enabled in the client pass the server's delegated auth settings
|
|
|
|
|
|
const delegatedAuthentication = this.oidcNativeFlowEnabled ? serverConfig.delegatedAuthentication : undefined;
|
|
|
|
|
|
|
|
|
|
|
|
this.loginLogic.setDelegatedAuthentication(delegatedAuthentication);
|
2020-11-24 12:09:11 +00:00
|
|
|
|
|
2023-08-15 16:00:17 +01:00
|
|
|
|
let ssoFlow: SSOFlow | undefined;
|
2023-10-12 10:44:46 +13:00
|
|
|
|
let oidcNativeFlow: OidcNativeFlow | undefined;
|
2020-11-24 12:09:11 +00:00
|
|
|
|
try {
|
2023-10-12 10:44:46 +13:00
|
|
|
|
const loginFlows = await this.loginLogic.getFlows(true);
|
2022-04-20 19:04:42 +01:00
|
|
|
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
2023-08-15 16:00:17 +01:00
|
|
|
|
ssoFlow = loginFlows.find((f) => f.type === "m.login.sso" || f.type === "m.login.cas") as SSOFlow;
|
2023-10-12 10:44:46 +13:00
|
|
|
|
oidcNativeFlow = loginFlows.find((f) => f.type === "oidcNativeFlow") as OidcNativeFlow;
|
2020-11-24 12:09:11 +00:00
|
|
|
|
} catch (e) {
|
2022-04-20 19:04:42 +01:00
|
|
|
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
2021-10-15 16:30:53 +02:00
|
|
|
|
logger.error("Failed to get login flows to check for SSO support", e);
|
2020-11-24 12:09:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-05 08:59:27 +01:00
|
|
|
|
await new Promise<void>((resolve) => {
|
|
|
|
|
|
this.setState(
|
|
|
|
|
|
({ flows }) => ({
|
|
|
|
|
|
matrixClient: cli,
|
|
|
|
|
|
ssoFlow,
|
|
|
|
|
|
oidcNativeFlow,
|
|
|
|
|
|
// if we are using oidc native we won't continue with flow discovery on HS
|
|
|
|
|
|
// so set an empty array to indicate flows are no longer loading
|
|
|
|
|
|
flows: oidcNativeFlow ? [] : flows,
|
|
|
|
|
|
busy: false,
|
|
|
|
|
|
}),
|
|
|
|
|
|
resolve,
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
2023-10-12 10:44:46 +13:00
|
|
|
|
|
|
|
|
|
|
// don't need to check with homeserver for login flows
|
|
|
|
|
|
// since we are going to use OIDC native flow
|
|
|
|
|
|
if (oidcNativeFlow) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-04-20 19:04:42 +01:00
|
|
|
|
|
2018-09-04 18:26:09 +01:00
|
|
|
|
try {
|
2020-05-15 13:32:12 +01:00
|
|
|
|
// We do the first registration request ourselves to discover whether we need to
|
|
|
|
|
|
// do SSO instead. If we've already started the UI Auth process though, we don't
|
|
|
|
|
|
// need to.
|
|
|
|
|
|
if (!this.state.doingUIAuth) {
|
2020-11-19 14:57:57 +00:00
|
|
|
|
await this.makeRegisterRequest(null);
|
2022-04-20 19:04:42 +01:00
|
|
|
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
2020-05-27 13:16:49 -06:00
|
|
|
|
// This should never succeed since we specified no auth object.
|
2021-09-21 17:48:09 +02:00
|
|
|
|
logger.log("Expecting 401 from register request but got success!");
|
2020-05-15 13:32:12 +01:00
|
|
|
|
}
|
2018-09-04 18:26:09 +01:00
|
|
|
|
} catch (e) {
|
2022-04-20 19:04:42 +01:00
|
|
|
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
2023-07-05 11:53:22 +01:00
|
|
|
|
if (e instanceof MatrixError && e.httpStatus === 401) {
|
2018-09-04 18:26:09 +01:00
|
|
|
|
this.setState({
|
|
|
|
|
|
flows: e.data.flows,
|
|
|
|
|
|
});
|
2023-07-05 11:53:22 +01:00
|
|
|
|
} else if (e instanceof MatrixError && (e.httpStatus === 403 || e.errcode === "M_FORBIDDEN")) {
|
2021-06-01 15:00:59 +01:00
|
|
|
|
// Check for 403 or M_FORBIDDEN, Synapse used to send 403 M_UNKNOWN but now sends 403 M_FORBIDDEN.
|
2020-03-03 23:19:36 -07:00
|
|
|
|
// At this point registration is pretty much disabled, but before we do that let's
|
|
|
|
|
|
// quickly check to see if the server supports SSO instead. If it does, we'll send
|
|
|
|
|
|
// the user off to the login page to figure their account out.
|
2020-11-24 12:09:11 +00:00
|
|
|
|
if (ssoFlow) {
|
|
|
|
|
|
// Redirect to login page - server probably expects SSO only
|
2021-06-29 13:11:58 +01:00
|
|
|
|
dis.dispatch({ action: "start_login" });
|
2020-11-24 12:09:11 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
this.setState({
|
|
|
|
|
|
serverErrorIsFatal: true, // fatal because user cannot continue on this server
|
2023-09-19 07:17:31 +01:00
|
|
|
|
errorText: _t("auth|registration_disabled"),
|
2020-11-24 12:09:11 +00:00
|
|
|
|
// add empty flows array to get rid of spinner
|
|
|
|
|
|
flows: [],
|
2020-03-03 23:19:36 -07:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2018-09-04 18:26:09 +01:00
|
|
|
|
} else {
|
2021-09-21 17:48:09 +02:00
|
|
|
|
logger.log("Unable to query for supported registration methods.", e);
|
2022-04-20 19:04:42 +01:00
|
|
|
|
this.setState({
|
2023-09-19 07:17:31 +01:00
|
|
|
|
errorText: _t("auth|failed_query_registration_methods"),
|
2022-04-20 19:04:42 +01:00
|
|
|
|
// add empty flows array to get rid of spinner
|
|
|
|
|
|
flows: [],
|
|
|
|
|
|
});
|
2018-09-04 18:26:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2022-04-20 19:04:42 +01:00
|
|
|
|
private onFormSubmit = async (formVals: Record<string, string>): Promise<void> => {
|
2015-11-30 18:11:04 +00:00
|
|
|
|
this.setState({
|
|
|
|
|
|
errorText: "",
|
2016-07-06 15:22:06 +01:00
|
|
|
|
busy: true,
|
2022-04-20 19:04:42 +01:00
|
|
|
|
formVals,
|
2017-02-24 11:41:23 +00:00
|
|
|
|
doingUIAuth: true,
|
2015-11-30 18:11:04 +00:00
|
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
|
};
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private requestEmailToken = (
|
|
|
|
|
|
emailAddress: string,
|
|
|
|
|
|
clientSecret: string,
|
|
|
|
|
|
sendAttempt: number,
|
|
|
|
|
|
sessionId: string,
|
|
|
|
|
|
): Promise<IRequestTokenResponse> => {
|
2023-03-07 10:45:55 +00:00
|
|
|
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
2023-08-15 16:14:53 +01:00
|
|
|
|
return this.state.matrixClient.requestRegisterEmailToken(emailAddress, clientSecret, sendAttempt);
|
2021-06-29 13:11:58 +01:00
|
|
|
|
};
|
2019-05-22 11:51:26 +01:00
|
|
|
|
|
2023-07-04 14:49:27 +01:00
|
|
|
|
private onUIAuthFinished: InteractiveAuthCallback<RegisterResponse> = async (success, response): Promise<void> => {
|
2023-03-07 10:45:55 +00:00
|
|
|
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
|
|
|
|
|
|
2022-05-20 19:14:17 +02:00
|
|
|
|
debuglog("Registration: ui authentication finished: ", { success, response });
|
2017-02-27 17:24:28 +00:00
|
|
|
|
if (!success) {
|
2023-01-12 13:25:14 +00:00
|
|
|
|
let errorText: ReactNode = (response as Error).message || (response as Error).toString();
|
2017-03-15 16:44:56 +00:00
|
|
|
|
// can we give a better error message?
|
2023-02-13 11:39:16 +00:00
|
|
|
|
if (response instanceof MatrixError && response.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
|
2023-04-27 09:05:31 +01:00
|
|
|
|
const errorTop = messageForResourceLimitError(
|
|
|
|
|
|
response.data.limit_type,
|
|
|
|
|
|
response.data.admin_contact,
|
|
|
|
|
|
resourceLimitStrings,
|
|
|
|
|
|
);
|
2018-08-16 13:31:17 +01:00
|
|
|
|
const errorDetail = messageForResourceLimitError(
|
|
|
|
|
|
response.data.limit_type,
|
2020-11-19 14:57:57 +00:00
|
|
|
|
response.data.admin_contact,
|
2023-04-27 09:05:31 +01:00
|
|
|
|
adminContactStrings,
|
2020-11-19 14:57:57 +00:00
|
|
|
|
);
|
2021-10-19 16:31:07 +01:00
|
|
|
|
errorText = (
|
|
|
|
|
|
<div>
|
2021-07-19 22:43:11 +01:00
|
|
|
|
<p>{errorTop}</p>
|
|
|
|
|
|
<p>{errorDetail}</p>
|
2018-08-02 16:09:57 +01:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2023-07-04 14:49:27 +01:00
|
|
|
|
} else if ((response as IAuthData).flows?.some((flow) => flow.stages.includes(AuthType.Msisdn))) {
|
|
|
|
|
|
const flows = (response as IAuthData).flows ?? [];
|
2023-03-07 10:45:55 +00:00
|
|
|
|
const msisdnAvailable = flows.some((flow) => flow.stages.includes(AuthType.Msisdn));
|
2018-06-21 14:04:08 +01:00
|
|
|
|
if (!msisdnAvailable) {
|
2023-09-19 07:17:31 +01:00
|
|
|
|
errorText = _t("auth|unsupported_auth_msisdn");
|
2017-03-15 16:44:56 +00:00
|
|
|
|
}
|
2023-02-13 11:39:16 +00:00
|
|
|
|
} else if (response instanceof MatrixError && response.errcode === "M_USER_IN_USE") {
|
2023-09-19 07:17:31 +01:00
|
|
|
|
errorText = _t("auth|username_in_use");
|
2023-02-13 11:39:16 +00:00
|
|
|
|
} else if (response instanceof MatrixError && response.errcode === "M_THREEPID_IN_USE") {
|
2023-09-19 07:17:31 +01:00
|
|
|
|
errorText = _t("auth|3pid_in_use");
|
2017-03-15 16:44:56 +00:00
|
|
|
|
}
|
2021-10-19 16:31:07 +01:00
|
|
|
|
|
2017-02-27 17:24:28 +00:00
|
|
|
|
this.setState({
|
|
|
|
|
|
busy: false,
|
|
|
|
|
|
doingUIAuth: false,
|
2021-10-19 16:31:07 +01:00
|
|
|
|
errorText,
|
2017-02-27 17:24:28 +00:00
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-04 14:49:27 +01:00
|
|
|
|
const userId = (response as RegisterResponse).user_id;
|
|
|
|
|
|
const accessToken = (response as RegisterResponse).access_token;
|
2023-03-07 10:45:55 +00:00
|
|
|
|
if (!userId || !accessToken) throw new Error("Registration failed");
|
|
|
|
|
|
|
|
|
|
|
|
MatrixClientPeg.setJustRegisteredUserId(userId);
|
2019-06-14 15:31:19 +01:00
|
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
|
const newState: Partial<IState> = {
|
2017-02-24 11:41:23 +00:00
|
|
|
|
doingUIAuth: false,
|
2023-07-04 14:49:27 +01:00
|
|
|
|
registeredUsername: userId,
|
2023-03-07 10:45:55 +00:00
|
|
|
|
differentLoggedInUserId: undefined,
|
2020-11-19 14:57:57 +00:00
|
|
|
|
completedNoSignin: false,
|
|
|
|
|
|
// we're still busy until we get unmounted: don't show the registration form again
|
|
|
|
|
|
busy: true,
|
2019-06-13 16:24:09 +01:00
|
|
|
|
};
|
2019-06-18 18:43:14 +01:00
|
|
|
|
|
|
|
|
|
|
// The user came in through an email validation link. To avoid overwriting
|
|
|
|
|
|
// their session, check to make sure the session isn't someone else, and
|
|
|
|
|
|
// isn't a guest user since we'll usually have set a guest user session before
|
|
|
|
|
|
// starting the registration process. This isn't perfect since it's possible
|
|
|
|
|
|
// the user had a separate guest session they didn't actually mean to replace.
|
2020-12-09 18:40:31 -05:00
|
|
|
|
const [sessionOwner, sessionIsGuest] = await Lifecycle.getStoredSessionOwner();
|
2023-07-04 14:49:27 +01:00
|
|
|
|
if (sessionOwner && !sessionIsGuest && sessionOwner !== userId) {
|
|
|
|
|
|
logger.log(`Found a session for ${sessionOwner} but ${userId} has just registered.`);
|
2019-06-18 18:43:14 +01:00
|
|
|
|
newState.differentLoggedInUserId = sessionOwner;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-20 19:14:17 +02:00
|
|
|
|
// if we don't have an email at all, only one client can be involved in this flow, and we can directly log in.
|
|
|
|
|
|
//
|
|
|
|
|
|
// if we've got an email, it needs to be verified. in that case, two clients can be involved in this flow, the
|
|
|
|
|
|
// original client starting the process and the client that submitted the verification token. After the token
|
|
|
|
|
|
// has been submitted, it can not be used again.
|
|
|
|
|
|
//
|
|
|
|
|
|
// we can distinguish them based on whether the client has form values saved (if so, it's the one that started
|
|
|
|
|
|
// the registration), or whether it doesn't have any form values saved (in which case it's the client that
|
|
|
|
|
|
// verified the email address)
|
|
|
|
|
|
//
|
|
|
|
|
|
// as the client that started registration may be gone by the time we've verified the email, and only the client
|
|
|
|
|
|
// that verified the email is guaranteed to exist, we'll always do the login in that client.
|
|
|
|
|
|
const hasEmail = Boolean(this.state.formVals.email);
|
2023-07-04 14:49:27 +01:00
|
|
|
|
const hasAccessToken = Boolean(accessToken);
|
2022-05-20 19:14:17 +02:00
|
|
|
|
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
|
2022-07-22 11:45:26 +02:00
|
|
|
|
// don’t log in if we found a session for a different user
|
2023-08-15 16:14:53 +01:00
|
|
|
|
if (hasAccessToken && !newState.differentLoggedInUserId) {
|
2024-09-13 12:12:04 +01:00
|
|
|
|
if (this.props.mobileRegister) {
|
|
|
|
|
|
const mobileResponse: MobileRegistrationResponse = {
|
|
|
|
|
|
user_id: userId,
|
|
|
|
|
|
home_server: this.state.matrixClient.getHomeserverUrl(),
|
|
|
|
|
|
access_token: accessToken,
|
|
|
|
|
|
device_id: (response as RegisterResponse).device_id!,
|
|
|
|
|
|
};
|
|
|
|
|
|
const event = new CustomEvent<MobileRegistrationResponse>("mobileregistrationresponse", {
|
|
|
|
|
|
detail: mobileResponse,
|
|
|
|
|
|
});
|
2024-09-13 12:18:32 +01:00
|
|
|
|
window.dispatchEvent(event);
|
2024-09-13 12:12:04 +01:00
|
|
|
|
newState.busy = false;
|
|
|
|
|
|
newState.completedNoSignin = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await this.props.onLoggedIn(
|
|
|
|
|
|
{
|
|
|
|
|
|
userId,
|
|
|
|
|
|
deviceId: (response as RegisterResponse).device_id!,
|
|
|
|
|
|
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
|
|
|
|
|
|
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
|
|
|
|
|
|
accessToken,
|
|
|
|
|
|
},
|
|
|
|
|
|
this.state.formVals.password!,
|
|
|
|
|
|
);
|
2016-06-02 13:14:52 +01:00
|
|
|
|
|
2024-09-13 12:12:04 +01:00
|
|
|
|
this.setupPushers();
|
|
|
|
|
|
}
|
2019-06-13 16:24:09 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
newState.busy = false;
|
|
|
|
|
|
newState.completedNoSignin = true;
|
|
|
|
|
|
}
|
2019-01-25 16:10:54 -06:00
|
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
|
this.setState(newState as IState);
|
2020-08-29 12:14:16 +01:00
|
|
|
|
};
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private setupPushers(): Promise<void> {
|
2017-02-28 15:18:00 +00:00
|
|
|
|
if (!this.props.brand) {
|
2017-07-12 14:02:00 +01:00
|
|
|
|
return Promise.resolve();
|
2017-02-24 11:41:23 +00:00
|
|
|
|
}
|
2023-06-15 15:11:49 +01:00
|
|
|
|
const matrixClient = MatrixClientPeg.safeGet();
|
2022-01-10 12:57:20 +00:00
|
|
|
|
return matrixClient.getPushers().then(
|
|
|
|
|
|
(resp) => {
|
2017-03-01 10:45:17 +00:00
|
|
|
|
const pushers = resp.pushers;
|
|
|
|
|
|
for (let i = 0; i < pushers.length; ++i) {
|
2018-06-21 14:04:08 +01:00
|
|
|
|
if (pushers[i].kind === "email") {
|
2017-03-01 10:45:17 +00:00
|
|
|
|
const emailPusher = pushers[i];
|
2017-02-28 15:18:00 +00:00
|
|
|
|
emailPusher.data = { brand: this.props.brand };
|
2019-11-18 10:03:05 +00:00
|
|
|
|
matrixClient.setPusher(emailPusher).then(
|
|
|
|
|
|
() => {
|
2021-09-21 17:48:09 +02:00
|
|
|
|
logger.log("Set email branding to " + this.props.brand);
|
2017-02-28 15:05:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
(error) => {
|
2021-10-15 16:30:53 +02:00
|
|
|
|
logger.error("Couldn't set email branding: " + error);
|
2017-02-28 15:05:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
);
|
2022-12-12 12:24:14 +01:00
|
|
|
|
}
|
2017-02-28 15:05:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
(error) => {
|
2021-10-15 16:30:53 +02:00
|
|
|
|
logger.error("Couldn't get pushers: " + error);
|
2017-02-28 15:05:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
);
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private onLoginClick = (ev: ButtonEvent): void => {
|
2018-12-20 17:26:13 -07:00
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
|
this.props.onLoginClick();
|
2020-08-29 12:14:16 +01:00
|
|
|
|
};
|
2018-12-20 17:26:13 -07:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private onGoToFormClicked = (ev: ButtonEvent): void => {
|
2019-03-11 16:32:50 -06:00
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
ev.stopPropagation();
|
2020-11-19 14:57:57 +00:00
|
|
|
|
this.replaceClient(this.props.serverConfig);
|
2019-03-11 16:32:50 -06:00
|
|
|
|
this.setState({
|
|
|
|
|
|
busy: false,
|
|
|
|
|
|
doingUIAuth: false,
|
2019-01-29 15:05:15 -06:00
|
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
|
};
|
2019-01-29 15:05:15 -06:00
|
|
|
|
|
2024-03-25 12:21:02 +00:00
|
|
|
|
private makeRegisterRequest = (auth: AuthDict | null): Promise<RegisterResponse> => {
|
2023-03-07 10:45:55 +00:00
|
|
|
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
|
|
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
|
const registerParams: IRegisterRequestParams = {
|
2020-01-30 11:27:54 +00:00
|
|
|
|
username: this.state.formVals.username,
|
|
|
|
|
|
password: this.state.formVals.password,
|
|
|
|
|
|
initial_device_display_name: this.props.defaultDeviceDisplayName,
|
2020-11-19 14:57:57 +00:00
|
|
|
|
auth: undefined,
|
2022-05-20 19:14:17 +02:00
|
|
|
|
// we still want to avoid the race conditions involved with multiple clients handling registration, but
|
|
|
|
|
|
// we'll handle these after we've received the access_token in onUIAuthFinished
|
2020-11-19 14:57:57 +00:00
|
|
|
|
inhibit_login: undefined,
|
2020-01-30 11:27:54 +00:00
|
|
|
|
};
|
|
|
|
|
|
if (auth) registerParams.auth = auth;
|
2022-05-20 19:14:17 +02:00
|
|
|
|
debuglog("Registration: sending registration request:", auth);
|
2020-01-30 11:27:54 +00:00
|
|
|
|
return this.state.matrixClient.registerRequest(registerParams);
|
2020-08-29 12:14:16 +01:00
|
|
|
|
};
|
2015-11-30 18:11:04 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private getUIAuthInputs(): IInputs {
|
2017-02-24 11:41:23 +00:00
|
|
|
|
return {
|
|
|
|
|
|
emailAddress: this.state.formVals.email,
|
|
|
|
|
|
phoneCountry: this.state.formVals.phoneCountry,
|
|
|
|
|
|
phoneNumber: this.state.formVals.phoneNumber,
|
2017-10-11 17:56:17 +01:00
|
|
|
|
};
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2017-02-24 11:41:23 +00:00
|
|
|
|
|
2019-06-13 16:24:09 +01:00
|
|
|
|
// Links to the login page shown after registration is completed are routed through this
|
|
|
|
|
|
// which checks the user hasn't already logged in somewhere else (perhaps we should do
|
|
|
|
|
|
// this more generally?)
|
2023-01-12 13:25:14 +00:00
|
|
|
|
private onLoginClickWithCheck = async (ev: ButtonEvent): Promise<boolean> => {
|
2019-06-13 16:24:09 +01:00
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
|
2021-06-29 13:11:58 +01:00
|
|
|
|
const sessionLoaded = await Lifecycle.loadSession({ ignoreGuest: true });
|
2019-06-13 16:24:09 +01:00
|
|
|
|
if (!sessionLoaded) {
|
|
|
|
|
|
// ok fine, there's still no session: really go to the login page
|
|
|
|
|
|
this.props.onLoginClick();
|
|
|
|
|
|
}
|
2021-04-12 17:01:27 +01:00
|
|
|
|
|
|
|
|
|
|
return sessionLoaded;
|
2020-08-29 12:14:16 +01:00
|
|
|
|
};
|
2019-06-13 16:24:09 +01:00
|
|
|
|
|
2023-03-07 10:45:55 +00:00
|
|
|
|
private renderRegisterComponent(): ReactNode {
|
2019-06-11 12:02:14 +01:00
|
|
|
|
if (this.state.matrixClient && this.state.doingUIAuth) {
|
2019-01-29 12:09:07 -06:00
|
|
|
|
return (
|
|
|
|
|
|
<InteractiveAuth
|
2019-06-11 12:02:14 +01:00
|
|
|
|
matrixClient={this.state.matrixClient}
|
2020-11-19 14:57:57 +00:00
|
|
|
|
makeRequest={this.makeRegisterRequest}
|
|
|
|
|
|
onAuthFinished={this.onUIAuthFinished}
|
|
|
|
|
|
inputs={this.getUIAuthInputs()}
|
|
|
|
|
|
requestEmailToken={this.requestEmailToken}
|
2019-01-29 12:09:07 -06:00
|
|
|
|
sessionId={this.props.sessionId}
|
|
|
|
|
|
clientSecret={this.props.clientSecret}
|
|
|
|
|
|
emailSid={this.props.idSid}
|
|
|
|
|
|
poll={true}
|
|
|
|
|
|
/>
|
|
|
|
|
|
);
|
2019-06-11 12:10:44 +01:00
|
|
|
|
} else if (!this.state.matrixClient && !this.state.busy) {
|
|
|
|
|
|
return null;
|
2019-08-16 15:27:11 +01:00
|
|
|
|
} else if (this.state.busy || !this.state.flows) {
|
2019-02-27 11:21:35 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="mx_AuthBody_spinner">
|
|
|
|
|
|
<Spinner />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2023-10-12 10:44:46 +13:00
|
|
|
|
} else if (this.state.matrixClient && this.state.oidcNativeFlow) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<AccessibleButton
|
|
|
|
|
|
className="mx_Login_fullWidthButton"
|
|
|
|
|
|
kind="primary"
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
await startOidcLogin(
|
|
|
|
|
|
this.props.serverConfig.delegatedAuthentication!,
|
|
|
|
|
|
this.state.oidcNativeFlow!.clientId,
|
|
|
|
|
|
this.props.serverConfig.hsUrl,
|
|
|
|
|
|
this.props.serverConfig.isUrl,
|
|
|
|
|
|
true /* isRegistration */,
|
|
|
|
|
|
);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{_t("action|continue")}
|
|
|
|
|
|
</AccessibleButton>
|
|
|
|
|
|
);
|
2023-03-07 10:45:55 +00:00
|
|
|
|
} else if (this.state.matrixClient && this.state.flows.length) {
|
|
|
|
|
|
let ssoSection: JSX.Element | undefined;
|
2024-09-13 12:12:04 +01:00
|
|
|
|
if (!this.props.mobileRegister && this.state.ssoFlow) {
|
2020-11-25 10:22:16 +00:00
|
|
|
|
let continueWithSection;
|
2021-06-01 11:01:09 +01:00
|
|
|
|
const providers = this.state.ssoFlow.identity_providers || [];
|
2020-11-25 10:22:16 +00:00
|
|
|
|
// when there is only a single (or 0) providers we show a wide button with `Continue with X` text
|
|
|
|
|
|
if (providers.length > 1) {
|
2020-12-01 12:04:41 +00:00
|
|
|
|
// i18n: ssoButtons is a placeholder to help translators understand context
|
2022-08-01 08:31:14 +01:00
|
|
|
|
continueWithSection = (
|
|
|
|
|
|
<h2 className="mx_AuthBody_centered">
|
2023-09-13 09:30:56 +01:00
|
|
|
|
{_t("auth|continue_with_sso", { ssoButtons: "" }).trim()}
|
2022-08-01 08:31:14 +01:00
|
|
|
|
</h2>
|
|
|
|
|
|
);
|
2020-11-25 10:22:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-01 12:04:41 +00:00
|
|
|
|
// i18n: ssoButtons & usernamePassword are placeholders to help translators understand context
|
2020-11-24 12:09:11 +00:00
|
|
|
|
ssoSection = (
|
|
|
|
|
|
<React.Fragment>
|
2020-11-25 09:46:56 +00:00
|
|
|
|
{continueWithSection}
|
2020-11-24 12:09:11 +00:00
|
|
|
|
<SSOButtons
|
|
|
|
|
|
matrixClient={this.loginLogic.createTemporaryClient()}
|
|
|
|
|
|
flow={this.state.ssoFlow}
|
|
|
|
|
|
loginType={this.state.ssoFlow.type === "m.login.sso" ? "sso" : "cas"}
|
|
|
|
|
|
fragmentAfterLogin={this.props.fragmentAfterLogin}
|
2023-01-27 11:06:10 +00:00
|
|
|
|
action={SSOAction.REGISTER}
|
2020-11-24 12:09:11 +00:00
|
|
|
|
/>
|
2022-08-01 08:31:14 +01:00
|
|
|
|
<h2 className="mx_AuthBody_centered">
|
2023-09-13 09:30:56 +01:00
|
|
|
|
{_t("auth|sso_or_username_password", {
|
2021-06-29 13:42:19 +01:00
|
|
|
|
ssoButtons: "",
|
|
|
|
|
|
usernamePassword: "",
|
2021-07-19 22:43:11 +01:00
|
|
|
|
}).trim()}
|
2022-08-01 08:31:14 +01:00
|
|
|
|
</h2>
|
2020-11-24 12:09:11 +00:00
|
|
|
|
</React.Fragment>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
|
{ssoSection}
|
|
|
|
|
|
<RegistrationForm
|
|
|
|
|
|
defaultUsername={this.state.formVals.username}
|
|
|
|
|
|
defaultEmail={this.state.formVals.email}
|
|
|
|
|
|
defaultPhoneCountry={this.state.formVals.phoneCountry}
|
|
|
|
|
|
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
|
|
|
|
|
defaultPassword={this.state.formVals.password}
|
|
|
|
|
|
onRegisterClick={this.onFormSubmit}
|
|
|
|
|
|
flows={this.state.flows}
|
|
|
|
|
|
serverConfig={this.props.serverConfig}
|
|
|
|
|
|
canSubmit={!this.state.serverErrorIsFatal}
|
2021-11-04 10:25:26 +00:00
|
|
|
|
matrixClient={this.state.matrixClient}
|
2024-09-20 12:24:39 +01:00
|
|
|
|
mobileRegister={this.props.mobileRegister}
|
2020-11-24 12:09:11 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
|
);
|
2019-01-29 12:09:07 -06:00
|
|
|
|
}
|
2023-03-07 10:45:55 +00:00
|
|
|
|
|
|
|
|
|
|
return null;
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
2019-01-29 12:09:07 -06:00
|
|
|
|
|
2023-02-13 17:01:43 +00:00
|
|
|
|
public render(): React.ReactNode {
|
2017-10-25 02:04:02 +01:00
|
|
|
|
let errorText;
|
2019-05-02 23:04:06 -06:00
|
|
|
|
const err = this.state.errorText;
|
2019-01-21 12:05:07 -06:00
|
|
|
|
if (err) {
|
|
|
|
|
|
errorText = <div className="mx_Login_error">{err}</div>;
|
2017-10-25 02:04:02 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-04 23:41:59 -06:00
|
|
|
|
let serverDeadSection;
|
|
|
|
|
|
if (!this.state.serverIsAlive) {
|
2019-06-10 19:28:32 -06:00
|
|
|
|
const classes = classNames({
|
|
|
|
|
|
mx_Login_error: true,
|
|
|
|
|
|
mx_Login_serverError: true,
|
|
|
|
|
|
mx_Login_serverErrorNonFatal: !this.state.serverErrorIsFatal,
|
|
|
|
|
|
});
|
2021-07-19 22:43:11 +01:00
|
|
|
|
serverDeadSection = <div className={classes}>{this.state.serverDeadError}</div>;
|
2019-06-04 23:41:59 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-23 11:28:49 +00:00
|
|
|
|
const signIn = (
|
|
|
|
|
|
<span className="mx_AuthBody_changeFlow">
|
2021-07-19 22:43:11 +01:00
|
|
|
|
{_t(
|
2023-09-20 12:45:24 +01:00
|
|
|
|
"auth|sign_in_instead_prompt",
|
2021-07-19 22:43:11 +01:00
|
|
|
|
{},
|
|
|
|
|
|
{
|
2022-01-07 10:40:53 +01:00
|
|
|
|
a: (sub) => (
|
|
|
|
|
|
<AccessibleButton kind="link_inline" onClick={this.onLoginClick}>
|
|
|
|
|
|
{sub}
|
|
|
|
|
|
</AccessibleButton>
|
2022-12-12 12:24:14 +01:00
|
|
|
|
),
|
2022-01-07 10:40:53 +01:00
|
|
|
|
},
|
2021-07-19 22:43:11 +01:00
|
|
|
|
)}
|
2020-11-23 11:28:49 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
);
|
2017-10-26 17:57:49 +01:00
|
|
|
|
|
2019-03-12 10:39:38 -06:00
|
|
|
|
// Only show the 'go back' button if you're not looking at the form
|
|
|
|
|
|
let goBack;
|
2020-11-25 09:46:56 +00:00
|
|
|
|
if (this.state.doingUIAuth) {
|
2022-01-07 10:40:53 +01:00
|
|
|
|
goBack = (
|
|
|
|
|
|
<AccessibleButton kind="link" className="mx_AuthBody_changeFlow" onClick={this.onGoToFormClicked}>
|
2023-08-23 11:57:22 +01:00
|
|
|
|
{_t("action|go_back")}
|
2022-01-07 10:40:53 +01:00
|
|
|
|
</AccessibleButton>
|
|
|
|
|
|
);
|
2019-03-11 16:32:50 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-13 16:24:09 +01:00
|
|
|
|
let body;
|
|
|
|
|
|
if (this.state.completedNoSignin) {
|
|
|
|
|
|
let regDoneText;
|
2024-09-13 12:12:04 +01:00
|
|
|
|
if (this.props.mobileRegister) {
|
|
|
|
|
|
regDoneText = undefined;
|
|
|
|
|
|
} else if (this.state.differentLoggedInUserId) {
|
2019-06-18 18:43:14 +01:00
|
|
|
|
regDoneText = (
|
|
|
|
|
|
<div>
|
2021-07-19 22:43:11 +01:00
|
|
|
|
<p>
|
2023-09-13 09:30:56 +01:00
|
|
|
|
{_t("auth|account_clash", {
|
|
|
|
|
|
newAccountId: this.state.registeredUsername,
|
|
|
|
|
|
loggedInUserId: this.state.differentLoggedInUserId,
|
|
|
|
|
|
})}
|
2021-07-19 22:43:11 +01:00
|
|
|
|
</p>
|
2021-07-23 10:23:45 +01:00
|
|
|
|
<p>
|
|
|
|
|
|
<AccessibleButton
|
2022-06-29 22:37:34 +00:00
|
|
|
|
kind="link_inline"
|
2023-01-12 13:25:14 +00:00
|
|
|
|
onClick={async (event: ButtonEvent): Promise<void> => {
|
2021-07-23 10:23:45 +01:00
|
|
|
|
const sessionLoaded = await this.onLoginClickWithCheck(event);
|
|
|
|
|
|
if (sessionLoaded) {
|
|
|
|
|
|
dis.dispatch({ action: "view_welcome_page" });
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2023-09-13 09:30:56 +01:00
|
|
|
|
{_t("auth|account_clash_previous_account")}
|
2019-06-18 18:43:14 +01:00
|
|
|
|
</AccessibleButton>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2022-05-20 19:14:17 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
// regardless of whether we're the client that started the registration or not, we should
|
|
|
|
|
|
// try our credentials anyway
|
2022-08-01 08:31:14 +01:00
|
|
|
|
regDoneText = (
|
|
|
|
|
|
<h2>
|
|
|
|
|
|
{_t(
|
2023-09-13 09:30:56 +01:00
|
|
|
|
"auth|log_in_new_account",
|
2019-06-13 16:24:09 +01:00
|
|
|
|
{},
|
|
|
|
|
|
{
|
2022-05-20 19:14:17 +02:00
|
|
|
|
a: (sub) => (
|
|
|
|
|
|
<AccessibleButton
|
2022-06-29 22:37:34 +00:00
|
|
|
|
kind="link_inline"
|
2023-01-12 13:25:14 +00:00
|
|
|
|
onClick={async (event: ButtonEvent): Promise<void> => {
|
2022-05-20 19:14:17 +02:00
|
|
|
|
const sessionLoaded = await this.onLoginClickWithCheck(event);
|
|
|
|
|
|
if (sessionLoaded) {
|
|
|
|
|
|
dis.dispatch({ action: "view_home_page" });
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{sub}
|
|
|
|
|
|
</AccessibleButton>
|
|
|
|
|
|
),
|
2019-06-13 16:24:09 +01:00
|
|
|
|
},
|
2022-08-01 08:31:14 +01:00
|
|
|
|
)}
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
);
|
2019-06-13 16:24:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
body = (
|
|
|
|
|
|
<div>
|
2023-09-13 09:30:56 +01:00
|
|
|
|
<h1>{_t("auth|registration_successful")}</h1>
|
2019-06-18 18:43:14 +01:00
|
|
|
|
{regDoneText}
|
2019-06-13 16:24:09 +01:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2024-09-13 12:12:04 +01:00
|
|
|
|
} else if (this.props.mobileRegister) {
|
2024-09-13 14:48:48 +01:00
|
|
|
|
body = (
|
|
|
|
|
|
<Fragment>
|
|
|
|
|
|
<h1>{_t("auth|mobile_create_account_title", { hsName: this.props.serverConfig.hsName })}</h1>
|
|
|
|
|
|
{errorText}
|
|
|
|
|
|
{serverDeadSection}
|
|
|
|
|
|
{this.renderRegisterComponent()}
|
|
|
|
|
|
</Fragment>
|
|
|
|
|
|
);
|
2019-06-13 16:24:09 +01:00
|
|
|
|
} else {
|
2022-05-13 16:10:22 +02:00
|
|
|
|
body = (
|
|
|
|
|
|
<Fragment>
|
|
|
|
|
|
<div className="mx_Register_mainContent">
|
|
|
|
|
|
<AuthHeaderDisplay
|
2023-09-25 18:12:41 +01:00
|
|
|
|
title={_t("auth|create_account_title")}
|
2022-05-13 16:10:22 +02:00
|
|
|
|
serverPicker={
|
|
|
|
|
|
<ServerPicker
|
2023-09-20 12:45:24 +01:00
|
|
|
|
title={_t("auth|server_picker_title_registration")}
|
2023-09-13 09:30:56 +01:00
|
|
|
|
dialogTitle={_t("auth|server_picker_dialog_title")}
|
2022-05-13 16:10:22 +02:00
|
|
|
|
serverConfig={this.props.serverConfig}
|
|
|
|
|
|
onServerConfigChange={
|
|
|
|
|
|
this.state.doingUIAuth ? undefined : this.props.onServerConfigChange
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
2022-12-12 12:24:14 +01:00
|
|
|
|
}
|
2022-05-13 16:10:22 +02:00
|
|
|
|
>
|
|
|
|
|
|
{errorText}
|
|
|
|
|
|
{serverDeadSection}
|
|
|
|
|
|
</AuthHeaderDisplay>
|
|
|
|
|
|
{this.renderRegisterComponent()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="mx_Register_footerActions">
|
|
|
|
|
|
{goBack}
|
|
|
|
|
|
{signIn}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Fragment>
|
|
|
|
|
|
);
|
2019-06-13 16:24:09 +01:00
|
|
|
|
}
|
2024-09-13 12:12:04 +01:00
|
|
|
|
if (this.props.mobileRegister) {
|
2024-09-20 12:24:39 +01:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="mx_MobileRegister_body" data-testid="mobile-register">
|
|
|
|
|
|
{body}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2024-09-13 12:12:04 +01:00
|
|
|
|
}
|
2015-11-30 18:11:04 +00:00
|
|
|
|
return (
|
2019-01-21 18:33:17 -06:00
|
|
|
|
<AuthPage>
|
2019-01-25 16:10:54 -06:00
|
|
|
|
<AuthHeader />
|
2022-05-13 16:10:22 +02:00
|
|
|
|
<AuthHeaderProvider>
|
|
|
|
|
|
<AuthBody flex>{body}</AuthBody>
|
|
|
|
|
|
</AuthHeaderProvider>
|
2019-01-21 18:33:17 -06:00
|
|
|
|
</AuthPage>
|
2015-11-30 18:11:04 +00:00
|
|
|
|
);
|
2020-08-29 12:14:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|