Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/resources/scripts/components/dashboard/forms/SetupTOTPDialog.tsx
7461 views
1
import React, { useContext, useEffect, useState } from 'react';
2
import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';
3
import getTwoFactorTokenData, { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData';
4
import { useFlashKey } from '@/plugins/useFlash';
5
import tw from 'twin.macro';
6
import QRCode from 'qrcode.react';
7
import { Button } from '@/components/elements/button/index';
8
import Spinner from '@/components/elements/Spinner';
9
import { Input } from '@/components/elements/inputs';
10
import CopyOnClick from '@/components/elements/CopyOnClick';
11
import Tooltip from '@/components/elements/tooltip/Tooltip';
12
import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor';
13
import FlashMessageRender from '@/components/FlashMessageRender';
14
import { Actions, useStoreActions } from 'easy-peasy';
15
import { ApplicationStore } from '@/state';
16
import asDialog from '@/hoc/asDialog';
17
18
interface Props {
19
onTokens: (tokens: string[]) => void;
20
}
21
22
const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
23
const [submitting, setSubmitting] = useState(false);
24
const [value, setValue] = useState('');
25
const [password, setPassword] = useState('');
26
const [token, setToken] = useState<TwoFactorTokenData | null>(null);
27
const { clearAndAddHttpError } = useFlashKey('account:two-step');
28
const updateUserData = useStoreActions((actions: Actions<ApplicationStore>) => actions.user.updateUserData);
29
30
const { close, setProps } = useContext(DialogWrapperContext);
31
32
useEffect(() => {
33
getTwoFactorTokenData()
34
.then(setToken)
35
.catch((error) => clearAndAddHttpError(error));
36
}, []);
37
38
useEffect(() => {
39
setProps((state) => ({ ...state, preventExternalClose: submitting }));
40
}, [submitting]);
41
42
const submit = (e: React.FormEvent<HTMLFormElement>) => {
43
e.preventDefault();
44
e.stopPropagation();
45
46
if (submitting) return;
47
48
setSubmitting(true);
49
clearAndAddHttpError();
50
enableAccountTwoFactor(value, password)
51
.then((tokens) => {
52
updateUserData({ useTotp: true });
53
onTokens(tokens);
54
})
55
.catch((error) => {
56
clearAndAddHttpError(error);
57
setSubmitting(false);
58
});
59
};
60
61
return (
62
<form id={'enable-totp-form'} onSubmit={submit}>
63
<FlashMessageRender byKey={'account:two-step'} className={'mt-4'} />
64
<div className={'flex items-center justify-center w-56 h-56 p-2 bg-gray-50 shadow mx-auto mt-6'}>
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={'font-mono text-sm text-gray-100 text-center mt-2'}>
73
{token?.secret.match(/.{1,4}/g)!.join(' ') || 'Loading...'}
74
</p>
75
</CopyOnClick>
76
<p id={'totp-code-description'} className={'mt-6'}>
77
Scan the QR code above using the two-step authentication app of your choice. Then, enter the 6-digit
78
code generated into the field below.
79
</p>
80
<Input.Text
81
aria-labelledby={'totp-code-description'}
82
variant={Input.Text.Variants.Loose}
83
value={value}
84
onChange={(e) => setValue(e.currentTarget.value)}
85
className={'mt-3'}
86
placeholder={'000000'}
87
type={'text'}
88
inputMode={'numeric'}
89
autoComplete={'one-time-code'}
90
pattern={'\\d{6}'}
91
/>
92
<label htmlFor={'totp-password'} className={'block mt-3'}>
93
Account Password
94
</label>
95
<Input.Text
96
variant={Input.Text.Variants.Loose}
97
className={'mt-1'}
98
type={'password'}
99
value={password}
100
onChange={(e) => setPassword(e.currentTarget.value)}
101
/>
102
<Dialog.Footer>
103
<Button.Text onClick={close}>Cancel</Button.Text>
104
<Tooltip
105
disabled={password.length > 0 && value.length === 6}
106
content={
107
!token
108
? 'Waiting for QR code to load...'
109
: 'You must enter the 6-digit code and your password to continue.'
110
}
111
delay={100}
112
>
113
<Button
114
disabled={!token || value.length !== 6 || !password.length}
115
type={'submit'}
116
form={'enable-totp-form'}
117
>
118
Enable
119
</Button>
120
</Tooltip>
121
</Dialog.Footer>
122
</form>
123
);
124
};
125
126
export default asDialog({
127
title: 'Enable Two-Step Verification',
128
description:
129
"Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in.",
130
})(ConfigureTwoFactorForm);
131
132