From a6dec86374a1d544a352deea86ca7e523ca0757e Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 13 Sep 2024 12:12:04 +0100 Subject: [PATCH 1/6] Add mobile registration and dispatch event for mobile postmessage dance --- res/css/_components.pcss | 1 + .../structures/auth/_MobileRegistration.pcss | 10 +++ src/components/structures/MatrixChat.tsx | 20 +++++- .../structures/auth/Registration.tsx | 63 ++++++++++++++----- src/settings/Settings.tsx | 4 ++ 5 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 res/css/structures/auth/_MobileRegistration.pcss diff --git a/res/css/_components.pcss b/res/css/_components.pcss index bfcab19879..dadd9aadb3 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -94,6 +94,7 @@ @import "./structures/auth/_ConfirmSessionLockTheftView.pcss"; @import "./structures/auth/_Login.pcss"; @import "./structures/auth/_LoginSplashView.pcss"; +@import "./structures/auth/_MobileRegistration.pcss"; @import "./structures/auth/_Registration.pcss"; @import "./structures/auth/_SessionLockStolenView.pcss"; @import "./structures/auth/_SetupEncryptionBody.pcss"; diff --git a/res/css/structures/auth/_MobileRegistration.pcss b/res/css/structures/auth/_MobileRegistration.pcss new file mode 100644 index 0000000000..d50ff8dc1f --- /dev/null +++ b/res/css/structures/auth/_MobileRegistration.pcss @@ -0,0 +1,10 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +.mx_MobileRegister_body { + padding: 32px; +} diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index cae0a549b7..a4119382a1 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -140,7 +140,7 @@ import { cleanUpDraftsIfRequired } from "../../DraftCleaner"; // legacy export export { default as Views } from "../../Views"; -const AUTH_SCREENS = ["register", "login", "forgot_password", "start_sso", "start_cas", "welcome"]; +const AUTH_SCREENS = ["register", "mobile_register", "login", "forgot_password", "start_sso", "start_cas", "welcome"]; // Actions that are redirected through the onboarding process prior to being // re-dispatched. NOTE: some actions are non-trivial and would require @@ -189,6 +189,7 @@ interface IState { register_session_id?: string; // eslint-disable-next-line camelcase register_id_sid?: string; + isMobileRegistration?: boolean; // When showing Modal dialogs we need to set aria-hidden on the root app element // and disable it when there are no dialogs hideToSRUsers: boolean; @@ -243,6 +244,7 @@ export default class MatrixChat extends React.PureComponent { currentUserId: null, hideToSRUsers: false, + isMobileRegistration: false, syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. resizeNotifier: new ResizeNotifier(), @@ -650,6 +652,9 @@ export default class MatrixChat extends React.PureComponent { case "require_registration": startAnyRegistrationFlow(payload as any); break; + case "start_mobile_registration": + this.startRegistration(payload.params || {}, true); + break; case "start_registration": if (Lifecycle.isSoftLogout()) { this.onSoftLogout(); @@ -946,7 +951,7 @@ export default class MatrixChat extends React.PureComponent { }); } - private async startRegistration(params: { [key: string]: string }): Promise { + private async startRegistration(params: { [key: string]: string }, isMobileRegistration?: boolean): Promise { if (!SettingsStore.getValue(UIFeature.Registration)) { this.showScreen("welcome"); return; @@ -976,12 +981,15 @@ export default class MatrixChat extends React.PureComponent { newState.register_client_secret = params.client_secret; newState.register_session_id = params.session_id; newState.register_id_sid = params.sid; + newState.register_id_sid = params.sid; } + newState.isMobileRegistration = isMobileRegistration; //&& SettingsStore.getValue("Registration.mobileRegistrationHelper"); + this.setStateForNewView(newState); ThemeController.isLogin = true; this.themeWatcher.recheck(); - this.notifyNewScreen("register"); + this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register"); } // switch view to the given room @@ -1721,6 +1729,11 @@ export default class MatrixChat extends React.PureComponent { params: params, }); PerformanceMonitor.instance.start(PerformanceEntryNames.REGISTER); + } else if (screen === "mobile_register") { + dis.dispatch({ + action: "start_mobile_registration", + params: params, + }); } else if (screen === "login") { dis.dispatch({ action: "start_login", @@ -2080,6 +2093,7 @@ export default class MatrixChat extends React.PureComponent { onServerConfigChange={this.onServerConfigChange} defaultDeviceDisplayName={this.props.defaultDeviceDisplayName} fragmentAfterLogin={fragmentAfterLogin} + mobileRegister={this.state.isMobileRegistration} {...this.getServerProperties()} /> ); diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index d48bdfdcba..0d8c62604a 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -53,6 +53,13 @@ const debuglog = (...args: any[]): void => { } }; +export interface MobileRegistrationResponse { + user_id: string; + home_server: string; + access_token: string; + device_id: string; +} + interface IProps { serverConfig: ValidatedServerConfig; defaultDeviceDisplayName?: string; @@ -62,7 +69,7 @@ interface IProps { sessionId?: string; idSid?: string; fragmentAfterLogin?: string; - + mobileRegister?: boolean; // 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 @@ -410,18 +417,33 @@ export default class Registration extends React.Component { debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken }); // don’t log in if we found a session for a different user if (hasAccessToken && !newState.differentLoggedInUserId) { - 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!, - ); + 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", { + detail: mobileResponse, + }); + document.dispatchEvent(event); + 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!, + ); - this.setupPushers(); + this.setupPushers(); + } } else { newState.busy = false; newState.completedNoSignin = true; @@ -558,7 +580,7 @@ export default class Registration extends React.Component { ); } else if (this.state.matrixClient && this.state.flows.length) { let ssoSection: JSX.Element | undefined; - if (this.state.ssoFlow) { + if (!this.props.mobileRegister && this.state.ssoFlow) { let continueWithSection; const providers = this.state.ssoFlow.identity_providers || []; // when there is only a single (or 0) providers we show a wide button with `Continue with X` text @@ -591,7 +613,6 @@ export default class Registration extends React.Component { ); } - return ( {ssoSection} @@ -660,7 +681,9 @@ export default class Registration extends React.Component { let body; if (this.state.completedNoSignin) { let regDoneText; - if (this.state.differentLoggedInUserId) { + if (this.props.mobileRegister) { + regDoneText = undefined; + } else if (this.state.differentLoggedInUserId) { regDoneText = (

