diff --git a/app/Http/Controllers/PasswordSecurityController.php b/app/Http/Controllers/PasswordSecurityController.php index 9914b8b13..4f196aa4c 100644 --- a/app/Http/Controllers/PasswordSecurityController.php +++ b/app/Http/Controllers/PasswordSecurityController.php @@ -69,16 +69,29 @@ class PasswordSecurityController extends Controller $user->passwordSecurity->google2fa_enable = 1; $user->passwordSecurity->save(); + // Check if we should redirect to profile page + if ($request->has('redirect_to_profile')) { + return redirect()->to('profileedit#security')->with('success_2fa', '2FA is Enabled Successfully.'); + } + return redirect()->to('2fa')->with('success', '2FA is Enabled Successfully.'); } + // Check if we should redirect to profile page on failure as well + if ($request->has('redirect_to_profile')) { + return redirect()->to('profileedit#security')->with('error_2fa', 'Invalid Verification Code, Please try again.'); + } + return redirect()->to('2fa')->with('error', 'Invalid Verification Code, Please try again.'); } public function disable2fa(Disable2faPasswordSecurityRequest $request): \Illuminate\Routing\Redirector|RedirectResponse|\Illuminate\Contracts\Foundation\Application { if (! (Hash::check($request->get('current-password'), $request->user()->password))) { - // The passwords matches + // Password doesn't match + if ($request->has('redirect_to_profile') || $request->has('from_profile')) { + return redirect()->to('profileedit#security')->with('error_2fa', 'Your password does not match with your account password. Please try again.'); + } return redirect()->back()->with('error', 'Your password does not match with your account password. Please try again.'); } @@ -87,6 +100,11 @@ class PasswordSecurityController extends Controller $user->passwordSecurity->google2fa_enable = 0; $user->passwordSecurity->save(); + // Check if this request is from the profile edit page + if ($request->has('redirect_to_profile') || $request->has('from_profile')) { + return redirect()->to('profileedit#security')->with('success_2fa', '2FA is now Disabled.'); + } + return redirect()->to('2fa')->with('success', '2FA is now Disabled.'); } @@ -182,4 +200,60 @@ class PasswordSecurityController extends Controller return app('smarty.view')->display($theme.'/2fa_verify.tpl'); } + + /** + * Handle disabling 2FA directly from profile page to avoid form conflicts. + * This route is specifically for the profile page 2FA section. + */ + public function profileDisable2fa(Request $request): RedirectResponse + { + $request->validate([ + 'current-password' => 'required', + ]); + + if (! (Hash::check($request->get('current-password'), $request->user()->password))) { + return redirect()->to('profileedit#security')->with('error_2fa', 'Your password does not match with your account password. Please try again.'); + } + + $user = $request->user(); + if ($user->passwordSecurity) { + $user->passwordSecurity->google2fa_enable = 0; + $user->passwordSecurity->save(); + } + + return redirect()->to('profileedit#security')->with('success_2fa', '2FA is now Disabled.'); + } + + /** + * Show the 2FA enable form on a dedicated page + */ + public function showEnable2faForm(Request $request): Application|View|Factory|\Illuminate\Contracts\Foundation\Application + { + $user = $request->user(); + $success = $request->session()->get('success'); + $error = $request->session()->get('error'); + + $google2fa_url = ''; + if ($user->passwordSecurity()->exists()) { + $google2fa_url = \Google2FA::getQRCodeInline( + config('app.name'), + $user->email, + $user->passwordSecurity->google2fa_secret + ); + } + + return view('themes.Gentele.2fa_enable', compact('user', 'google2fa_url', 'success', 'error')); + } + + /** + * Show the 2FA disable form on a dedicated page + */ + public function showDisable2faForm(Request $request): Application|View|Factory|\Illuminate\Contracts\Foundation\Application + { + $user = $request->user(); + $success = $request->session()->get('success'); + $error = $request->session()->get('error'); + + return view('themes.Gentele.2fa_disable', compact('user', 'success', 'error')); + } } diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 584a4196f..f1d72da93 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -117,6 +117,18 @@ class ProfileController extends BasePageController } $errorStr = ''; + $success_2fa = $request->session()->get('success'); + $error_2fa = $request->session()->get('error'); + + // Generate 2FA QR code URL if 2FA is set up but not enabled + $google2fa_url = ''; + if ($this->userdata->passwordSecurity()->exists() && !$this->userdata->passwordSecurity->google2fa_enable) { + $google2fa_url = \Google2FA::getQRCodeInline( + config('app.name'), + $this->userdata->email, + $this->userdata->passwordSecurity->google2fa_secret + ); + } switch ($action) { case 'newapikey': @@ -247,6 +259,9 @@ class ProfileController extends BasePageController $this->smarty->assign('error', $errorStr); $this->smarty->assign('user', $this->userdata); $this->smarty->assign('userexccat', User::getCategoryExclusionById($userid)); + $this->smarty->assign('success_2fa', $success_2fa); + $this->smarty->assign('error_2fa', $error_2fa); + $this->smarty->assign('google2fa_url', $google2fa_url); $meta_title = 'Edit User Profile'; $meta_keywords = 'edit,profile,user,details'; diff --git a/app/Http/Controllers/ProfileSecurityController.php b/app/Http/Controllers/ProfileSecurityController.php new file mode 100644 index 000000000..0ea33f6c9 --- /dev/null +++ b/app/Http/Controllers/ProfileSecurityController.php @@ -0,0 +1,75 @@ +validate([ + 'current_password' => 'required', + ]); + + // Check if password is correct + if (!Hash::check($validated['current_password'], Auth::user()->password)) { + if ($request->expectsJson() || $request->ajax()) { + return response()->json([ + 'success' => false, + 'message' => 'Your password does not match. Please try again.' + ]); + } + + return redirect() + ->to('profileedit#security') + ->with('error_2fa', 'Your password does not match. Please try again.'); + } + + // Get the user and disable 2FA + $user = Auth::user(); + if ($user->passwordSecurity) { + $user->passwordSecurity->google2fa_enable = 0; + $user->passwordSecurity->save(); + + if ($request->expectsJson() || $request->ajax()) { + return response()->json([ + 'success' => true, + 'message' => '2FA has been successfully disabled.' + ]); + } + + return redirect() + ->to('profileedit#security') + ->with('success_2fa', '2FA has been successfully disabled.'); + } + + if ($request->expectsJson() || $request->ajax()) { + return response()->json([ + 'success' => false, + 'message' => 'No 2FA configuration found for this user.' + ]); + } + + return redirect() + ->to('profileedit#security') + ->with('error_2fa', 'No 2FA configuration found for this user.'); + } +} diff --git a/resources/views/themes/Gentele/2fa_disable.tpl b/resources/views/themes/Gentele/2fa_disable.tpl new file mode 100644 index 000000000..3113d7506 --- /dev/null +++ b/resources/views/themes/Gentele/2fa_disable.tpl @@ -0,0 +1,120 @@ + + + + + + + {$meta_title}{if $meta_title != "" && $site->metatitle != ""} - {/if}{$site->metatitle} + {{Html::style("{{asset('/assets/css/all-css.css')}}")}} + + + +
+
+
+ + + + +
+
+

