Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 52 additions & 69 deletions resources/js/hooks/use-two-factor-auth.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import axios, { AxiosError } from 'axios';

interface EnableResponse {
qrCode: string;
Expand All @@ -16,15 +17,6 @@ interface RecoveryCodesResponse {
}

export function useTwoFactorAuth(initialConfirmed: boolean, initialRecoveryCodes: string[]) {
const csrfToken =
document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';

const headers = {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-CSRF-TOKEN': csrfToken,
'X-Requested-With': 'XMLHttpRequest',
};

const [confirmed, setConfirmed] = useState(initialConfirmed);
const [qrCodeSvg, setQrCodeSvg] = useState('');
Expand All @@ -46,20 +38,19 @@ export function useTwoFactorAuth(initialConfirmed: boolean, initialRecoveryCodes

const enable = async () => {
try {
const response = await fetch(route('two-factor.enable'), {
method: 'POST',
headers,
});

if (response.ok) {
const data: EnableResponse = await response.json();
setQrCodeSvg(data.qrCode);
const response = await axios.post(route('two-factor.enable'));

const data: EnableResponse = response.data;
setQrCodeSvg(data.qrCode);
setSecretKey(data.secret);
} else {
console.error('Error enabling 2FA:', response.statusText);
}
} catch (error) {
console.error('Error enabling 2FA:', error);
let errorMessage = 'Invalid verification code';
if (error instanceof AxiosError && error.response?.data) {
errorMessage = error.response.data.message || errorMessage;
}
setPasscode('');
setError(errorMessage);
}
};

Expand All @@ -69,71 +60,63 @@ export function useTwoFactorAuth(initialConfirmed: boolean, initialRecoveryCodes
const formattedCode = passcode.replace(/\s+/g, '').trim();

try {
const response = await fetch(route('two-factor.confirm'), {
method: 'POST',
headers,
body: JSON.stringify({ code: formattedCode }),
});

if (response.ok) {
const responseData: ConfirmResponse = await response.json();
if (responseData.recovery_codes) {
setRecoveryCodesList(responseData.recovery_codes);
}

setConfirmed(true);
setVerifyStep(false);
setShowModal(false);
setShowingRecoveryCodes(true);
setPasscode('');
setError('');
} else {
const errorData = await response.json();
console.error('Verification error:', errorData.message);
setError(errorData.message || 'Invalid verification code');
setPasscode('');
const response = await axios.post(route('two-factor.confirm'), { code: formattedCode });

const responseData: ConfirmResponse = response.data;
if (responseData.recovery_codes) {
setRecoveryCodesList(responseData.recovery_codes);
}

setConfirmed(true);
setVerifyStep(false);
setShowModal(false);
setShowingRecoveryCodes(true);
setPasscode('');
setError('');

} catch (error) {
console.error('Error confirming 2FA:', error);
setError('An error occurred while confirming 2FA');
let errorMessage = 'An error occurred while confirming 2FA';
if (error instanceof AxiosError && error.response?.data) {
errorMessage = error.response.data.message || errorMessage;
}
setError(errorMessage);
setPasscode('');
}
};

const regenerateRecoveryCodes = async () => {
try {
const response = await fetch(route('two-factor.regenerate-recovery-codes'), {
method: 'POST',
headers,
});

if (response.ok) {
const data: RecoveryCodesResponse = await response.json();
if (data.recovery_codes) {
setRecoveryCodesList(data.recovery_codes);
}
} else {
console.error('Error regenerating codes:', response.statusText);
const response = await axios.post(route('two-factor.regenerate-recovery-codes'));
const data: RecoveryCodesResponse = await response.data;
if (data.recovery_codes) {
setRecoveryCodesList(data.recovery_codes);
}
} catch (error) {
console.error('Error regenerating codes:', error);
let errorMessage = 'An error occurred while regenerating recovery codes';
if (error instanceof AxiosError && error.response?.data) {
errorMessage = error.response.data.message || errorMessage;
}
setError(errorMessage);
}
};

const disable = async () => {
try {
const response = await fetch(route('two-factor.disable'), { method: 'DELETE', headers });

if (response.ok) {
setConfirmed(false);
setShowingRecoveryCodes(false);
setRecoveryCodesList([]);
setQrCodeSvg('');
setSecretKey('');
} else {
console.error('Error disabling 2FA:', response.statusText);
}
await axios.delete(route('two-factor.disable'));

setConfirmed(false);
setShowingRecoveryCodes(false);
setRecoveryCodesList([]);
setQrCodeSvg('');
setSecretKey('');
} catch (error) {
console.error('Error disabling 2FA:', error);
let errorMessage = 'An error occurred while disabling 2FA';
if (error instanceof AxiosError && error.response?.data) {
errorMessage = error.response.data.message || errorMessage;
}
setError(errorMessage);
}
};

Expand Down Expand Up @@ -165,4 +148,4 @@ export function useTwoFactorAuth(initialConfirmed: boolean, initialRecoveryCodes
disable,
copyToClipboard,
};
}
}
1 change: 0 additions & 1 deletion resources/views/app.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">

{{-- Inline script to detect system dark mode preference and apply it immediately --}}
<script>
Expand Down