diff --git a/src/Console/InstallsLivewireStack.php b/src/Console/InstallsLivewireStack.php index 19114c89e..bab1acece 100644 --- a/src/Console/InstallsLivewireStack.php +++ b/src/Console/InstallsLivewireStack.php @@ -65,6 +65,14 @@ protected function installLivewireStack($functional = false) (new Filesystem)->ensureDirectoryExists(app_path('View/Components')); (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/app/View/Components', app_path('View/Components')); + // Actions... + (new Filesystem)->ensureDirectoryExists(app_path('Livewire/Actions')); + (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/livewire-common/app/Livewire/Actions', app_path('Livewire/Actions')); + + // Forms... + (new Filesystem)->ensureDirectoryExists(app_path('Livewire/Forms')); + (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/livewire-common/app/Livewire/Forms', app_path('Livewire/Forms')); + // Dark mode... if (! $this->option('dark')) { $this->removeDarkClasses((new Finder) diff --git a/stubs/livewire-common/app/Livewire/Actions/Logout.php b/stubs/livewire-common/app/Livewire/Actions/Logout.php new file mode 100644 index 000000000..3ef481d1c --- /dev/null +++ b/stubs/livewire-common/app/Livewire/Actions/Logout.php @@ -0,0 +1,20 @@ +logout(); + + Session::invalidate(); + Session::regenerateToken(); + } +} diff --git a/stubs/livewire-common/app/Livewire/Forms/LoginForm.php b/stubs/livewire-common/app/Livewire/Forms/LoginForm.php new file mode 100644 index 000000000..f2811b19d --- /dev/null +++ b/stubs/livewire-common/app/Livewire/Forms/LoginForm.php @@ -0,0 +1,72 @@ +ensureIsNotRateLimited(); + + if (! Auth::attempt($this->only(['email', 'password']), $this->remember)) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + /** + * Ensure the authentication request is not rate limited. + */ + protected function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout(request())); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + /** + * Get the authentication rate limiting throttle key. + */ + protected function throttleKey(): string + { + return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); + } +} diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php index f4f043979..4827635af 100644 --- a/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php +++ b/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php @@ -16,8 +16,8 @@ $user = User::factory()->create(); $component = Volt::test('pages.auth.login') - ->set('email', $user->email) - ->set('password', 'password'); + ->set('form.email', $user->email) + ->set('form.password', 'password'); $component->call('login'); @@ -32,8 +32,8 @@ $user = User::factory()->create(); $component = Volt::test('pages.auth.login') - ->set('email', $user->email) - ->set('password', 'wrong-password'); + ->set('form.email', $user->email) + ->set('form.password', 'wrong-password'); $component->call('login'); diff --git a/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php b/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php index 8fde170f5..8eb8d15fe 100644 --- a/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php +++ b/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php @@ -26,8 +26,8 @@ public function test_users_can_authenticate_using_the_login_screen(): void $user = User::factory()->create(); $component = Volt::test('pages.auth.login') - ->set('email', $user->email) - ->set('password', 'password'); + ->set('form.email', $user->email) + ->set('form.password', 'password'); $component->call('login'); @@ -43,8 +43,8 @@ public function test_users_can_not_authenticate_with_invalid_password(): void $user = User::factory()->create(); $component = Volt::test('pages.auth.login') - ->set('email', $user->email) - ->set('password', 'wrong-password'); + ->set('form.email', $user->email) + ->set('form.password', 'wrong-password'); $component->call('login'); diff --git a/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php b/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php index 747e58285..2f894d2cd 100644 --- a/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php @@ -1,10 +1,9 @@ guard('web')->logout(); +use App\Livewire\Actions\Logout; - session()->invalidate(); - session()->regenerateToken(); +$logout = function (Logout $logout) { + $logout(); $this->redirect('/', navigate: true); }; diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php index 2f4c0015f..60e4a9c5a 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -1,6 +1,7 @@ validate(); - if (! auth()->guard('web')->validate([ - 'email' => auth()->user()->email, + if (! Auth::guard('web')->validate([ + 'email' => Auth::user()->email, 'password' => $this->password, ])) { throw ValidationException::withMessages([ diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php index 9e3822748..a94f43071 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -1,6 +1,7 @@ reset('email'); - session()->flash('status', __($status)); + Session::flash('status', __($status)); }; ?> diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php index 598f24b8d..7f4cc26bf 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php @@ -1,54 +1,22 @@ '', 'password' => '', 'remember' => false]); - -rules([ - 'email' => ['required', 'string', 'email'], - 'password' => ['required', 'string'], - 'remember' => ['boolean'], -]); +form(LoginForm::class); $login = function () { $this->validate(); - $throttleKey = Str::transliterate(Str::lower($this->email).'|'.request()->ip()); - - if (RateLimiter::tooManyAttempts($throttleKey, 5)) { - event(new Lockout(request())); - - $seconds = RateLimiter::availableIn($throttleKey); - - throw ValidationException::withMessages([ - 'email' => trans('auth.throttle', [ - 'seconds' => $seconds, - 'minutes' => ceil($seconds / 60), - ]), - ]); - } - - if (! auth()->attempt($this->only(['email', 'password']), $this->remember)) { - RateLimiter::hit($throttleKey); - - throw ValidationException::withMessages([ - 'email' => trans('auth.failed'), - ]); - } - - RateLimiter::clear($throttleKey); + $this->form->authenticate(); - session()->regenerate(); + Session::regenerate(); $this->redirect( session('url.intended', RouteServiceProvider::HOME), @@ -66,7 +34,7 @@
- +
@@ -74,7 +42,7 @@
- @@ -85,7 +53,7 @@
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php index dff657272..869ec4b9e 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php @@ -3,8 +3,10 @@ use App\Models\User; use App\Providers\RouteServiceProvider; use Illuminate\Auth\Events\Registered; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules; + use function Livewire\Volt\layout; use function Livewire\Volt\rules; use function Livewire\Volt\state; @@ -31,7 +33,7 @@ event(new Registered($user = User::create($validated))); - auth()->login($user); + Auth::login($user); $this->redirect(RouteServiceProvider::HOME, navigate: true); }; diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php index cca45754e..140b33af4 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php @@ -3,6 +3,7 @@ use Illuminate\Auth\Events\PasswordReset; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Password; +use Illuminate\Support\Facades\Session; use Illuminate\Support\Str; use Illuminate\Validation\Rules; @@ -53,7 +54,7 @@ function ($user) { return; } - session()->flash('status', __($status)); + Session::flash('status', __($status)); $this->redirectRoute('login', navigate: true); }; diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php index 376397cbc..a3f870de6 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php @@ -1,13 +1,16 @@ user()->hasVerifiedEmail()) { + if (Auth::user()->hasVerifiedEmail()) { $this->redirect( session('url.intended', RouteServiceProvider::HOME), navigate: true @@ -16,16 +19,13 @@ return; } - auth()->user()->sendEmailVerificationNotification(); + Auth::user()->sendEmailVerificationNotification(); - session()->flash('status', 'verification-link-sent'); + Session::flash('status', 'verification-link-sent'); }; -$logout = function () { - auth()->guard('web')->logout(); - - session()->invalidate(); - session()->regenerateToken(); +$logout = function (Logout $logout) { + $logout(); $this->redirect('/', navigate: true); }; diff --git a/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php index 154a96088..4b3e04d5e 100644 --- a/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php @@ -1,5 +1,8 @@ ['required', 'string', 'current_password']]); -$deleteUser = function () { +$deleteUser = function (Logout $logout) { $this->validate(); - tap(auth()->user(), fn () => auth()->logout())->delete(); - - session()->invalidate(); - session()->regenerateToken(); + tap(Auth::user(), $logout(...))->delete(); $this->redirect('/', navigate: true); }; diff --git a/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php index a36ccd008..408abfbd1 100644 --- a/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php @@ -1,5 +1,6 @@ user()->update([ + Auth::user()->update([ 'password' => Hash::make($validated['password']), ]); diff --git a/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php index a3680208d..cad3a3816 100644 --- a/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php @@ -3,6 +3,8 @@ use App\Models\User; use App\Providers\RouteServiceProvider; use Illuminate\Contracts\Auth\MustVerifyEmail; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Session; use Illuminate\Validation\Rule; use function Livewire\Volt\state; @@ -13,7 +15,7 @@ ]); $updateProfileInformation = function () { - $user = auth()->user(); + $user = Auth::user(); $validated = $this->validate([ 'name' => ['required', 'string', 'max:255'], @@ -32,7 +34,7 @@ }; $sendVerification = function () { - $user = auth()->user(); + $user = Auth::user(); if ($user->hasVerifiedEmail()) { $path = session('url.intended', RouteServiceProvider::HOME); @@ -44,7 +46,7 @@ $user->sendEmailVerificationNotification(); - session()->flash('status', 'verification-link-sent'); + Session::flash('status', 'verification-link-sent'); }; ?> diff --git a/stubs/livewire/resources/views/livewire/layout/navigation.blade.php b/stubs/livewire/resources/views/livewire/layout/navigation.blade.php index 8efb8b04e..8ea1628d5 100644 --- a/stubs/livewire/resources/views/livewire/layout/navigation.blade.php +++ b/stubs/livewire/resources/views/livewire/layout/navigation.blade.php @@ -1,15 +1,17 @@ guard('web')->logout(); - - session()->invalidate(); - session()->regenerateToken(); + $logout(); $this->redirect('/', navigate: true); } diff --git a/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php index 9d9325004..29bda5998 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -1,22 +1,26 @@ validate(); + $this->validate([ + 'password' => ['required', 'string'], + ]); - if (! auth()->guard('web')->validate([ - 'email' => auth()->user()->email, + if (! Auth::guard('web')->validate([ + 'email' => Auth::user()->email, 'password' => $this->password, ])) { throw ValidationException::withMessages([ diff --git a/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php index 168207498..b80401225 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -2,17 +2,20 @@ use Illuminate\Support\Facades\Password; use Livewire\Attributes\Layout; -use Livewire\Attributes\Rule; use Livewire\Volt\Component; new #[Layout('layouts.guest')] class extends Component { - #[Rule(['required', 'string', 'email'])] public string $email = ''; + /** + * Send a password reset link to the provided email address. + */ public function sendPasswordResetLink(): void { - $this->validate(); + $this->validate([ + 'email' => ['required', 'string', 'email'], + ]); // We will send the password reset link to this user. Once we have attempted // to send the link, we will examine the response then see the message we diff --git a/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php index 776c93da5..5885d085c 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php @@ -1,71 +1,31 @@ validate(); - $this->ensureIsNotRateLimited(); - - if (! auth()->attempt($this->only(['email', 'password']), $this->remember)) { - RateLimiter::hit($this->throttleKey()); - - throw ValidationException::withMessages([ - 'email' => trans('auth.failed'), - ]); - } + $this->form->authenticate(); - RateLimiter::clear($this->throttleKey()); - - session()->regenerate(); + Session::regenerate(); $this->redirect( session('url.intended', RouteServiceProvider::HOME), navigate: true ); } - - protected function ensureIsNotRateLimited(): void - { - if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { - return; - } - - event(new Lockout(request())); - - $seconds = RateLimiter::availableIn($this->throttleKey()); - - throw ValidationException::withMessages([ - 'email' => trans('auth.throttle', [ - 'seconds' => $seconds, - 'minutes' => ceil($seconds / 60), - ]), - ]); - } - - protected function throttleKey(): string - { - return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); - } }; ?>
@@ -76,7 +36,7 @@ protected function throttleKey(): string
- +
@@ -84,7 +44,7 @@ protected function throttleKey(): string
- @@ -95,7 +55,7 @@ protected function throttleKey(): string
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php index 1464c3015..179b644c0 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php @@ -3,6 +3,7 @@ use App\Models\User; use App\Providers\RouteServiceProvider; use Illuminate\Auth\Events\Registered; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules; use Livewire\Attributes\Layout; @@ -11,13 +12,13 @@ new #[Layout('layouts.guest')] class extends Component { public string $name = ''; - public string $email = ''; - public string $password = ''; - public string $password_confirmation = ''; + /** + * Handle an incoming registration request. + */ public function register(): void { $validated = $this->validate([ @@ -30,7 +31,7 @@ public function register(): void event(new Registered($user = User::create($validated))); - auth()->login($user); + Auth::login($user); $this->redirect(RouteServiceProvider::HOME, navigate: true); } diff --git a/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php index e258c4c85..310a1966c 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php @@ -3,6 +3,7 @@ use Illuminate\Auth\Events\PasswordReset; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Password; +use Illuminate\Support\Facades\Session; use Illuminate\Support\Str; use Illuminate\Validation\Rules; use Livewire\Attributes\Layout; @@ -13,13 +14,13 @@ { #[Locked] public string $token = ''; - public string $email = ''; - public string $password = ''; - public string $password_confirmation = ''; + /** + * Mount the component. + */ public function mount(string $token): void { $this->token = $token; @@ -27,6 +28,9 @@ public function mount(string $token): void $this->email = request()->string('email'); } + /** + * Reset the password for the given user. + */ public function resetPassword(): void { $this->validate([ @@ -59,7 +63,7 @@ function ($user) { return; } - session()->flash('status', __($status)); + Session::flash('status', __($status)); $this->redirectRoute('login', navigate: true); } diff --git a/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php index a70ce13b4..5935a10f9 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php @@ -1,14 +1,20 @@ user()->hasVerifiedEmail()) { + if (Auth::user()->hasVerifiedEmail()) { $this->redirect( session('url.intended', RouteServiceProvider::HOME), navigate: true @@ -17,17 +23,17 @@ public function sendVerification(): void return; } - auth()->user()->sendEmailVerificationNotification(); + Auth::user()->sendEmailVerificationNotification(); - session()->flash('status', 'verification-link-sent'); + Session::flash('status', 'verification-link-sent'); } - public function logout(): void + /** + * Log the current user out of the application. + */ + public function logout(Logout $logout): void { - auth()->guard('web')->logout(); - - session()->invalidate(); - session()->regenerateToken(); + $logout(); $this->redirect('/', navigate: true); } diff --git a/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php b/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php index 6f0f5f23d..1d74a63dc 100644 --- a/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php +++ b/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php @@ -1,21 +1,23 @@ validate(); + $this->validate([ + 'password' => ['required', 'string', 'current_password'], + ]); - tap(auth()->user(), fn () => auth()->logout())->delete(); - - session()->invalidate(); - session()->regenerateToken(); + tap(Auth::user(), $logout(...))->delete(); $this->redirect('/', navigate: true); } diff --git a/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php b/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php index 2f07531ac..73846cc4a 100644 --- a/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php +++ b/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php @@ -1,5 +1,6 @@ user()->update([ + Auth::user()->update([ 'password' => Hash::make($validated['password']), ]); diff --git a/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php b/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php index e00961b82..a5c45eca8 100644 --- a/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php +++ b/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php @@ -2,24 +2,31 @@ use App\Models\User; use App\Providers\RouteServiceProvider; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Session; use Illuminate\Validation\Rule; use Livewire\Volt\Component; new class extends Component { public string $name = ''; - public string $email = ''; + /** + * Mount the component. + */ public function mount(): void { - $this->name = auth()->user()->name; - $this->email = auth()->user()->email; + $this->name = Auth::user()->name; + $this->email = Auth::user()->email; } + /** + * Update the profile information for the currently authenticated user. + */ public function updateProfileInformation(): void { - $user = auth()->user(); + $user = Auth::user(); $validated = $this->validate([ 'name' => ['required', 'string', 'max:255'], @@ -37,9 +44,12 @@ public function updateProfileInformation(): void $this->dispatch('profile-updated', name: $user->name); } + /** + * Send an email verification notification to the current user. + */ public function sendVerification(): void { - $user = auth()->user(); + $user = Auth::user(); if ($user->hasVerifiedEmail()) { $path = session('url.intended', RouteServiceProvider::HOME); @@ -51,7 +61,7 @@ public function sendVerification(): void $user->sendEmailVerificationNotification(); - session()->flash('status', 'verification-link-sent'); + Session::flash('status', 'verification-link-sent'); } }; ?>