Disable Two-Factor Authentication

+
+ +
+ {if isset($error)} + + {/if} + {if isset($success)} + + {/if} + +
+
+
+ +
+
+
Disable Two-Factor Authentication
+

Remove the extra security layer from your account

+
+ + {if $user->passwordSecurity && $user->passwordSecurity->google2fa_enable} +
+ Warning: Disabling 2FA will make your account less secure. Only proceed if absolutely necessary. +
+ +
+
Confirm Password to Disable 2FA
+

Please enter your current password to verify your identity:

+
+ {{csrf_field()}} + +
+
+ + +
+
+
+ + + Cancel and Go Back + +
+
+
+ {else} +
+ Two-factor authentication is not currently enabled on your account. +
+ + {/if} +
+
+
+
+
+ + + + + + diff --git a/resources/views/themes/Gentele/2fa_enable.tpl b/resources/views/themes/Gentele/2fa_enable.tpl new file mode 100644 index 000000000..164085ae5 --- /dev/null +++ b/resources/views/themes/Gentele/2fa_enable.tpl @@ -0,0 +1,152 @@ + + + + + + + {$meta_title}{if $meta_title != "" && $site->metatitle != ""} - {/if}{$site->metatitle} + {{Html::style("{{asset('/assets/css/all-css.css')}}")}} + + + +
+
+
+ + + + +
+
+

Enable Two-Factor Authentication

+
+ +
+ {if isset($error)} + + {/if} + {if isset($success)} + + {/if} + +
+
+
+ +
+
+
Two-Factor Authentication Setup
+

Add an extra layer of security to your account

+
+ +
+ Two factor authentication (2FA) strengthens access security by requiring two methods to verify your identity. It protects against phishing, social engineering, and password brute force attacks. +
+ + {if !isset($user->passwordSecurity)} +
+
+ {{csrf_field()}} + +
+ +
+
+
+ {elseif !$user->passwordSecurity->google2fa_enable} +
+
+
+
+
1. Scan this QR code with your Google Authenticator app:
+
+ 2FA QR Code +
+

