From 3acdc7a4a36fb54d42fe9245d05b9c67eb5d2e7b Mon Sep 17 00:00:00 2001
From: Pushpak Chhajed
Date: Mon, 13 Oct 2025 16:57:51 +0530
Subject: [PATCH 1/4] Replace reset password with fortify
---
.../Fortify/PasswordValidationRules.php | 18 +++++
app/Actions/Fortify/ResetUserPassword.php | 28 ++++++++
.../Auth/NewPasswordController.php | 70 -------------------
.../Auth/PasswordResetLinkController.php | 41 -----------
app/Providers/FortifyServiceProvider.php | 19 +++++
config/fortify.php | 4 +-
resources/js/layouts/settings/layout.tsx | 2 +-
resources/js/pages/auth/forgot-password.tsx | 4 +-
resources/js/pages/auth/reset-password.tsx | 4 +-
resources/js/pages/settings/password.tsx | 2 +-
routes/auth.php | 14 ----
routes/settings.php | 4 +-
tests/Feature/Auth/PasswordResetTest.php | 4 +-
tests/Feature/Settings/PasswordUpdateTest.php | 14 ++--
14 files changed, 83 insertions(+), 145 deletions(-)
create mode 100644 app/Actions/Fortify/PasswordValidationRules.php
create mode 100644 app/Actions/Fortify/ResetUserPassword.php
delete mode 100644 app/Http/Controllers/Auth/NewPasswordController.php
delete mode 100644 app/Http/Controllers/Auth/PasswordResetLinkController.php
diff --git a/app/Actions/Fortify/PasswordValidationRules.php b/app/Actions/Fortify/PasswordValidationRules.php
new file mode 100644
index 000000000..76b19d330
--- /dev/null
+++ b/app/Actions/Fortify/PasswordValidationRules.php
@@ -0,0 +1,18 @@
+|string>
+ */
+ protected function passwordRules(): array
+ {
+ return ['required', 'string', Password::default(), 'confirmed'];
+ }
+}
diff --git a/app/Actions/Fortify/ResetUserPassword.php b/app/Actions/Fortify/ResetUserPassword.php
new file mode 100644
index 000000000..688d62f3b
--- /dev/null
+++ b/app/Actions/Fortify/ResetUserPassword.php
@@ -0,0 +1,28 @@
+ $input
+ */
+ public function reset(User $user, array $input): void
+ {
+ Validator::make($input, [
+ 'password' => $this->passwordRules(),
+ ])->validate();
+
+ $user->forceFill([
+ 'password' => $input['password'],
+ ])->save();
+ }
+}
diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php
deleted file mode 100644
index 3496b1d3e..000000000
--- a/app/Http/Controllers/Auth/NewPasswordController.php
+++ /dev/null
@@ -1,70 +0,0 @@
- $request->email,
- 'token' => $request->route('token'),
- ]);
- }
-
- /**
- * Handle an incoming new password request.
- *
- * @throws \Illuminate\Validation\ValidationException
- */
- public function store(Request $request): RedirectResponse
- {
- $request->validate([
- 'token' => 'required',
- 'email' => 'required|email',
- 'password' => ['required', 'confirmed', Rules\Password::defaults()],
- ]);
-
- // Here we will attempt to reset the user's password. If it is successful we
- // will update the password on an actual user model and persist it to the
- // database. Otherwise we will parse the error and return the response.
- $status = Password::reset(
- $request->only('email', 'password', 'password_confirmation', 'token'),
- function (User $user) use ($request) {
- $user->forceFill([
- 'password' => Hash::make($request->password),
- 'remember_token' => Str::random(60),
- ])->save();
-
- event(new PasswordReset($user));
- }
- );
-
- // If the password was successfully reset, we will redirect the user back to
- // the application's home authenticated view. If there is an error we can
- // redirect them back to where they came from with their error message.
- if ($status == Password::PasswordReset) {
- return to_route('login')->with('status', __($status));
- }
-
- throw ValidationException::withMessages([
- 'email' => [__($status)],
- ]);
- }
-}
diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php
deleted file mode 100644
index 9fcfe49d0..000000000
--- a/app/Http/Controllers/Auth/PasswordResetLinkController.php
+++ /dev/null
@@ -1,41 +0,0 @@
- $request->session()->get('status'),
- ]);
- }
-
- /**
- * Handle an incoming password reset link request.
- *
- * @throws \Illuminate\Validation\ValidationException
- */
- public function store(Request $request): RedirectResponse
- {
- $request->validate([
- 'email' => 'required|email',
- ]);
-
- Password::sendResetLink(
- $request->only('email')
- );
-
- return back()->with('status', __('A reset link will be sent if the account exists.'));
- }
-}
diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php
index e8fc91508..961914ce2 100644
--- a/app/Providers/FortifyServiceProvider.php
+++ b/app/Providers/FortifyServiceProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use App\Actions\Fortify\ResetUserPassword;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
@@ -26,10 +27,19 @@ public function register(): void
*/
public function boot(): void
{
+ $this->configureActions();
$this->configureViews();
$this->configureRateLimiting();
}
+ /**
+ * Configure Fortify actions.
+ */
+ private function configureActions(): void
+ {
+ Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
+ }
+
/**
* Configure Fortify views.
*/
@@ -44,6 +54,15 @@ private function configureViews(): void
'status' => $request->session()->get('status'),
]));
+ Fortify::requestPasswordResetLinkView(fn (Request $request) => Inertia::render('auth/forgot-password', [
+ 'status' => $request->session()->get('status'),
+ ]));
+
+ Fortify::resetPasswordView(fn (Request $request) => Inertia::render('auth/reset-password', [
+ 'email' => $request->email,
+ 'token' => $request->route('token'),
+ ]));
+
Fortify::twoFactorChallengeView(fn () => Inertia::render('auth/two-factor-challenge'));
Fortify::confirmPasswordView(fn () => Inertia::render('auth/confirm-password'));
diff --git a/config/fortify.php b/config/fortify.php
index 0846d4498..9dd5999b2 100644
--- a/config/fortify.php
+++ b/config/fortify.php
@@ -145,10 +145,8 @@
'features' => [
// Features::registration(),
- // Features::resetPasswords(),
+ Features::resetPasswords(),
Features::emailVerification(),
- // Features::updateProfileInformation(),
- // Features::updatePasswords(),
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
diff --git a/resources/js/layouts/settings/layout.tsx b/resources/js/layouts/settings/layout.tsx
index c128d82c5..1939af84e 100644
--- a/resources/js/layouts/settings/layout.tsx
+++ b/resources/js/layouts/settings/layout.tsx
@@ -3,9 +3,9 @@ import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { cn } from '@/lib/utils';
import { edit as editAppearance } from '@/routes/appearance';
-import { edit as editPassword } from '@/routes/password';
import { edit } from '@/routes/profile';
import { show } from '@/routes/two-factor';
+import { edit as editPassword } from '@/routes/user-password';
import { type NavItem } from '@/types';
import { Link } from '@inertiajs/react';
import { type PropsWithChildren } from 'react';
diff --git a/resources/js/pages/auth/forgot-password.tsx b/resources/js/pages/auth/forgot-password.tsx
index 0e8803e28..3d32f16e9 100644
--- a/resources/js/pages/auth/forgot-password.tsx
+++ b/resources/js/pages/auth/forgot-password.tsx
@@ -1,6 +1,6 @@
// Components
-import PasswordResetLinkController from '@/actions/App/Http/Controllers/Auth/PasswordResetLinkController';
import { login } from '@/routes';
+import { email } from '@/routes/password';
import { Form, Head } from '@inertiajs/react';
import { LoaderCircle } from 'lucide-react';
@@ -26,7 +26,7 @@ export default function ForgotPassword({ status }: { status?: string }) {
)}
-
- Don't have an account?{' '}
-
- Sign up
-
-
+ {canRegister && (
+
+ Don't have an account?{' '}
+
+ Sign up
+
+
+ )}
>
)}
diff --git a/resources/js/pages/auth/register.tsx b/resources/js/pages/auth/register.tsx
index 4015acae3..96a44ef9e 100644
--- a/resources/js/pages/auth/register.tsx
+++ b/resources/js/pages/auth/register.tsx
@@ -1,5 +1,5 @@
-import RegisteredUserController from '@/actions/App/Http/Controllers/Auth/RegisteredUserController';
import { login } from '@/routes';
+import { store } from '@/routes/register';
import { Form, Head } from '@inertiajs/react';
import { LoaderCircle } from 'lucide-react';
@@ -18,7 +18,7 @@ export default function Register() {
>