ente/packages/accounts/pages/verify.tsx
2023-11-09 14:29:06 +05:30

190 lines
7.2 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Trans } from 'react-i18next';
import { t } from 'i18next';
import { LS_KEYS, getData, setData } from '@ente/shared/storage/localStorage';
import { verifyOtt, sendOtt, putAttributes } from '../api/user';
import { logoutUser } from '../services/user';
import { configureSRP } from '../services/srp';
import { clearFiles } from '@ente/shared/storage/localForage/helpers';
import { setIsFirstLogin } from '@ente/shared/storage/localStorage/helpers';
import { clearKeys } from '@ente/shared/storage/sessionStorage';
import { PAGES } from '../constants/pages';
import { KeyAttributes, User } from '@ente/shared/user/types';
import { SRPAttributes, SRPSetupAttributes } from '../types/srp';
import { Box, Typography } from '@mui/material';
import FormPaperTitle from '@ente/shared/components/Form/FormPaper/Title';
import FormPaper from '@ente/shared/components/Form/FormPaper';
import FormPaperFooter from '@ente/shared/components/Form/FormPaper/Footer';
import LinkButton from '@ente/shared/components/LinkButton';
import SingleInputForm, {
SingleInputFormProps,
} from '@ente/shared/components/SingleInputForm';
import EnteSpinner from '@ente/shared/components/EnteSpinner';
import { VerticallyCentered } from '@ente/shared/components/Container';
import InMemoryStore, { MS_KEYS } from '@ente/shared/storage/InMemoryStore';
import { ApiError } from '@ente/shared/error';
import { HttpStatusCode } from 'axios';
import { PageProps } from '@ente/shared/apps/types';
import { UserVerificationResponse } from '@ente/accounts/types/user';
export default function VerifyPage({ appContext, router, appName }: PageProps) {
const [email, setEmail] = useState('');
const [resend, setResend] = useState(0);
useEffect(() => {
const main = async () => {
router.prefetch(PAGES.TWO_FACTOR_VERIFY);
router.prefetch(PAGES.CREDENTIALS);
router.prefetch(PAGES.GENERATE);
const user: User = getData(LS_KEYS.USER);
const keyAttributes: KeyAttributes = getData(
LS_KEYS.KEY_ATTRIBUTES
);
const srpAttributes: SRPAttributes = getData(
LS_KEYS.SRP_ATTRIBUTES
);
if (!user?.email) {
router.push(PAGES.ROOT);
} else if (
keyAttributes?.encryptedKey &&
(user.token || user.encryptedToken)
) {
router.push(PAGES.CREDENTIALS);
} else if (
srpAttributes?.srpUserID &&
!srpAttributes?.isEmailMFAEnabled
) {
router.push(PAGES.CREDENTIALS);
} else {
setEmail(user.email);
}
};
main();
appContext.showNavBar(true);
}, []);
const onSubmit: SingleInputFormProps['callback'] = async (
ott,
setFieldError
) => {
try {
const resp = await verifyOtt(email, ott);
const {
keyAttributes,
encryptedToken,
token,
id,
twoFactorSessionID,
} = resp.data as UserVerificationResponse;
if (twoFactorSessionID) {
setData(LS_KEYS.USER, {
email,
twoFactorSessionID,
isTwoFactorEnabled: true,
});
setIsFirstLogin(true);
router.push(PAGES.TWO_FACTOR_VERIFY);
} else {
setData(LS_KEYS.USER, {
email,
token,
encryptedToken,
id,
isTwoFactorEnabled: false,
});
if (keyAttributes) {
setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
setData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, keyAttributes);
} else {
if (getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES)) {
await putAttributes(
getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES)
);
}
if (getData(LS_KEYS.SRP_SETUP_ATTRIBUTES)) {
const srpSetupAttributes: SRPSetupAttributes = getData(
LS_KEYS.SRP_SETUP_ATTRIBUTES
);
await configureSRP(srpSetupAttributes);
}
}
clearFiles();
setIsFirstLogin(true);
const redirectURL = InMemoryStore.get(MS_KEYS.REDIRECT_URL);
InMemoryStore.delete(MS_KEYS.REDIRECT_URL);
if (keyAttributes?.encryptedKey) {
clearKeys();
router.push(redirectURL ?? PAGES.CREDENTIALS);
} else {
router.push(redirectURL ?? PAGES.GENERATE);
}
}
} catch (e) {
if (e instanceof ApiError) {
if (e?.httpStatusCode === HttpStatusCode.Unauthorized) {
setFieldError(t('INVALID_CODE'));
} else if (e?.httpStatusCode === HttpStatusCode.Gone) {
setFieldError(t('EXPIRED_CODE'));
}
} else {
setFieldError(`${t('UNKNOWN_ERROR')} ${JSON.stringify(e)}`);
}
}
};
const resendEmail = async () => {
setResend(1);
await sendOtt(appName, email);
setResend(2);
setTimeout(() => setResend(0), 3000);
};
if (!email) {
return (
<VerticallyCentered>
<EnteSpinner />
</VerticallyCentered>
);
}
return (
<VerticallyCentered>
<FormPaper>
<FormPaperTitle sx={{ mb: 14, wordBreak: 'break-word' }}>
<Trans
i18nKey="EMAIL_SENT"
components={{
a: <Box color="text.muted" component={'span'} />,
}}
values={{ email }}
/>
</FormPaperTitle>
<Typography color={'text.muted'} mb={2} variant="small">
{t('CHECK_INBOX')}
</Typography>
<SingleInputForm
fieldType="text"
autoComplete="one-time-code"
placeholder={t('ENTER_OTT')}
buttonText={t('VERIFY')}
callback={onSubmit}
/>
<FormPaperFooter style={{ justifyContent: 'space-between' }}>
{resend === 0 && (
<LinkButton onClick={resendEmail}>
{t('RESEND_MAIL')}
</LinkButton>
)}
{resend === 1 && <span>{t('SENDING')}</span>}
{resend === 2 && <span>{t('SENT')}</span>}
<LinkButton onClick={logoutUser}>
{t('CHANGE_EMAIL')}
</LinkButton>
</FormPaperFooter>
</FormPaper>
</VerticallyCentered>
);
}