Skip to content

Commit 23fd86c

Browse files
committed
fixing auth and 2fa func
1 parent cefb3df commit 23fd86c

File tree

4 files changed

+76
-93
lines changed

4 files changed

+76
-93
lines changed

app/Http/Controllers/Auth/AuthenticatedSessionController.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ public function store(LoginRequest $request): RedirectResponse
3232
// Find user by email
3333
$user = \App\Models\User::where('email', $request->email)->first();
3434

35-
// If user has 2FA enabled and confirmed, redirect to challenge
36-
if ($user && $user->two_factor_confirmed_at) {
35+
// If user exists, password is correct, and 2FA is enabled, redirect to challenge
36+
if ($user && $user->two_factor_confirmed_at && \Illuminate\Support\Facades\Hash::check($request->password, $user->password)) {
3737
$request->session()->put('login.id', $user->getKey());
3838
$request->session()->put('login.remember', $request->boolean('remember'));
39+
// Optionally clear any previous errors
3940
return redirect()->route('two-factor.challenge');
4041
}
4142

42-
// Proceed with normal authentication
43+
// Proceed with normal authentication (this will handle errors and login)
4344
$request->authenticate();
4445
$request->session()->regenerate();
4546

Lines changed: 62 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { useState } from 'react';
22
import { Head, useForm } from '@inertiajs/react';
3-
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
43
import { Button } from '@/components/ui/button';
5-
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
64
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp';
75
import { Input } from '@/components/ui/input';
86
import { Label } from '@/components/ui/label';
97
import InputError from '@/components/input-error';
8+
import AuthLayout from '@/layouts/auth-layout';
9+
import TextLink from '@/components/text-link';
1010

1111
export default function TwoFactorChallenge() {
12-
const [activeTab, setActiveTab] = useState('code');
12+
const [recovery, setRecovery] = useState(false);
1313

1414
const { data, setData, post, processing, errors } = useForm({
1515
code: '',
@@ -27,74 +27,67 @@ export default function TwoFactorChallenge() {
2727
};
2828

2929
return (
30-
<div className="flex min-h-screen flex-col items-center justify-center">
30+
<AuthLayout
31+
title={recovery ? 'Recovery Code' : 'Authentication Code'}
32+
description={recovery
33+
? 'Please confirm access to your account by entering one of your emergency recovery codes.'
34+
: 'Enter the authentication code provided by your authenticator application.'}
35+
>
3136
<Head title="Two Factor Authentication" />
3237

33-
<div className="w-full max-w-md">
34-
<Card>
35-
<CardHeader className="space-y-1">
36-
<CardTitle className="text-2xl">Two Factor Authentication</CardTitle>
37-
<CardDescription>
38-
Confirm access to your account by entering the authentication code provided by your authenticator application or one of your recovery codes.
39-
</CardDescription>
40-
</CardHeader>
41-
<CardContent>
42-
<Tabs defaultValue="code" value={activeTab} onValueChange={setActiveTab}>
43-
<TabsList className="grid w-full grid-cols-2">
44-
<TabsTrigger value="code">Code</TabsTrigger>
45-
<TabsTrigger value="recovery">Recovery Code</TabsTrigger>
46-
</TabsList>
47-
<TabsContent value="code">
48-
<form onSubmit={submitCode} className="space-y-4 pt-4">
49-
<div className="grid gap-2">
50-
<Label htmlFor="code">Authentication Code</Label>
51-
<InputOTP
52-
maxLength={6}
53-
value={data.code}
54-
onChange={(value) => setData('code', value)}
55-
>
56-
<InputOTPGroup>
57-
{Array.from({ length: 6 }).map((_, index) => (
58-
<InputOTPSlot key={index} index={index} />
59-
))}
60-
</InputOTPGroup>
61-
</InputOTP>
62-
<InputError message={errors.code} />
63-
</div>
64-
<Button type="submit" className="w-full" disabled={processing}>
65-
Verify
66-
</Button>
67-
</form>
68-
</TabsContent>
69-
<TabsContent value="recovery">
70-
<form onSubmit={submitRecoveryCode} className="space-y-4 pt-4">
71-
<div className="grid gap-2">
72-
<Label htmlFor="recovery_code">Recovery Code</Label>
73-
<Input
74-
id="recovery_code"
75-
type="text"
76-
value={data.recovery_code}
77-
onChange={(e) => setData('recovery_code', e.target.value)}
78-
className="block w-full"
79-
autoComplete="one-time-code"
80-
placeholder="Enter recovery code"
81-
/>
82-
<InputError message={errors.recovery_code} />
83-
</div>
84-
<Button type="submit" className="w-full" disabled={processing}>
85-
Verify
86-
</Button>
87-
</form>
88-
</TabsContent>
89-
</Tabs>
90-
</CardContent>
91-
<CardFooter className="flex flex-col">
92-
<p className="mt-4 text-center text-sm text-gray-500">
93-
Lost your device? Please contact your administrator.
94-
</p>
95-
</CardFooter>
96-
</Card>
38+
{!recovery ? (
39+
<form onSubmit={submitCode} className="space-y-4">
40+
<div className="flex flex-col items-center justify-center text-center">
41+
<InputOTP
42+
maxLength={6}
43+
value={data.code}
44+
onChange={(value) => setData('code', value)}
45+
autoFocus
46+
>
47+
<InputOTPGroup>
48+
{Array.from({ length: 6 }).map((_, index) => (
49+
<InputOTPSlot key={index} index={index} />
50+
))}
51+
</InputOTPGroup>
52+
</InputOTP>
53+
<InputError message={errors.code} />
54+
</div>
55+
<Button type="submit" className="w-full" disabled={processing}>
56+
Continue
57+
</Button>
58+
</form>
59+
) : (
60+
<form onSubmit={submitRecoveryCode} className="space-y-4">
61+
<Input
62+
id="recovery_code"
63+
type="text"
64+
value={data.recovery_code}
65+
onChange={(e) => setData('recovery_code', e.target.value)}
66+
className="block w-full"
67+
autoComplete="one-time-code"
68+
placeholder="Enter recovery code"
69+
required
70+
/>
71+
<InputError message={errors.recovery_code} />
72+
<Button type="submit" className="w-full" disabled={processing}>
73+
Continue
74+
</Button>
75+
</form>
76+
)}
77+
78+
<div className="space-x-0.5 text-center text-sm leading-5 text-muted-foreground">
79+
<span className="opacity-80">or you can </span>
80+
<button
81+
type="button"
82+
className="font-medium underline opacity-80 cursor-pointer bg-transparent border-0 p-0"
83+
onClick={() => setRecovery(!recovery)}
84+
>
85+
{recovery
86+
? 'login using an authentication code'
87+
: 'login using a recovery code'}
88+
</button>
9789
</div>
98-
</div>
90+
</AuthLayout>
9991
);
10092
}
93+

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

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default function TwoFactor({ enabled: initialEnabled, confirmed: initialC
4040
const [recoveryCodesList, setRecoveryCodesList] = useState(recoveryCodes);
4141
const [copied, setCopied] = useState(false);
4242

43-
const { data, setData, post, delete: destroy, processing, reset, recentlySuccessful, errors } = useForm({
43+
const { data, setData, post, delete: destroy, processing, reset, errors } = useForm({
4444
code: '',
4545
});
4646

@@ -233,12 +233,12 @@ export default function TwoFactor({ enabled: initialEnabled, confirmed: initialC
233233
</>
234234
) : (
235235
<>
236-
<div className="mt-4">
237-
<Label htmlFor="code">Verification Code</Label>
236+
<div>
238237
<InputOTP
239238
maxLength={6}
240239
value={data.code}
241240
onChange={(value) => setData('code', value)}
241+
autoFocus
242242
>
243243
<InputOTPGroup>
244244
{Array.from({ length: 6 }).map((_, index) => (
@@ -284,9 +284,9 @@ export default function TwoFactor({ enabled: initialEnabled, confirmed: initialC
284284
</p>
285285

286286
<div>
287-
<div className="flex items-center p-4 bg-stone-50 dark:bg-stone-800 border border-stone-200 dark:border-stone-700 rounded-t-xl">
287+
<div className="flex items-start p-4 bg-stone-50 dark:bg-stone-800 border border-stone-200 dark:border-stone-700 rounded-t-xl">
288288
<LockKeyhole className="size-5 mr-2 text-stone-500" />
289-
<div>
289+
<div className="space-y-1">
290290
<h3 className="font-medium">2FA Recovery Codes</h3>
291291
<p className="text-sm text-stone-500 dark:text-stone-400">
292292
Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password manager.
@@ -348,17 +348,6 @@ export default function TwoFactor({ enabled: initialEnabled, confirmed: initialC
348348
</div>
349349
</div>
350350
)}
351-
352-
353-
<Transition
354-
show={recentlySuccessful}
355-
enter="transition ease-in-out"
356-
enterFrom="opacity-0"
357-
leave="transition ease-in-out"
358-
leaveTo="opacity-0"
359-
>
360-
<p className="text-sm text-neutral-600">Saved</p>
361-
</Transition>
362351
</div>
363352
</SettingsLayout>
364353
</AppLayout>

routes/auth.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
->name('login');
2222

2323
Route::post('login', [AuthenticatedSessionController::class, 'store']);
24-
25-
Route::get('two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'create'])
26-
->name('two-factor.challenge');
27-
28-
Route::post('two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'store']);
2924

3025
Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
3126
->name('password.request');
@@ -60,3 +55,8 @@
6055
Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
6156
->name('logout');
6257
});
58+
59+
Route::get('two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'create'])
60+
->name('two-factor.challenge');
61+
62+
Route::post('two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'store']);

0 commit comments

Comments
 (0)