diff --git a/src/components/pages/Manage/TotpModal.tsx b/src/components/pages/Manage/TotpModal.tsx index f4f44f07..e982406b 100644 --- a/src/components/pages/Manage/TotpModal.tsx +++ b/src/components/pages/Manage/TotpModal.tsx @@ -1,4 +1,4 @@ -import { Button, Center, Image, Modal, NumberInput, Text, Title } from '@mantine/core'; +import { Button, Center, Image, Modal, NumberInput, PinInput, Text, Title } from '@mantine/core'; import { showNotification } from '@mantine/notifications'; import { useForm } from '@mantine/form'; import { CheckIcon, CrossIcon } from 'components/icons'; @@ -9,9 +9,7 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) { const [secret, setSecret] = useState(''); const [qrCode, setQrCode] = useState(''); const [disabled, setDisabled] = useState(false); - const [code, setCode] = useState(undefined); const [error, setError] = useState(''); - const form = useForm(); useEffect(() => { (async () => { @@ -34,15 +32,15 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) { })(); }, [opened]); - const disableTotp = async () => { + const disableTotp = async (code) => { setDisabled(true); - const str = code.toString(); - if (str.length !== 6) { + if (code.length !== 6) { + setDisabled(false); return setError('Code must be 6 digits'); } const resp = await useFetch('/api/user/mfa/totp', 'DELETE', { - code: str, + code, }); if (resp.error) { @@ -63,16 +61,16 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) { setDisabled(false); }; - const verifyCode = async () => { + const verifyCode = async (code) => { setDisabled(true); - const str = code.toString(); - if (str.length !== 6) { + if (code.length !== 6) { + setDisabled(false); return setError('Code must be 6 digits'); } const resp = await useFetch('/api/user/mfa/totp', 'POST', { secret, - code: str, + code, register: true, }); @@ -94,6 +92,13 @@ export function TotpModal({ opened, onClose, deleteTotp, setTotpEnabled }) { setDisabled(false); }; + const handlePinChange = (value) => { + if (value.length === 6) { + setDisabled(true); + deleteTotp ? disableTotp(value) : verifyCode(value); + } + }; + return ( - QR Code not working? Try manually entering the code into your app: {secret} > )} - { - deleteTotp ? disableTotp() : verifyCode(); - })} - > - setCode(e)} + + - - } - onClick={deleteTotp ? disableTotp : verifyCode} - > - Verify{deleteTotp ? ' and Disable' : ''} - - + size='xl' + /> + + + {error && ( + + {error} + + )} + + {!deleteTotp && ( + + QR Code not working? Try manually entering the code into your app: {secret} + + )} + + } type='submit'> + Verify{deleteTotp ? ' and Disable' : ''} + ); } diff --git a/src/components/pages/Manage/index.tsx b/src/components/pages/Manage/index.tsx index f50aaaa4..d275d081 100644 --- a/src/components/pages/Manage/index.tsx +++ b/src/components/pages/Manage/index.tsx @@ -413,7 +413,7 @@ export default function Manage({ oauth_registration, oauth_providers: raw_oauth_ Two Factor Authentication - {user.totpSecret + {totpEnabled ? 'You have two factor authentication enabled.' : 'You do not have two factor authentication enabled.'} diff --git a/src/pages/auth/login.tsx b/src/pages/auth/login.tsx index 890facd2..36b48736 100644 --- a/src/pages/auth/login.tsx +++ b/src/pages/auth/login.tsx @@ -6,6 +6,8 @@ import { Modal, NumberInput, PasswordInput, + PinInput, + Text, TextInput, Title, } from '@mantine/core'; @@ -23,10 +25,11 @@ export default function Login({ title, user_registration, oauth_registration, oa // totp modal const [totpOpen, setTotpOpen] = useState(false); - const [code, setCode] = useState(undefined); const [error, setError] = useState(''); const [disabled, setDisabled] = useState(false); + const [loading, setLoading] = useState(false); + const oauth_providers = JSON.parse(unparsed); const icons = { @@ -46,8 +49,10 @@ export default function Login({ title, user_registration, oauth_registration, oa }, }); - const onSubmit = async (values) => { + const onSubmit = async (values, code = null) => { + setLoading(true); setError(''); + setDisabled(true); const username = values.username.trim(); const password = values.password.trim(); @@ -65,20 +70,31 @@ export default function Login({ title, user_registration, oauth_registration, oa } else if (res.totp) { if (res.code === 400) { setError('Invalid code'); + setDisabled(false); + setLoading(false); } else { setError(''); + setDisabled(false); + setLoading(false); } setTotpOpen(true); } else { form.setFieldError('username', 'Invalid username'); form.setFieldError('password', 'Invalid password'); + setLoading(false); } } else { await router.push((router.query.url as string) || '/dashboard'); } }; + const handlePinChange = (value) => { + if (value.length === 6) { + onSubmit(form.values, value); + } + }; + useEffect(() => { (async () => { const a = await fetch('/api/user'); @@ -98,24 +114,38 @@ export default function Login({ title, user_registration, oauth_registration, oa title={Two-Factor Authentication Required} size='lg' > - onSubmit(form.values))}> - setCode(e)} + + + - } type='submit'> - Verify & Login - - + {error && ( + + {error} + + )} + + } + type='submit' + > + Verify & Login + @@ -133,7 +163,7 @@ export default function Login({ title, user_registration, oauth_registration, oa {...form.getInputProps('password')} /> - + Login