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 @@ + + +
+ + + +Please enter your one-time verification code
+