mirror of
				https://github.com/pelican-dev/panel.git
				synced 2025-10-31 02:36:52 +01:00 
			
		
		
		
	 6a11c32bb2
			
		
	
	
		6a11c32bb2
		
			
		
	
	
	
	
		
			
			Dark rounded background shadow around the QR code makes it more difficult or impossible to scan on some devices. Replaces it with a white shadow to make easier scanning.
		
			
				
	
	
		
			131 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { useContext, useEffect, useState } from 'react';
 | |
| import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';
 | |
| import getTwoFactorTokenData, { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData';
 | |
| import { useFlashKey } from '@/plugins/useFlash';
 | |
| import tw from 'twin.macro';
 | |
| import QRCode from 'qrcode.react';
 | |
| import { Button } from '@/components/elements/button/index';
 | |
| import Spinner from '@/components/elements/Spinner';
 | |
| import { Input } from '@/components/elements/inputs';
 | |
| import CopyOnClick from '@/components/elements/CopyOnClick';
 | |
| import Tooltip from '@/components/elements/tooltip/Tooltip';
 | |
| import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor';
 | |
| import FlashMessageRender from '@/components/FlashMessageRender';
 | |
| import { Actions, useStoreActions } from 'easy-peasy';
 | |
| import { ApplicationStore } from '@/state';
 | |
| import asDialog from '@/hoc/asDialog';
 | |
| 
 | |
| interface Props {
 | |
|     onTokens: (tokens: string[]) => void;
 | |
| }
 | |
| 
 | |
| const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
 | |
|     const [submitting, setSubmitting] = useState(false);
 | |
|     const [value, setValue] = useState('');
 | |
|     const [password, setPassword] = useState('');
 | |
|     const [token, setToken] = useState<TwoFactorTokenData | null>(null);
 | |
|     const { clearAndAddHttpError } = useFlashKey('account:two-step');
 | |
|     const updateUserData = useStoreActions((actions: Actions<ApplicationStore>) => actions.user.updateUserData);
 | |
| 
 | |
|     const { close, setProps } = useContext(DialogWrapperContext);
 | |
| 
 | |
|     useEffect(() => {
 | |
|         getTwoFactorTokenData()
 | |
|             .then(setToken)
 | |
|             .catch((error) => clearAndAddHttpError(error));
 | |
|     }, []);
 | |
| 
 | |
|     useEffect(() => {
 | |
|         setProps((state) => ({ ...state, preventExternalClose: submitting }));
 | |
|     }, [submitting]);
 | |
| 
 | |
|     const submit = (e: React.FormEvent<HTMLFormElement>) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         if (submitting) return;
 | |
| 
 | |
|         setSubmitting(true);
 | |
|         clearAndAddHttpError();
 | |
|         enableAccountTwoFactor(value, password)
 | |
|             .then((tokens) => {
 | |
|                 updateUserData({ useTotp: true });
 | |
|                 onTokens(tokens);
 | |
|             })
 | |
|             .catch((error) => {
 | |
|                 clearAndAddHttpError(error);
 | |
|                 setSubmitting(false);
 | |
|             });
 | |
|     };
 | |
| 
 | |
|     return (
 | |
|         <form id={'enable-totp-form'} onSubmit={submit}>
 | |
|             <FlashMessageRender byKey={'account:two-step'} className={'mt-4'} />
 | |
|             <div className={'flex items-center justify-center w-56 h-56 p-2 bg-gray-50 shadow mx-auto mt-6'}>
 | |
|                 {!token ? (
 | |
|                     <Spinner />
 | |
|                 ) : (
 | |
|                     <QRCode renderAs={'svg'} value={token.image_url_data} css={tw`w-full h-full shadow-none`} />
 | |
|                 )}
 | |
|             </div>
 | |
|             <CopyOnClick text={token?.secret}>
 | |
|                 <p className={'font-mono text-sm text-gray-100 text-center mt-2'}>
 | |
|                     {token?.secret.match(/.{1,4}/g)!.join(' ') || 'Loading...'}
 | |
|                 </p>
 | |
|             </CopyOnClick>
 | |
|             <p id={'totp-code-description'} className={'mt-6'}>
 | |
|                 Scan the QR code above using the two-step authentication app of your choice. Then, enter the 6-digit
 | |
|                 code generated into the field below.
 | |
|             </p>
 | |
|             <Input.Text
 | |
|                 aria-labelledby={'totp-code-description'}
 | |
|                 variant={Input.Text.Variants.Loose}
 | |
|                 value={value}
 | |
|                 onChange={(e) => setValue(e.currentTarget.value)}
 | |
|                 className={'mt-3'}
 | |
|                 placeholder={'000000'}
 | |
|                 type={'text'}
 | |
|                 inputMode={'numeric'}
 | |
|                 autoComplete={'one-time-code'}
 | |
|                 pattern={'\\d{6}'}
 | |
|             />
 | |
|             <label htmlFor={'totp-password'} className={'block mt-3'}>
 | |
|                 Account Password
 | |
|             </label>
 | |
|             <Input.Text
 | |
|                 variant={Input.Text.Variants.Loose}
 | |
|                 className={'mt-1'}
 | |
|                 type={'password'}
 | |
|                 value={password}
 | |
|                 onChange={(e) => setPassword(e.currentTarget.value)}
 | |
|             />
 | |
|             <Dialog.Footer>
 | |
|                 <Button.Text onClick={close}>Cancel</Button.Text>
 | |
|                 <Tooltip
 | |
|                     disabled={password.length > 0 && value.length === 6}
 | |
|                     content={
 | |
|                         !token
 | |
|                             ? 'Waiting for QR code to load...'
 | |
|                             : 'You must enter the 6-digit code and your password to continue.'
 | |
|                     }
 | |
|                     delay={100}
 | |
|                 >
 | |
|                     <Button
 | |
|                         disabled={!token || value.length !== 6 || !password.length}
 | |
|                         type={'submit'}
 | |
|                         form={'enable-totp-form'}
 | |
|                     >
 | |
|                         Enable
 | |
|                     </Button>
 | |
|                 </Tooltip>
 | |
|             </Dialog.Footer>
 | |
|         </form>
 | |
|     );
 | |
| };
 | |
| 
 | |
| export default asDialog({
 | |
|     title: 'Enable Two-Step Verification',
 | |
|     description:
 | |
|         "Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in.",
 | |
| })(ConfigureTwoFactorForm);
 |