refactor: remove two-factor authentication management features from security settings

This commit is contained in:
Yoga Pangestu 2026-04-30 23:24:31 +07:00
parent 098c2a9cf1
commit 2fe3173817
2 changed files with 55 additions and 204 deletions

View File

@ -4,44 +4,18 @@
use App\Http\Controllers\Controller;
use App\Http\Requests\Settings\PasswordUpdateRequest;
use App\Http\Requests\Settings\TwoFactorAuthenticationRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Inertia\Inertia;
use Inertia\Response;
use Laravel\Fortify\Features;
class SecurityController extends Controller implements HasMiddleware
class SecurityController extends Controller
{
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return Features::canManageTwoFactorAuthentication()
&& Features::optionEnabled(Features::twoFactorAuthentication(), 'confirmPassword')
? [new Middleware('password.confirm', only: ['edit'])]
: [];
}
/**
* Show the user's security settings page.
*/
public function edit(TwoFactorAuthenticationRequest $request): Response
public function edit(): Response
{
$props = [
'canManageTwoFactor' => Features::canManageTwoFactorAuthentication(),
];
if (Features::canManageTwoFactorAuthentication()) {
$request->ensureStateIsValid();
$props['twoFactorEnabled'] = $request->user()->hasEnabledTwoFactorAuthentication();
$props['requiresConfirmation'] = Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm');
}
return Inertia::render('settings/security', $props);
return Inertia::render('settings/security');
}
/**

View File

@ -1,64 +1,25 @@
import SecurityController from '@/actions/App/Http/Controllers/Settings/SecurityController';
import PasswordInput from '@/components/password-input';
import TwoFactorRecoveryCodes from '@/components/two-factor-recovery-codes';
import TwoFactorSetupModal from '@/components/two-factor-setup-modal';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Field, FieldError } from "@/components/ui/field";
import { Label } from '@/components/ui/label';
import { useTwoFactorAuth } from '@/hooks/use-two-factor-auth';
import { edit } from '@/routes/security';
import { disable, enable } from '@/routes/two-factor';
import { Head, useForm } from '@inertiajs/react';
import { Loader, Save, ShieldCheck } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { Loader, Save } from 'lucide-react';
import { useRef } from 'react';
import { toast } from 'sonner';
type Props = {
canManageTwoFactor?: boolean;
requiresConfirmation?: boolean;
twoFactorEnabled?: boolean;
};
export default function Security({
canManageTwoFactor = false,
requiresConfirmation = false,
twoFactorEnabled = false,
}: Props) {
export default function Security() {
const passwordInput = useRef<HTMLInputElement>(null);
const currentPasswordInput = useRef<HTMLInputElement>(null);
const {
qrCodeSvg,
hasSetupData,
manualSetupKey,
clearSetupData,
clearTwoFactorAuthData,
fetchSetupData,
recoveryCodesList,
fetchRecoveryCodes,
errors: tfaErrors,
} = useTwoFactorAuth();
const [showSetupModal, setShowSetupModal] = useState<boolean>(false);
const prevTwoFactorEnabled = useRef(twoFactorEnabled);
const { data, setData, put, processing, errors, reset } = useForm({
current_password: '',
password: '',
password_confirmation: '',
});
const { post: postEnable, processing: processingEnable } = useForm({});
const { post: postDisable, processing: processingDisable } = useForm({});
useEffect(() => {
if (prevTwoFactorEnabled.current && !twoFactorEnabled) {
clearTwoFactorAuthData();
}
prevTwoFactorEnabled.current = twoFactorEnabled;
}, [twoFactorEnabled, clearTwoFactorAuthData]);
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
put(SecurityController.update().url, {
@ -78,18 +39,6 @@ export default function Security({
});
};
const handleEnable2FA = (e: React.FormEvent) => {
e.preventDefault();
postEnable(enable().url, {
onSuccess: () => setShowSetupModal(true),
});
};
const handleDisable2FA = (e: React.FormEvent) => {
e.preventDefault();
postDisable(disable().url);
};
return (
<div className="flex flex-col gap-6">
<Head title="Keamanan Akun" />
@ -97,131 +46,59 @@ export default function Security({
<h1 className="sr-only">Keamanan Akun</h1>
<form onSubmit={onSubmit} className="space-y-6">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 space-y-6">
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm">
<CardHeader className="border-b">
<CardTitle>Perbarui Kata Sandi</CardTitle>
</CardHeader>
<CardContent className="space-y-6 pt-6">
<Field>
<Label htmlFor="current_password" required>Kata Sandi Saat Ini</Label>
<PasswordInput
id="current_password"
ref={currentPasswordInput}
value={data.current_password}
onChange={(e) => setData('current_password', e.target.value)}
className="w-full"
autoComplete="current-password"
placeholder="Masukkan kata sandi saat ini"
/>
<FieldError error={errors.current_password} label="Kata sandi saat ini" className="text-xs" />
</Field>
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm">
<CardHeader className="border-b">
<CardTitle>Perbarui Kata Sandi</CardTitle>
</CardHeader>
<CardContent className="space-y-6 pt-6">
<Field>
<Label htmlFor="current_password" required>Kata Sandi Saat Ini</Label>
<PasswordInput
id="current_password"
ref={currentPasswordInput}
value={data.current_password}
onChange={(e) => setData('current_password', e.target.value)}
className="w-full"
autoComplete="current-password"
placeholder="Masukkan kata sandi saat ini"
/>
<FieldError error={errors.current_password} label="Kata sandi saat ini" className="text-xs" />
</Field>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Field>
<Label htmlFor="password" required>Kata Sandi Baru</Label>
<PasswordInput
id="password"
ref={passwordInput}
value={data.password}
onChange={(e) => setData('password', e.target.value)}
placeholder="Masukkan kata sandi baru"
autoComplete="new-password"
/>
<FieldError error={errors.password} label="Kata sandi baru" className="text-xs" />
</Field>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Field>
<Label htmlFor="password" required>Kata Sandi Baru</Label>
<PasswordInput
id="password"
ref={passwordInput}
value={data.password}
onChange={(e) => setData('password', e.target.value)}
placeholder="Masukkan kata sandi baru"
autoComplete="new-password"
/>
<FieldError error={errors.password} label="Kata sandi baru" className="text-xs" />
</Field>
<Field>
<Label htmlFor="password_confirmation" required>Konfirmasi Kata Sandi Baru</Label>
<PasswordInput
id="password_confirmation"
value={data.password_confirmation}
onChange={(e) => setData('password_confirmation', e.target.value)}
placeholder="Ulangi kata sandi baru"
autoComplete="new-password"
/>
<FieldError error={errors.password_confirmation} label="Konfirmasi kata sandi baru" className="text-xs" />
</Field>
</div>
</CardContent>
</Card>
<Field>
<Label htmlFor="password_confirmation" required>Konfirmasi Kata Sandi Baru</Label>
<PasswordInput
id="password_confirmation"
value={data.password_confirmation}
onChange={(e) => setData('password_confirmation', e.target.value)}
placeholder="Ulangi kata sandi baru"
autoComplete="new-password"
/>
<FieldError error={errors.password_confirmation} label="Konfirmasi kata sandi baru" className="text-xs" />
</Field>
</div>
</CardContent>
</Card>
<Button type="submit" disabled={processing}>
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
{processing ? 'Menyimpan...' : 'Simpan'}
</Button>
{canManageTwoFactor && (
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm">
<CardHeader className="border-b">
<CardTitle>Autentikasi Dua Faktor (2FA)</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
{twoFactorEnabled
? "Anda akan diminta memasukkan PIN acak yang aman saat masuk, yang dapat Anda ambil dari aplikasi pendukung TOTP di ponsel Anda."
: "Saat Anda mengaktifkan autentikasi dua faktor, Anda akan diminta PIN aman saat masuk. PIN ini dapat diambil dari aplikasi pendukung TOTP di ponsel Anda."
}
</p>
{twoFactorEnabled ? (
<div className="space-y-4">
<Button
type="button"
variant="destructive"
onClick={handleDisable2FA}
disabled={processingDisable}
>
{processingDisable ? <Loader className="size-4 animate-spin" /> : null}
Nonaktifkan 2FA
</Button>
<TwoFactorRecoveryCodes
recoveryCodesList={recoveryCodesList}
fetchRecoveryCodes={fetchRecoveryCodes}
errors={tfaErrors}
/>
</div>
) : (
<div>
{hasSetupData ? (
<Button type="button" onClick={() => setShowSetupModal(true)}>
<ShieldCheck className="mr-2 h-4 w-4" />
Lanjutkan Penyiapan
</Button>
) : (
<Button
type="button"
onClick={handleEnable2FA}
disabled={processingEnable}
>
{processingEnable ? <Loader className="size-4 animate-spin" /> : null}
Aktifkan 2FA
</Button>
)}
</div>
)}
</CardContent>
</Card>
)}
</div>
</div>
<Button type="submit" disabled={processing}>
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
{processing ? 'Menyimpan...' : 'Simpan'}
</Button>
</form>
{canManageTwoFactor && (
<TwoFactorSetupModal
isOpen={showSetupModal}
onClose={() => setShowSetupModal(false)}
requiresConfirmation={requiresConfirmation}
twoFactorEnabled={twoFactorEnabled}
qrCodeSvg={qrCodeSvg}
manualSetupKey={manualSetupKey}
clearSetupData={clearSetupData}
fetchSetupData={fetchSetupData}
errors={tfaErrors}
/>
)}
</div>
);
}