If you can't scan the QR code, please set up manually using the code provided.

+
+
+
+
+
+
+
2. Enter the verification code from your app:
+
+ {{csrf_field()}} + +
+
+ + +
+
Enter the 6-digit code from your authenticator app
+
+
+ + + Back to Profile + +
+
+
+
+
+
+
+ Important: Store your backup codes in a secure location. If you lose your device, you will need these codes to regain access to your account. +
+ {elseif $user->passwordSecurity->google2fa_enable} +
+ Two-factor authentication is currently enabled for your account. +
+ + {/if} +
+
+
+
+
+ + + + + + diff --git a/resources/views/themes/Gentele/profileedit.tpl b/resources/views/themes/Gentele/profileedit.tpl index 03cd5fd24..e4a7a0a41 100755 --- a/resources/views/themes/Gentele/profileedit.tpl +++ b/resources/views/themes/Gentele/profileedit.tpl @@ -375,9 +375,19 @@
-
Security Settings
+
Two-Factor Authentication (2FA)
+ {if isset($error_2fa)} +
+ {$error_2fa} +
+ {/if} + {if isset($success_2fa)} +
+ {$success_2fa} +
+ {/if}
@@ -386,12 +396,56 @@
Two-Factor Authentication

Add an extra layer of security to your account

- + +
+
+
+ {if !isset($user->passwordSecurity) || !$user->passwordSecurity->google2fa_enable} +
+ Not Enabled +
+
+
Two-factor authentication is currently disabled
+

Enable 2FA to add an additional layer of security to your account

+
+ + {else} +
+ Enabled +
+
+
Two-factor authentication is active
+

Your account is protected with an additional layer of security

+
+ + {/if} +
+ +
+
What is Two-Factor Authentication?
+

Two-factor authentication adds a second layer of security to your account. In addition to your password, you'll need a code from your authenticator app to sign in. This helps protect your account even if your password is compromised.

+
+ +
+
Security Best Practices:
+
    +
  • Use a strong, unique password for your account
  • +
  • Store your 2FA backup codes in a secure location
  • +
  • Never share your authentication codes with others
  • +
  • Consider using a password manager for all your accounts
  • +
+
diff --git a/routes/web.php b/routes/web.php index e80e8e2b9..3050a6b19 100644 --- a/routes/web.php +++ b/routes/web.php @@ -64,6 +64,7 @@ use App\Http\Controllers\MyShowsController; use App\Http\Controllers\NfoController; use App\Http\Controllers\PasswordSecurityController; use App\Http\Controllers\ProfileController; +use App\Http\Controllers\ProfileSecurityController; use App\Http\Controllers\RssController; use App\Http\Controllers\SearchController; use App\Http\Controllers\SeriesController; @@ -140,9 +141,16 @@ Route::middleware('isVerified')->group(function () { Route::match(['GET', 'POST'], 'series/{id?}', [SeriesController::class, 'index'])->name('series'); Route::match(['GET', 'POST'], 'ajax_profile', [AjaxController::class, 'profile'])->name('ajax_profile'); Route::match(['GET', 'POST'], '2fa', [PasswordSecurityController::class, 'show2faForm'])->name('2fa'); + Route::get('2fa/enable', [PasswordSecurityController::class, 'showEnable2faForm'])->name('2fa.enable'); + Route::get('2fa/disable', [PasswordSecurityController::class, 'showDisable2faForm'])->name('2fa.disable'); Route::post('generate2faSecret', [PasswordSecurityController::class, 'generate2faSecret'])->name('generate2faSecret'); Route::post('2fa', [PasswordSecurityController::class, 'enable2fa'])->name('enable2fa'); Route::post('disable2fa', [PasswordSecurityController::class, 'disable2fa'])->name('disable2fa'); + Route::post('profile-disable2fa', [PasswordSecurityController::class, 'profileDisable2fa'])->name('profile-disable2fa'); + // Custom 2FA routes that redirect to profile page + Route::post('profileedit/enable2fa', [PasswordSecurityController::class, 'enable2fa'])->name('profileedit.enable2fa'); + Route::post('profileedit/disable2fa', [PasswordSecurityController::class, 'disable2fa'])->name('profileedit.disable2fa'); + Route::post('profile-security/disable-2fa', [ProfileSecurityController::class, 'disable2fa'])->name('profile.security.disable2fa'); }); Route::middleware('role:Admin', '2fa')->prefix('admin')->group(function () {