1 import { useState } from 'react';
3 import { c } from 'ttag';
5 import { useUser } from '@proton/account/user/hooks';
6 import { useUserSettings } from '@proton/account/userSettings/hooks';
7 import { Button } from '@proton/atoms';
8 import Info from '@proton/components/components/link/Info';
9 import Loader from '@proton/components/components/loader/Loader';
10 import useModalState from '@proton/components/components/modalTwo/useModalState';
11 import Toggle from '@proton/components/components/toggle/Toggle';
12 import ChangeBackupPasswordModal from '@proton/components/containers/account/ChangeBackupPasswordModal';
13 import useSearchParamsEffect from '@proton/components/hooks/useSearchParamsEffect';
14 import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
15 import { SETTINGS_PASSWORD_MODE } from '@proton/shared/lib/interfaces';
16 import { getIsGlobalSSOAccount } from '@proton/shared/lib/keys';
18 import { useAvailableRecoveryMethods, useIsSessionRecoveryInitiationAvailable } from '../../hooks/useSessionRecovery';
19 import ChangePasswordModal, { MODES } from './ChangePasswordModal';
20 import ReauthUsingRecoveryModal from './ReauthUsingRecoveryModal';
21 import SettingsLayout from './SettingsLayout';
22 import SettingsLayoutLeft from './SettingsLayoutLeft';
23 import SettingsLayoutRight from './SettingsLayoutRight';
24 import SettingsSection from './SettingsSection';
25 import InitiateSessionRecoveryModal from './sessionRecovery/InitiateSessionRecoveryModal';
26 import PasswordResetAvailableAccountModal from './sessionRecovery/PasswordResetAvailableAccountModal';
27 import { useSessionRecoveryLocalStorage } from './sessionRecovery/SessionRecoveryLocalStorageManager';
29 const PasswordsSection = () => {
30 const [user, loadingUser] = useUser();
31 const [userSettings, loadingUserSettings] = useUserSettings();
33 const [availableRecoveryMethods] = useAvailableRecoveryMethods();
34 const hasRecoveryMethod = availableRecoveryMethods.length > 0;
35 const isSessionRecoveryInitiationAvailable = useIsSessionRecoveryInitiationAvailable();
37 const [tmpPasswordMode, setTmpPasswordMode] = useState<MODES>();
38 const [changePasswordModal, setChangePasswordModalOpen, renderChangePasswordModal] = useModalState();
39 const [changeBackupPasswordModal, setChangeBackupPasswordModalOpen, renderChangeBackupPasswordModal] =
42 changePasswordAfterReauthModal,
43 setChangePasswordAfterReauthModalOpen,
44 renderChangePasswordAfterReauthModal,
46 const [sessionRecoveryModal, setSessionRecoveryModalOpen, renderSessionRecoveryModal] = useModalState();
47 const [recoveryModal, setRecoveryModalOpen, renderRecoveryModal] = useModalState();
49 sessionRecoveryPasswordResetModal,
50 setSessionRecoveryPasswordResetModalOpen,
51 renderSessionRecoveryPasswordResetModal,
54 const { dismissSessionRecoveryCancelled } = useSessionRecoveryLocalStorage();
55 const [skipInfoStep, setSkipInfoStep] = useState(false);
57 const isOnePasswordMode = userSettings?.Password?.Mode === SETTINGS_PASSWORD_MODE.ONE_PASSWORD_MODE;
58 const passwordLabel = isOnePasswordMode ? c('Title').t`Password` : c('Title').t`Main password`;
59 const passwordButtonLabel = isOnePasswordMode ? c('Title').t`Change password` : c('Title').t`Change login password`;
60 const changePasswordMode = isOnePasswordMode
61 ? MODES.CHANGE_ONE_PASSWORD_MODE
62 : MODES.CHANGE_TWO_PASSWORD_LOGIN_MODE;
63 const loading = loadingUserSettings || loadingUser;
65 const handleChangePassword = (mode: MODES) => {
66 setTmpPasswordMode(mode);
67 setChangePasswordModalOpen(true);
70 useSearchParamsEffect(
75 const action = params.get('action');
81 if (action === 'change-password') {
82 handleChangePassword(changePasswordMode);
83 params.delete('action');
85 } else if (action === 'session-recovery-password-reset-available') {
86 setSkipInfoStep(false);
87 setSessionRecoveryPasswordResetModalOpen(true);
88 params.delete('action');
90 } else if (action === 'session-recovery-reset-password') {
91 setSkipInfoStep(true);
92 setSessionRecoveryPasswordResetModalOpen(true);
93 params.delete('action');
104 // Users without any keys setup are by default in two password mode, even if they have an address.
105 // Don't allow them to change two-password mode.
106 const hasTwoPasswordOption = user.Keys.length > 0;
108 const onRecoveryClick = (() => {
109 if (hasRecoveryMethod) {
111 changePasswordModal.onClose();
112 setRecoveryModalOpen(true);
116 if (isSessionRecoveryInitiationAvailable) {
118 changePasswordModal.onClose();
119 setSessionRecoveryModalOpen(true);
128 {renderChangePasswordModal && tmpPasswordMode && (
130 mode={tmpPasswordMode}
131 onRecoveryClick={onRecoveryClick}
133 dismissSessionRecoveryCancelled();
135 {...changePasswordModal}
138 {renderChangeBackupPasswordModal && <ChangeBackupPasswordModal {...changeBackupPasswordModal} />}
139 {renderSessionRecoveryModal && (
140 <InitiateSessionRecoveryModal
141 onUseRecoveryMethodClick={() => {
142 sessionRecoveryModal.onClose();
143 setRecoveryModalOpen(true);
145 {...sessionRecoveryModal}
148 {renderRecoveryModal && (
149 <ReauthUsingRecoveryModal
150 availableRecoveryMethods={availableRecoveryMethods}
152 recoveryModal.onClose();
153 setChangePasswordModalOpen(true);
155 onInitiateSessionRecoveryClick={() => {
156 recoveryModal.onClose();
157 setSessionRecoveryModalOpen(true);
159 onSuccess={() => setChangePasswordAfterReauthModalOpen(true)}
163 {renderChangePasswordAfterReauthModal && (
165 mode={MODES.CHANGE_ONE_PASSWORD_MODE}
167 {...changePasswordAfterReauthModal}
170 {renderSessionRecoveryPasswordResetModal && (
171 <PasswordResetAvailableAccountModal
172 skipInfoStep={skipInfoStep}
173 {...sessionRecoveryPasswordResetModal}
177 if (getIsGlobalSSOAccount(user)) {
182 <label htmlFor="passwordChange" className="text-semibold">
183 {c('sso').t`Backup password`}
185 </SettingsLayoutLeft>
186 <SettingsLayoutRight>
187 <Button id="passwordChange" onClick={() => setChangeBackupPasswordModalOpen(true)}>
188 {c('sso').t`Change backup password`}
190 </SettingsLayoutRight>
200 <label htmlFor="passwordChange" className="text-semibold">
203 </SettingsLayoutLeft>
204 <SettingsLayoutRight>
205 <Button onClick={() => handleChangePassword(changePasswordMode)}>
206 {passwordButtonLabel}
208 </SettingsLayoutRight>
210 {hasTwoPasswordOption && (
214 <label htmlFor="passwordModeToggle" className="text-semibold">
215 <span className="mr-2">{c('Label').t`Two-password mode`}</span>
217 url={getKnowledgeBaseUrl('/single-password')}
219 .t`Two-password mode requires two passwords: one to sign in to your account and one to decrypt your data. (Advanced)`}
222 </SettingsLayoutLeft>
223 <SettingsLayoutRight isToggleContainer>
225 loading={loadingUserSettings}
226 checked={!isOnePasswordMode}
227 id="passwordModeToggle"
229 handleChangePassword(
231 ? MODES.SWITCH_TWO_PASSWORD
232 : MODES.SWITCH_ONE_PASSWORD
236 </SettingsLayoutRight>
238 {!isOnePasswordMode && (
241 <label htmlFor="passwordModeToggle" className="text-semibold">
242 <span className="mr-2">{c('Label').t`Second password`}</span>
243 <Info url={getKnowledgeBaseUrl('/single-password')} />
245 </SettingsLayoutLeft>
246 <SettingsLayoutRight>
249 handleChangePassword(MODES.CHANGE_TWO_PASSWORD_MAILBOX_MODE)
252 {c('Action').t`Change second password`}
254 </SettingsLayoutRight>
266 export default PasswordsSection;