From 5de1c645f92c4dee8436d1abe3457991b8eb8ca8 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Thu, 5 Jun 2025 14:48:32 +0200 Subject: [PATCH] Fix 2fa --- app/Http/Controllers/Auth/LoginController.php | 12 ++ .../PasswordSecurityController.php | 92 +++++++++++++ resources/views/themes/Gentele/2fa_verify.tpl | 126 ++++++++++++++++++ routes/web.php | 7 +- 4 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 resources/views/themes/Gentele/2fa_verify.tpl diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 7f1161e7e..c21e79443 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -83,6 +83,18 @@ class LoginController extends Controller $userIp = config('nntmux:settings.store_user_ip') ? ($request->ip() ?? $request->getClientIp()) : ''; event(new UserLoggedIn($user, $userIp)); + // Check if the user has 2FA enabled + if ($user->passwordSecurity && $user->passwordSecurity->google2fa_enable) { + // Store intended URL for redirecting after 2FA verification + $request->session()->put('url.intended', $this->redirectPath()); + Auth::logout(); + + // Store user ID in the session for 2FA verification + $request->session()->put('2fa:user:id', $user->id); + + return redirect()->route('2fa.verify'); + } + Auth::logoutOtherDevices($request->input('password')); $this->clearLoginAttempts($request); diff --git a/app/Http/Controllers/PasswordSecurityController.php b/app/Http/Controllers/PasswordSecurityController.php index 178046428..f05a06162 100644 --- a/app/Http/Controllers/PasswordSecurityController.php +++ b/app/Http/Controllers/PasswordSecurityController.php @@ -9,6 +9,7 @@ use Illuminate\Contracts\View\View; use Illuminate\Foundation\Application; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; class PasswordSecurityController extends Controller @@ -88,4 +89,95 @@ class PasswordSecurityController extends Controller return redirect()->to('2fa')->with('success', '2FA is now Disabled.'); } + + /** + * Verify the 2FA code provided by the user. + */ + public function verify2fa(Request $request): RedirectResponse + { + $request->validate([ + 'one_time_password' => 'required|numeric', + ]); + + // Get the user ID from session + if (!$request->session()->has('2fa:user:id')) { + return redirect()->route('login') + ->with('message', 'The two-factor authentication session has expired. Please login again.') + ->with('message_type', 'danger'); + } + + $userId = $request->session()->get('2fa:user:id'); + $user = \App\Models\User::find($userId); + + if (!$user || !$user->passwordSecurity) { + $request->session()->forget('2fa:user:id'); + return redirect()->route('login') + ->with('message', 'User not found or 2FA not configured. Please login again.') + ->with('message_type', 'danger'); + } + + // Verify the OTP code + $valid = \Google2FA::verifyKey( + $user->passwordSecurity->google2fa_secret, + $request->input('one_time_password') + ); + + if (!$valid) { + return redirect()->route('2fa.verify') + ->with('message', 'Invalid authentication code. Please try again.') + ->with('message_type', 'danger'); + } + + // Log the user back in + Auth::login($user); + + // Mark the user as having passed 2FA + session([config('google2fa.session_var') => true]); + + // Store the timestamp for determining how long the 2FA session is valid + session([config('google2fa.session_var').'.auth.passed_at' => time()]); + + // Clean up the temporary session variable + $request->session()->forget('2fa:user:id'); + + // Determine where to redirect after successful verification + $redirectUrl = $request->session()->pull('url.intended', '/'); + + return redirect()->to($redirectUrl) + ->with('message', 'Two-factor authentication verified successfully.') + ->with('message_type', 'success'); + } + + /** + * Display the 2FA verification form for a user who has already authenticated with username/password + * but needs to enter their 2FA code. + */ + public function getVerify2fa(Request $request) + { + // Check if user ID is stored in the session + if (!$request->session()->has('2fa:user:id')) { + return redirect()->route('login') + ->withErrors(['msg' => 'The two-factor authentication session has expired. Please login again.']); + } + + // Get the user ID from session + $userId = $request->session()->get('2fa:user:id'); + + // Get the user + $user = \App\Models\User::find($userId); + if (!$user) { + $request->session()->forget('2fa:user:id'); + return redirect()->route('login') + ->withErrors(['msg' => 'User not found. Please login again.']); + } + + $theme = 'Gentele'; + $meta_title = 'Two Factor Authentication'; + $meta_keywords = 'Two Factor Authentication, 2FA'; + $meta_description = 'Two Factor Authentication Verification'; + + app('smarty.view')->assign(compact('meta_title', 'meta_keywords', 'meta_description', 'user')); + + return app('smarty.view')->display($theme.'/2fa_verify.tpl'); + } } diff --git a/resources/views/themes/Gentele/2fa_verify.tpl b/resources/views/themes/Gentele/2fa_verify.tpl new file mode 100644 index 000000000..8e2fb66c2 --- /dev/null +++ b/resources/views/themes/Gentele/2fa_verify.tpl @@ -0,0 +1,126 @@ + + + + + + + {$meta_title}{if $meta_title != "" && $site->metatitle != ""} - {/if}{$site->metatitle} + {{Html::style("{{asset('/assets/css/all-css.css')}}")}} + + + +
+
+
+
+
+

Two-Factor Authentication

+
+ +
+ {if Session::has('message')} + + + + + + {/if} + +
+ +
+ +

{{config('app.name')}}

+
+
+

Please enter your one-time verification code

+
+ + {{Form::open(['url' => route('2faVerify'), 'id' => '2faVerify'])}} +
+
+ + {{Form::text('one_time_password', null, ['placeholder' => 'Authentication Code', 'class' => 'form-control', 'required' => 'required', 'autofocus' => 'autofocus'])}} +
+
+ Enter the code from your authentication app +
+
+ +
+ {{Form::submit('Verify', ['class' => 'btn btn-success'])}} +
+ {{Form::close()}} +
+ + +
+
+
+
+ + + {{Html::script("{{asset('/assets/js/all-js.js')}}")}} + + + + diff --git a/routes/web.php b/routes/web.php index d6c452e5d..e80e8e2b9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -83,6 +83,9 @@ Route::get('login', [LoginController::class, 'showLoginForm'])->name('login'); Route::post('login', [LoginController::class, 'login'])->name('login.post'); Route::match(['GET', 'POST'], 'logout', [LoginController::class, 'logout'])->name('logout'); +Route::get('2fa/verify', [PasswordSecurityController::class, 'getVerify2fa'])->name('2fa.verify'); +Route::post('2faVerify', [PasswordSecurityController::class, 'verify2fa'])->name('2faVerify'); + Route::middleware('isVerified')->group(function () { Route::match(['GET', 'POST'], 'resetpassword', [ResetPasswordController::class, 'reset'])->name('resetpassword'); Route::match(['GET', 'POST'], 'profile', [ProfileController::class, 'show'])->name('profile'); @@ -209,8 +212,4 @@ Route::middleware('role_or_permission:Admin|Moderator|edit release')->prefix('ad Route::match(['GET', 'POST'], 'release-edit', [AdminReleasesController::class, 'edit'])->name('admin.release-edit'); }); -Route::post('2faVerify', function () { - return redirect()->to(URL()->previous()); -})->name('2faVerify')->middleware('2fa'); - Route::post('btcpay/webhook', [BtcPaymentController::class, 'btcPayCallback'])->name('btcpay.webhook');