@@ -717,6 +740,8 @@ export default class Registration extends React.Component { {regDoneText}

); + } else if (this.props.mobileRegister) { + body = this.renderRegisterComponent(); } else { body = ( @@ -746,7 +771,13 @@ export default class Registration extends React.Component { ); } - + if (this.props.mobileRegister) { + return ( + +
{body}
+
+ ); + } return ( diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index a82fdef1ba..5733c6e353 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -876,6 +876,10 @@ export const SETTINGS: { [setting: string]: ISetting } = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: null, }, + "Registration.mobileRegistrationHelper": { + supportedLevels: [SettingLevel.CONFIG], + default: false, + }, "autocompleteDelay": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: 200, From 0244aaeb2f1a5485ea30b0d10c34b524a00bcb9e Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 13 Sep 2024 12:18:32 +0100 Subject: [PATCH 2/6] use window.dispatchEvent --- src/components/structures/auth/Registration.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 0d8c62604a..f2a04d00b4 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -427,7 +427,7 @@ export default class Registration extends React.Component { const event = new CustomEvent("mobileregistrationresponse", { detail: mobileResponse, }); - document.dispatchEvent(event); + window.dispatchEvent(event); newState.busy = false; newState.completedNoSignin = true; } else { From a89f61a3578c770361ee21f86de177bf7ef266cf Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 13 Sep 2024 14:48:48 +0100 Subject: [PATCH 3/6] Add error text and title with server name --- src/components/structures/auth/Registration.tsx | 15 +++++++++------ src/i18n/strings/en_EN.json | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index f2a04d00b4..2dc9125362 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -741,7 +741,14 @@ export default class Registration extends React.Component { ); } else if (this.props.mobileRegister) { - body = this.renderRegisterComponent(); + body = ( + +

{_t("auth|mobile_create_account_title", { hsName: this.props.serverConfig.hsName })}

+ {errorText} + {serverDeadSection} + {this.renderRegisterComponent()} +
+ ); } else { body = ( @@ -772,11 +779,7 @@ export default class Registration extends React.Component { ); } if (this.props.mobileRegister) { - return ( - -
{body}
-
- ); + return
{body}
; } return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 21addb3b98..2eacd48d87 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -229,6 +229,7 @@ }, "misconfigured_body": "Ask your %(brand)s admin to check your config for incorrect or duplicate entries.", "misconfigured_title": "Your %(brand)s is misconfigured", + "mobile_create_account_title": "You're about to create an account on %(hsName)s", "msisdn_field_description": "Other users can invite you to rooms using your contact details", "msisdn_field_label": "Phone", "msisdn_field_number_invalid": "That phone number doesn't look quite right, please check and try again", From 0716434aa1cac6bf5434bbac0f55e137e6060d21 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 13 Sep 2024 16:59:11 +0100 Subject: [PATCH 4/6] Allow hs_url as param on mobile_register --- src/components/structures/MatrixChat.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index a4119382a1..0de3498003 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -961,9 +961,16 @@ export default class MatrixChat extends React.PureComponent { view: Views.REGISTER, }; - // Only honour params if they are all present, otherwise we reset - // HS and IS URLs when switching to registration. - if (params.client_secret && params.session_id && params.hs_url && params.is_url && params.sid) { + if (isMobileRegistration && params.hs_url) { + try { + const config = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(params.hs_url); + newState.serverConfig = config; + } catch (err) { + logger.warn("Failed to load hs_url param:", params.hs_url); + } + } else if (params.client_secret && params.session_id && params.hs_url && params.is_url && params.sid) { + // Only honour params if they are all present, otherwise we reset + // HS and IS URLs when switching to registration. newState.serverConfig = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls( params.hs_url, params.is_url, From 62d66f9d918c4314c9dde8fb10ad2c926b4e0e02 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 13 Sep 2024 16:59:50 +0100 Subject: [PATCH 5/6] Remove accidental paste --- src/components/structures/MatrixChat.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 0de3498003..b54c1bd65f 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -988,7 +988,6 @@ export default class MatrixChat extends React.PureComponent { newState.register_client_secret = params.client_secret; newState.register_session_id = params.session_id; newState.register_id_sid = params.sid; - newState.register_id_sid = params.sid; } newState.isMobileRegistration = isMobileRegistration; //&& SettingsStore.getValue("Registration.mobileRegistrationHelper"); From 20a4f0af995a45304450814ef9217761871bbe45 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 16 Sep 2024 15:16:24 +0100 Subject: [PATCH 6/6] Enforce config setting --- src/components/structures/MatrixChat.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index b54c1bd65f..8e0eaabe4f 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -956,12 +956,14 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("welcome"); return; } + const isMobileRegistrationAllowed = + isMobileRegistration && SettingsStore.getValue("Registration.mobileRegistrationHelper"); const newState: Partial = { view: Views.REGISTER, }; - if (isMobileRegistration && params.hs_url) { + if (isMobileRegistrationAllowed && params.hs_url) { try { const config = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(params.hs_url); newState.serverConfig = config; @@ -990,12 +992,12 @@ export default class MatrixChat extends React.PureComponent { newState.register_id_sid = params.sid; } - newState.isMobileRegistration = isMobileRegistration; //&& SettingsStore.getValue("Registration.mobileRegistrationHelper"); + newState.isMobileRegistration = isMobileRegistrationAllowed; this.setStateForNewView(newState); ThemeController.isLogin = true; this.themeWatcher.recheck(); - this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register"); + this.notifyNewScreen(isMobileRegistrationAllowed ? "mobile_register" : "register"); } // switch view to the given room