From 4ecc152037709a68a9dc879263413bb156114128 Mon Sep 17 00:00:00 2001 From: Immad Abdul Jabbar Date: Mon, 16 Dec 2024 20:20:59 +0500 Subject: [PATCH] feat: add enterprise login V2 flow [WPB-14366] --- src/i18n/en-US.json | 24 +- src/script/auth/assets/WavesPattern.tsx | 60 +++++ src/script/auth/component/Layout.tsx | 72 ++++++ src/script/auth/component/LoginForm.tsx | 7 +- src/script/auth/page/ClientManager.tsx | 6 +- .../auth/page/CreatePersonalAccount.tsx | 2 +- src/script/auth/page/CustomBackend.tsx | 147 ++++++++++++ .../auth/page/CustomEnvironmentRedirect.tsx | 10 +- src/script/auth/page/HistoryInfo.tsx | 2 +- src/script/auth/page/Index.tsx | 30 ++- src/script/auth/page/Login.tsx | 98 ++++++-- src/script/auth/page/Page.tsx | 8 + src/script/auth/page/Root.tsx | 9 +- src/script/auth/page/SetAccountType.tsx | 10 +- src/script/auth/page/SingleSignOn.tsx | 36 ++- src/script/auth/page/SingleSignOnForm.tsx | 216 +++++++++++------- src/script/auth/page/VerifyEmailCode.tsx | 2 +- src/script/auth/route.ts | 4 + 18 files changed, 591 insertions(+), 152 deletions(-) create mode 100644 src/script/auth/assets/WavesPattern.tsx create mode 100644 src/script/auth/component/Layout.tsx create mode 100644 src/script/auth/page/CustomBackend.tsx diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index 169f897c200..582553b1b8a 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -819,7 +819,8 @@ "index.login": "Log in", "index.loginInfo": "Already have an account?", "index.ssoLogin": "Log in with SSO", - "index.welcome": "Welcome to {brandName}", + "index.welcome": "Welcome to {brandName}!", + "index.or": "or", "initDecryption": "Decrypting messages", "initEvents": "Loading messages", "initProgress": " — {number1} of {number2}", @@ -1463,7 +1464,6 @@ "ssoLogin.subhead": "Enter the company SSO access code.", "ssoLogin.subheadCode": "Please enter your SSO code", "ssoLogin.subheadCodeOrEmail": "Please enter your email or SSO code.", - "ssoLogin.subheadEmailEnvironmentSwitchWarning": "If your email matches an enterprise installation of {brandName}, this app will connect to that server.", "startedAudioCallingAlert": "You are calling {conversationName}.", "startedGroupCallingAlert": "You started a conference call with {conversationName}.", "startedVideoCallingAlert": "You are calling {conversationName}, you camera is {cameraStatus}.", @@ -1664,5 +1664,21 @@ "wireLinux": "{brandName} for Linux", "wireMacos": "{brandName} for macOS", "wireWindows": "{brandName} for Windows", - "wire_for_web": "{brandName} for Web" -} + "wire_for_web": "{brandName} for Web", + "layoutSidebarHeader": "Collaborate without Compromise", + "layoutSidebarContent": "Connect, message, and share files with ease, protected by the industry’s most secure end-to-end encryption", + "layoutSidebarLink": "Learn more", + "redirectHeader": "Connect to your custom backend?", + "redirectSubHeader": "If you continue, we will redirect you to your team’s customized backend {backendName} to log in.", + "redirectBackendName": "Backend name:", + "redirectBackendURL": "Backend URL:", + "redirectBackendWSURL": "Backend WSURL:", + "redirectBlacklistURL": "Blacklist URL:", + "redirectTeamsURL": "Teams URL:", + "redirectAccountURL": "Accounts URL:", + "redirectWebsiteURL": "Website URL:", + "redirectHideDetails": "Hide details", + "redirectShowDetails": "Show details", + "redirectConnect": "Connect", + "redirectCancel": "Cancel" +} \ No newline at end of file diff --git a/src/script/auth/assets/WavesPattern.tsx b/src/script/auth/assets/WavesPattern.tsx new file mode 100644 index 00000000000..32c10e0a0bd --- /dev/null +++ b/src/script/auth/assets/WavesPattern.tsx @@ -0,0 +1,60 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export const WavesPattern = () => ( + + + + + + +); diff --git a/src/script/auth/component/Layout.tsx b/src/script/auth/component/Layout.tsx new file mode 100644 index 00000000000..827a0de067a --- /dev/null +++ b/src/script/auth/component/Layout.tsx @@ -0,0 +1,72 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {ReactNode} from 'react'; + +import {CSSObject} from '@emotion/react'; + +import {Bold, COLOR_V2, FlexBox, Link, Logo, QUERY, QueryKeys, Text, useMatchMedia} from '@wireapp/react-ui-kit'; + +import {t} from 'Util/LocalizerUtil'; + +import {Config} from '../../Config'; +import {WavesPattern} from '../assets/WavesPattern'; + +export const Layout = ({children}: {children: ReactNode}) => { + const isTablet = useMatchMedia(QUERY[QueryKeys.TABLET_DOWN]); + + return ( + + {!isTablet && ( +
+ +
+ + {t('layoutSidebarHeader')} + +
+
+ {t('layoutSidebarContent')} +
+ + {t('layoutSidebarLink')} + +
+
+ +
+ )} +
{children}
+
+ ); +}; + +const leftSectionCss: CSSObject = { + background: 'black', + margin: 0, + height: '100vh', + maxWidth: '26rem', + padding: '6rem 3.75rem', + position: 'relative', + minHeight: '42rem', +}; + +const whiteFontCss: CSSObject = { + color: 'white', +}; diff --git a/src/script/auth/component/LoginForm.tsx b/src/script/auth/component/LoginForm.tsx index 2bb5fd10dbd..246368d0988 100644 --- a/src/script/auth/component/LoginForm.tsx +++ b/src/script/auth/component/LoginForm.tsx @@ -20,6 +20,7 @@ import React, {useRef, useState} from 'react'; import {LoginData} from '@wireapp/api-client/lib/auth'; +import {useSearchParams} from 'react-router-dom'; import {Button, Input, Loading} from '@wireapp/react-ui-kit'; @@ -27,6 +28,7 @@ import {t} from 'Util/LocalizerUtil'; import {isValidEmail, isValidUsername} from 'Util/ValidationUtil'; import {ValidationError} from '../module/action/ValidationError'; +import {QUERY_KEY} from '../route'; interface LoginFormProps { isFetching: boolean; @@ -36,11 +38,13 @@ interface LoginFormProps { const LoginForm = ({isFetching, onSubmit}: LoginFormProps) => { const emailInput = useRef(null); const passwordInput = useRef(null); + const [params] = useSearchParams(); + const defaultEmail = params.get(QUERY_KEY.EMAIL); const [validEmailInput, setValidEmailInput] = useState(true); const [validPasswordInput, setValidPasswordInput] = useState(true); - const [email, setEmail] = useState(''); + const [email, setEmail] = useState(decodeURIComponent(defaultEmail || '')); const [password, setPassword] = useState(''); const handleSubmit = (event: React.FormEvent): void => { @@ -97,6 +101,7 @@ const LoginForm = ({isFetching, onSubmit}: LoginFormProps) => { return (
) => { diff --git a/src/script/auth/page/ClientManager.tsx b/src/script/auth/page/ClientManager.tsx index d281020a898..c8d8405af90 100644 --- a/src/script/auth/page/ClientManager.tsx +++ b/src/script/auth/page/ClientManager.tsx @@ -70,7 +70,7 @@ const ClientManagerComponent = ({doGetAllClients, doLogout}: Props & ConnectedPr }; return ( - + -

+

{t('clientManager.headline')}

{t('clientManager.logout')} diff --git a/src/script/auth/page/CreatePersonalAccount.tsx b/src/script/auth/page/CreatePersonalAccount.tsx index 201dd506a17..2d3b52ebd2b 100644 --- a/src/script/auth/page/CreatePersonalAccount.tsx +++ b/src/script/auth/page/CreatePersonalAccount.tsx @@ -80,7 +80,7 @@ const CreatePersonalAccountComponent = ({ ); return ( - +
{backArrow}
diff --git a/src/script/auth/page/CustomBackend.tsx b/src/script/auth/page/CustomBackend.tsx new file mode 100644 index 00000000000..ea2a3b53b72 --- /dev/null +++ b/src/script/auth/page/CustomBackend.tsx @@ -0,0 +1,147 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {useEffect, useState} from 'react'; + +import {BackendConfig} from '@wireapp/api-client/lib/account/BackendConfig'; +import {useNavigate, useSearchParams} from 'react-router-dom'; +import {container} from 'tsyringe'; + +import {Button, ButtonVariant, Container, Muted, Text} from '@wireapp/react-ui-kit'; + +import {APIClient} from 'src/script/service/APIClientSingleton'; +import {t} from 'Util/LocalizerUtil'; + +import {Page} from './Page'; + +import {QUERY_KEY, ROUTE} from '../route'; +import {getSearchParams} from '../util/urlUtil'; + +export function CustomBackend() { + const [searchParams] = useSearchParams(); + const url = searchParams.get(QUERY_KEY.CONFIG_URL); + const navigate = useNavigate(); + const [config, setConfig] = useState(); + const [isDetailVisible, setIsDetailVisible] = useState(false); + + if (!url) { + navigate(ROUTE.INDEX); + } + + const apiClient = container.resolve(APIClient); + + useEffect(() => { + if (url) { + apiClient.api.account + .getBackendConfig(url) + .then(res => { + setConfig(res); + }) + .catch(() => { + navigate(ROUTE.INDEX); + }); + } + }, [apiClient.api.account, navigate, url]); + + const details = [ + { + label: t('redirectBackendName'), + text: config?.backendName, + }, + { + label: t('redirectBackendURL'), + text: config?.backendURL, + }, + { + label: t('redirectBackendWSURL'), + text: config?.backendWSURL, + }, + { + label: t('redirectBlacklistURL'), + text: config?.blacklistURL, + }, + { + label: t('redirectTeamsURL'), + text: config?.teamsURL, + }, + { + label: t('redirectAccountURL'), + text: config?.accountURL, + }, + { + label: t('redirectWebsiteURL'), + text: config?.websiteURL, + }, + ]; + + const toggleDetails = () => { + setIsDetailVisible(visibility => !visibility); + }; + + const onConnect = () => { + if (config?.webAppURL) { + window.location.assign( + `/auth?${getSearchParams({[QUERY_KEY.DESTINATION_URL]: encodeURIComponent(`https://local.zinfra.io:8081/auth/#/login?email=${searchParams.get(QUERY_KEY.EMAIL)}`)})}#${ + ROUTE.CUSTOM_ENV_REDIRECT + }`, + ); + } + }; + + return ( + + + + {t('redirectHeader')} + + + {t('redirectSubHeader', {backendName: config?.backendName || ''})} + + {isDetailVisible && ( +
+ {details.map(({label, text}) => ( +
+ {label} +
+ {text} +
+ ))} +
+ )} + + {isDetailVisible ? t('redirectHideDetails') : t('redirectShowDetails')} + +
+ + +
+
+
+ ); +} diff --git a/src/script/auth/page/CustomEnvironmentRedirect.tsx b/src/script/auth/page/CustomEnvironmentRedirect.tsx index 91b4e4114f7..5899e6e5830 100644 --- a/src/script/auth/page/CustomEnvironmentRedirect.tsx +++ b/src/script/auth/page/CustomEnvironmentRedirect.tsx @@ -63,13 +63,9 @@ const CustomEnvironmentRedirectComponent = ({doNavigate, doSendNavigationEvent}: }, [destinationUrl]); return ( - + - + -