Skip to content

Commit 347eda0

Browse files
committed
fixing the 2fa codes from regenerating before submitted
1 parent ef8a672 commit 347eda0

File tree

4 files changed

+73
-25
lines changed

4 files changed

+73
-25
lines changed

app/Actions/TwoFactorAuth/GenerateQrCodeAndSecretKey.php

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,34 @@ class GenerateQrCodeAndSecretKey
2020
*/
2121
public function __invoke($user): array
2222
{
23-
24-
$google2fa = new Google2FA;
25-
$secret_key = $google2fa->generateSecretKey();
26-
27-
$this->companyName = 'Auth';
28-
if (is_string(config('app.name'))) {
29-
$this->companyName = config('app.name');
30-
}
31-
23+
// Create a new Google2FA instance with explicit configuration
24+
$google2fa = new Google2FA();
25+
$google2fa->setOneTimePasswordLength(6);
26+
27+
// Generate a standard 16-character secret key
28+
$secret_key = $google2fa->generateSecretKey(16);
29+
30+
// Set company name from config
31+
$this->companyName = config('app.name', 'Laravel');
32+
33+
// Generate the QR code URL
3234
$g2faUrl = $google2fa->getQRCodeUrl(
3335
$this->companyName,
34-
(string) $user->email,
36+
$user->email,
3537
$secret_key
3638
);
37-
39+
40+
// Create the QR code image
3841
$writer = new Writer(
3942
new ImageRenderer(
40-
new RendererStyle(800),
43+
new RendererStyle(400),
4144
new SvgImageBackEnd()
4245
)
4346
);
44-
47+
48+
// Generate the QR code as a base64 encoded SVG
4549
$qrcode_image = base64_encode($writer->writeString($g2faUrl));
46-
50+
4751
return [$qrcode_image, $secret_key];
4852

4953
}

app/Actions/TwoFactorAuth/VerifyTwoFactorCode.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@ class VerifyTwoFactorCode
1515
*/
1616
public function __invoke(string $secret, string $code): bool
1717
{
18+
// Clean the code (remove spaces and non-numeric characters)
19+
$code = preg_replace('/[^0-9]/', '', $code);
20+
21+
// Create a new Google2FA instance with explicit configuration
1822
$google2fa = new Google2FA();
19-
return $google2fa->verifyKey($secret, $code);
23+
$google2fa->setWindow(8); // Allow for some time drift
24+
$google2fa->setOneTimePasswordLength(6); // Ensure 6-digit codes
25+
26+
try {
27+
return $google2fa->verify($code, $secret);
28+
} catch (\Exception $e) {
29+
return false;
30+
}
2031
}
2132
}

app/Http/Controllers/Settings/TwoFactorAuthController.php

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ class TwoFactorAuthController extends Controller
2222
*/
2323
public function edit(Request $request)
2424
{
25-
$recoveryCodes = $request->user()->two_factor_secret ? json_decode(decrypt($request->user()->two_factor_recovery_codes)) : [];
25+
26+
2627
return Inertia::render('settings/two-factor', [
27-
'enabled' => !empty($request->user()->two_factor_secret),
28-
'qrCode' => $request->user()->two_factor_secret ? true : false,
29-
'recoveryCodes' => $recoveryCodes,
28+
'enabled' => !is_null($request->user()->two_factor_secret),
29+
'confirmed' => !is_null($request->user()->two_factor_confirmed_at),
30+
'recoveryCodes' => $request->user()->two_factor_secret ? json_decode(decrypt($request->user()->two_factor_recovery_codes)) : [],
3031
]);
3132
}
3233

@@ -82,17 +83,21 @@ public function disable(Request $request)
8283
public function confirm(Request $request)
8384
{
8485
$request->validate([
85-
'code' => 'required|string|min:6',
86+
'code' => 'required|string',
8687
]);
8788

89+
// Get the secret key from the user's record
8890
$secret = decrypt($request->user()->two_factor_secret);
91+
92+
// Verify the code
8993
$valid = app(VerifyTwoFactorCode::class)($secret, $request->code);
9094

9195
if ($valid) {
9296
app(ConfirmTwoFactorAuthentication::class)($request->user());
9397
return back()->with('status', 'two-factor-authentication-confirmed');
9498
}
9599

100+
96101
return back()->withErrors(['code' => 'The provided two-factor authentication code was invalid.']);
97102
}
98103

@@ -107,9 +112,29 @@ public function qrCode(Request $request)
107112
if (empty($request->user()->two_factor_secret)) {
108113
return response('', 404);
109114
}
110-
111-
[$qrCode, $secret] = app(GenerateQrCodeAndSecretKey::class)($request->user());
112-
115+
116+
// Get the existing secret key instead of generating a new one
117+
$secret = decrypt($request->user()->two_factor_secret);
118+
119+
// Generate QR code based on the existing secret
120+
$google2fa = new \PragmaRX\Google2FA\Google2FA();
121+
$companyName = config('app.name', 'Laravel');
122+
123+
$g2faUrl = $google2fa->getQRCodeUrl(
124+
$companyName,
125+
$request->user()->email,
126+
$secret
127+
);
128+
129+
$writer = new \BaconQrCode\Writer(
130+
new \BaconQrCode\Renderer\ImageRenderer(
131+
new \BaconQrCode\Renderer\RendererStyle\RendererStyle(400),
132+
new \BaconQrCode\Renderer\Image\SvgImageBackEnd()
133+
)
134+
);
135+
136+
$qrCode = base64_encode($writer->writeString($g2faUrl));
137+
113138
return response()->json([
114139
'svg' => $qrCode,
115140
'secret' => $secret

resources/js/pages/settings/two-factor.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ const breadcrumbs: BreadcrumbItem[] = [
2424

2525
interface TwoFactorProps {
2626
enabled: boolean;
27+
confirmed: boolean;
2728
qrCode: boolean;
2829
recoveryCodes: string[];
2930
}
3031

31-
export default function TwoFactor({ enabled: initialEnabled, qrCode, recoveryCodes }: TwoFactorProps) {
32+
export default function TwoFactor({ enabled: initialEnabled, confirmed: initialConfirmed, qrCode, recoveryCodes }: TwoFactorProps) {
3233
const [enabled, setEnabled] = useState(initialEnabled);
33-
const [confirmed, setConfirmed] = useState(initialEnabled);
34+
const [confirmed, setConfirmed] = useState(initialConfirmed);
3435
const [showModal, setShowModal] = useState(false);
3536
const [verifyStep, setVerifyStep] = useState(false);
3637
const [qrCodeSvg, setQrCodeSvg] = useState('');
@@ -54,6 +55,7 @@ export default function TwoFactor({ enabled: initialEnabled, qrCode, recoveryCod
5455
post(route('two-factor.enable'), {
5556
preserveScroll: true,
5657
onSuccess: async () => {
58+
// Only set enabled to true, but not confirmed yet
5759
setEnabled(true);
5860
const response = await fetch(route('two-factor.qr-code'));
5961
const data = await response.json();
@@ -78,6 +80,12 @@ export default function TwoFactor({ enabled: initialEnabled, qrCode, recoveryCod
7880
return;
7981
}
8082

83+
// Make sure the code is properly formatted (no spaces, exactly 6 digits)
84+
const formattedCode = data.code.replace(/\s+/g, '').trim();
85+
86+
// Set the formatted code back to the form data
87+
setData('code', formattedCode);
88+
8189
post(route('two-factor.confirm'), {
8290
preserveScroll: true,
8391
onSuccess: () => {

0 commit comments

Comments
 (0)