Path: blob/develop/resources/scripts/components/dashboard/forms/SetupTOTPDialog.tsx
7454 views
import { useContext, useEffect, useState } from 'react';1import * as React from 'react';2import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';3import getTwoFactorTokenData, { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData';4import { useFlashKey } from '@/plugins/useFlash';5import tw from 'twin.macro';6import QRCode from 'qrcode.react';7import { Button } from '@/components/elements/button/index';8import Spinner from '@/components/elements/Spinner';9import { Input } from '@/components/elements/inputs';10import CopyOnClick from '@/components/elements/CopyOnClick';11import Tooltip from '@/components/elements/tooltip/Tooltip';12import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor';13import FlashMessageRender from '@/components/FlashMessageRender';14import { Actions, useStoreActions } from 'easy-peasy';15import { ApplicationStore } from '@/state';16import asDialog from '@/hoc/asDialog';1718interface Props {19onTokens: (tokens: string[]) => void;20}2122const ConfigureTwoFactorForm = ({ onTokens }: Props) => {23const [submitting, setSubmitting] = useState(false);24const [value, setValue] = useState('');25const [password, setPassword] = useState('');26const [token, setToken] = useState<TwoFactorTokenData | null>(null);27const { clearAndAddHttpError } = useFlashKey('account:two-step');28const updateUserData = useStoreActions((actions: Actions<ApplicationStore>) => actions.user.updateUserData);2930const { close, setProps } = useContext(DialogWrapperContext);3132useEffect(() => {33getTwoFactorTokenData()34.then(setToken)35.catch(error => clearAndAddHttpError(error));36}, []);3738useEffect(() => {39setProps(state => ({ ...state, preventExternalClose: submitting }));40}, [submitting]);4142const submit = (e: React.FormEvent<HTMLFormElement>) => {43e.preventDefault();44e.stopPropagation();4546if (submitting) return;4748setSubmitting(true);49clearAndAddHttpError();50enableAccountTwoFactor(value, password)51.then(tokens => {52updateUserData({ useTotp: true });53onTokens(tokens);54})55.catch(error => {56clearAndAddHttpError(error);57setSubmitting(false);58});59};6061return (62<form id={'enable-totp-form'} onSubmit={submit}>63<FlashMessageRender byKey={'account:two-step'} className={'mt-4'} />64<div className={'mx-auto mt-6 flex h-56 w-56 items-center justify-center bg-slate-50 p-2 shadow'}>65{!token ? (66<Spinner />67) : (68<QRCode renderAs={'svg'} value={token.image_url_data} css={tw`w-full h-full shadow-none`} />69)}70</div>71<CopyOnClick text={token?.secret}>72<p className={'mt-2 text-center font-mono text-sm text-slate-100'}>73{token?.secret.match(/.{1,4}/g)!.join(' ') || 'Loading...'}74</p>75</CopyOnClick>76<p id={'totp-code-description'} className={'mt-6'}>77Scan the QR code above using the two-step authentication app of your choice. Then, enter the 6-digit78code generated into the field below.79</p>80<Input.Text81aria-labelledby={'totp-code-description'}82variant={Input.Text.Variants.Loose}83value={value}84onChange={e => setValue(e.currentTarget.value)}85className={'mt-3'}86placeholder={'000000'}87type={'text'}88inputMode={'numeric'}89autoComplete={'one-time-code'}90pattern={'\\d{6}'}91/>92<label htmlFor={'totp-password'} className={'mt-3 block'}>93Account Password94</label>95<Input.Text96variant={Input.Text.Variants.Loose}97className={'mt-1'}98type={'password'}99value={password}100onChange={e => setPassword(e.currentTarget.value)}101/>102<Dialog.Footer>103<Button.Text onClick={close}>Cancel</Button.Text>104<Tooltip105disabled={password.length > 0 && value.length === 6}106content={107!token108? 'Waiting for QR code to load...'109: 'You must enter the 6-digit code and your password to continue.'110}111delay={100}112>113<Button114disabled={!token || value.length !== 6 || !password.length}115type={'submit'}116form={'enable-totp-form'}117>118Enable119</Button>120</Tooltip>121</Dialog.Footer>122</form>123);124};125126export default asDialog({127title: 'Enable Two-Step Verification',128description:129"Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in.",130})(ConfigureTwoFactorForm);131132133