@@ -0,0 +1,126 @@
|
||||
import { Form } from '@inertiajs/react';
|
||||
import { ShieldCheck } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import Heading from '@/components/heading';
|
||||
import TwoFactorRecoveryCodes from '@/components/two-factor-recovery-codes';
|
||||
import TwoFactorSetupModal from '@/components/two-factor-setup-modal';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useTwoFactorAuth } from '@/hooks/use-two-factor-auth';
|
||||
import { disable, enable } from '@/routes/two-factor';
|
||||
|
||||
export type Props = {
|
||||
canManageTwoFactor?: boolean;
|
||||
requiresConfirmation?: boolean;
|
||||
twoFactorEnabled?: boolean;
|
||||
};
|
||||
|
||||
export default function ManageTwoFactor(props: Props) {
|
||||
const requiresConfirmation = props.requiresConfirmation ?? false;
|
||||
const twoFactorEnabled = props.twoFactorEnabled ?? false;
|
||||
|
||||
const {
|
||||
qrCodeSvg,
|
||||
hasSetupData,
|
||||
manualSetupKey,
|
||||
clearSetupData,
|
||||
clearTwoFactorAuthData,
|
||||
fetchSetupData,
|
||||
recoveryCodesList,
|
||||
fetchRecoveryCodes,
|
||||
errors,
|
||||
} = useTwoFactorAuth();
|
||||
const [showSetupModal, setShowSetupModal] = useState<boolean>(false);
|
||||
const prevTwoFactorEnabled = useRef(twoFactorEnabled);
|
||||
|
||||
useEffect(() => {
|
||||
if (prevTwoFactorEnabled.current && !twoFactorEnabled) {
|
||||
clearTwoFactorAuthData();
|
||||
}
|
||||
|
||||
prevTwoFactorEnabled.current = twoFactorEnabled;
|
||||
}, [twoFactorEnabled, clearTwoFactorAuthData]);
|
||||
|
||||
if (!(props.canManageTwoFactor ?? false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Heading
|
||||
variant="small"
|
||||
title="Two-factor authentication"
|
||||
description="Manage your two-factor authentication settings"
|
||||
/>
|
||||
{twoFactorEnabled ? (
|
||||
<div className="flex flex-col items-start justify-start space-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
You will be prompted for a secure, random pin during
|
||||
login, which you can retrieve from the TOTP-supported
|
||||
application on your phone.
|
||||
</p>
|
||||
|
||||
<div className="relative inline">
|
||||
<Form {...disable.form()}>
|
||||
{({ processing }) => (
|
||||
<Button
|
||||
variant="destructive"
|
||||
type="submit"
|
||||
disabled={processing}
|
||||
>
|
||||
Disable 2FA
|
||||
</Button>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<TwoFactorRecoveryCodes
|
||||
recoveryCodesList={recoveryCodesList}
|
||||
fetchRecoveryCodes={fetchRecoveryCodes}
|
||||
errors={errors}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-start justify-start space-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
When you enable two-factor authentication, you will be
|
||||
prompted for a secure pin during login. This pin can be
|
||||
retrieved from a TOTP-supported application on your
|
||||
phone.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
{hasSetupData ? (
|
||||
<Button onClick={() => setShowSetupModal(true)}>
|
||||
<ShieldCheck />
|
||||
Continue setup
|
||||
</Button>
|
||||
) : (
|
||||
<Form
|
||||
{...enable.form()}
|
||||
onSuccess={() => setShowSetupModal(true)}
|
||||
>
|
||||
{({ processing }) => (
|
||||
<Button type="submit" disabled={processing}>
|
||||
Enable 2FA
|
||||
</Button>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<TwoFactorSetupModal
|
||||
isOpen={showSetupModal}
|
||||
onClose={() => setShowSetupModal(false)}
|
||||
requiresConfirmation={requiresConfirmation}
|
||||
twoFactorEnabled={twoFactorEnabled}
|
||||
qrCodeSvg={qrCodeSvg}
|
||||
manualSetupKey={manualSetupKey}
|
||||
clearSetupData={clearSetupData}
|
||||
fetchSetupData={fetchSetupData}
|
||||
errors={errors}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user