I had similar issue. I deleted user and created a new user and it worked.
Laravel Fortify 2fa not authenticating
Hey everyone, I have a Laravel 10.10 application scaffolded with Laravel Breeze and using the ReactJS front end. Since Breeze does not come with two-factor authentication, I have attempted to create the UI for it and use the endpoints provided by Fortify. So just for context, I'm not using the default User model to apply the 2fa functionality. I have created a model called Faculty and need 2fa functionality for that model. Everything but this: https://laravel.com/docs/10.x/fortify#authenticating-with-two-factor-authentication works.
The routes to enable, disable, confirm setting up 2fa, generating the QR code, and backup recvoery codes all work. I just cannot authenticate with 2fa.
Here's a brief part of the front end for the two-factor-challenge route:
export default function TwoFactorChallenge({ message }) {
const { data, setData, post, processing, reset } = useForm({
code: ''
});
const [error, setError] = useState(null);
const submit = (e) => {
e.preventDefault();
post(route('two-factor.login'));
};
Here's the backend logic for authentication:
public function authenticate(Request $request)
{
try {
$ip = Faculty::where('email', $request->email)->value('client_ip');
if($ip){
$decryptedIP = Crypt::decryptString($ip);
}
else{
$decryptedIP = null;
}
$request->validate([
'email' => 'required|email|exists:faculty',
'password' => 'required',
], [
'email.required' => 'Your email is required',
'email.email' => 'You\'ve entered an invalid email',
'email.exists' => 'An account for that email does not exist',
'password.required' => 'Your password is required',
]);
$user = Faculty::where('email', $request->email)->first();
$banned = $this->isBanned($user->faculty_id);
if ($banned !== null) {
return redirect()->back()->withErrors(['auth_error'=>$banned]);
}
$rateLimitKey = $request->ip();
$remainingAttempts = 5 - RateLimiter::attempts($rateLimitKey);
// Set the lockout duration to 10 minutes (600 seconds)
$lockoutDuration = 600;
if (RateLimiter::tooManyAttempts($rateLimitKey, 5)) {
$minutesRemaining = ceil(RateLimiter::availableIn($rateLimitKey) / 60);
return $this->rateLimitExceededResponse($minutesRemaining);
}
$rememberMe = $request->input('remember');
if (Auth::guard('faculty')->attempt(['email' => $request->input('email'), 'password' => $request->input('password')], $rememberMe)) {
$user = Auth::guard('faculty')->user();
// if ($user->two_factor_secret && $user->two_factor_confirmed_at !== null) {
// return redirect('auth/two-factor-challenge');
// }
if ($user->two_factor_secret && $user->two_factor_confirmed_at !== null) {
// Return an Inertia redirect to the two-factor challenge page
return Inertia::location('/auth/two-factor-challenge');
}
if ($rememberMe) {
$encryptedEmail = Crypt::encryptString($request->input('email'));
$encryptedPassword = Crypt::encryptString($request->input('password'));
// Use secure cookies instead of regular cookies
cookie()->queue('email', $encryptedEmail, 60); // 60 minutes
cookie()->queue('password', $encryptedPassword, 60);
}
if ($ip !== null) {
if ($decryptedIP !== $request->ip()) {
$saveIP = Faculty::where('email', $request->email)->update(['client_ip' => Crypt::encryptString($request->ip())]);
}
} else {
$saveIP = Faculty::where('email', $request->email)->update(['client_ip' => Crypt::encryptString($request->ip())]);
}
RateLimiter::clear($rateLimitKey);
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::DASH);
} else {
RateLimiter::hit($rateLimitKey, $lockoutDuration);
// If decrypted IP is not equal to current IP, then log failed attempt with location
if($decryptedIP !== $request->ip()){
$userAgent = $request->header('User-Agent');
// Get Approximate Location
$url = "https://nordvpn.com/wp-admin/admin-ajax.php?action=get_user_info_data&ip={$request->ip()}";
// Fetch the JSON response using file_get_contents
$response = file_get_contents($url);
if ($response === false) {
// Error fetching data
$data = null;
} else {
// Parse JSON response
$locationData = json_decode($response);
$latitude = isset($locationData->coordinates) && is_object($locationData->coordinates) && isset($locationData->coordinates->latitude) ? $locationData->coordinates->latitude : '';
$longitude = isset($locationData->coordinates) && is_object($locationData->coordinates) && isset($locationData->coordinates->longitude) ? $locationData->coordinates->longitude : '';
// Build data array
$data = [
'email_used' => $request->email,
'client_ip' => Crypt::encryptString($request->ip()),
'user_agent' => $userAgent,
'location_information' => $locationData ?
($locationData->city ?? '') . ', ' .
($locationData->region ?? '') . ', ' .
($locationData->area_code ?? '') . ', ' .
($locationData->country ?? '') . ', ' .
($locationData->timezone ?? '') . ', '.
($latitude ?? '') . ', ' .
($longitude ?? '') :
null,
'google_maps_link'=>"https://www.google.com/maps?q=$latitude,$longitude",
'google_earth_link'=>"https://earth.google.com/web/@$latitude,$longitude,1000a,41407.87820565d,1y,0h,0t,0r",
];
}
LoginAttempts::updateOrCreate(['client_ip' => Crypt::encryptString($request->ip())], $data);
}
return redirect()->back()->withErrors(['auth_error' => 'Your login credentials do not match our records. You have ' . $remainingAttempts . ' attempts remaining before your account gets locked out for 10 minutes']);
}
} catch (ValidationException $e) {
return redirect()->back()->withErrors($e->errors())->withInput();
}
}
The logic which checks for whether or not 2fa is enabled and then redirects to the challenge page works. But authenticating with 2fa does not, meaning when I enter the code it does not work.
For reference, I've developed the front end but am hitting these endpoints defined by Fortify:
php artisan route:list | grep two-factor
GET|HEAD auth/two-factor-challenge ..........................................................................
GET|HEAD two-factor-challenge two-factor.login › Laravel\Fortify › TwoFactorAuthenticatedSessionController@c…
POST two-factor-challenge ............... Laravel\Fortify › TwoFactorAuthenticatedSessionController@store
POST user/confirmed-two-factor-authentication two-factor.confirm › Laravel\Fortify › ConfirmedTwoFactorA…
POST user/two-factor-authentication two-factor.enable › Laravel\Fortify › TwoFactorAuthenticationControl…
DELETE user/two-factor-authentication two-factor.disable › Laravel\Fortify › TwoFactorAuthenticationContro…
GET|HEAD user/two-factor-qr-code ...... two-factor.qr-code › Laravel\Fortify › TwoFactorQrCodeController@show
GET|HEAD user/two-factor-recovery-codes two-factor.recovery-codes › Laravel\Fortify › RecoveryCodeController…
POST user/two-factor-recovery-codes ...................... Laravel\Fortify › RecoveryCodeController@store
GET|HEAD user/two-factor-secret-key two-factor.secret-key › Laravel\Fortify › TwoFactorSecretKeyController@s…
Please or to participate in this conversation.