diff --git a/resources/views/livewire/auth/forgot-password.blade.php b/resources/views/livewire/auth/forgot-password.blade.php index af75cfed..9bbfab1d 100644 --- a/resources/views/livewire/auth/forgot-password.blade.php +++ b/resources/views/livewire/auth/forgot-password.blade.php @@ -1,6 +1,7 @@ validate([ 'email' => ['required', 'string', 'email'], ]); - + $this->email = Str::lower($this->email); Password::sendResetLink($this->only('email')); session()->flash('status', __('A reset link will be sent if the account exists.')); diff --git a/resources/views/livewire/auth/login.blade.php b/resources/views/livewire/auth/login.blade.php index 3c09468f..a2e21b98 100644 --- a/resources/views/livewire/auth/login.blade.php +++ b/resources/views/livewire/auth/login.blade.php @@ -29,7 +29,7 @@ public function login(): void $this->ensureIsNotRateLimited(); - if (! Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { + if (! Auth::attempt(['email' => Str::lower($this->email), 'password' => $this->password], $this->remember)) { RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ diff --git a/resources/views/livewire/auth/register.blade.php b/resources/views/livewire/auth/register.blade.php index 85ba8be6..71c56917 100644 --- a/resources/views/livewire/auth/register.blade.php +++ b/resources/views/livewire/auth/register.blade.php @@ -4,6 +4,7 @@ use Illuminate\Auth\Events\Registered; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Str; use Illuminate\Validation\Rules; use Livewire\Attributes\Layout; use Livewire\Volt\Component; @@ -21,10 +22,11 @@ public function register(): void { $validated = $this->validate([ 'name' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class], + 'email' => ['required', 'string', 'email', 'max:255', 'unique:' . User::class], 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], ]); + $validated['email'] = Str::lower($validated['email']); $validated['password'] = Hash::make($validated['password']); event(new Registered(($user = User::create($validated)))); diff --git a/resources/views/livewire/auth/reset-password.blade.php b/resources/views/livewire/auth/reset-password.blade.php index f3234db2..8a8f70dd 100644 --- a/resources/views/livewire/auth/reset-password.blade.php +++ b/resources/views/livewire/auth/reset-password.blade.php @@ -24,7 +24,7 @@ public function mount(string $token): void { $this->token = $token; - $this->email = request()->string('email'); + $this->email = Str::lower(request()->string('email')); } /** @@ -37,6 +37,7 @@ public function resetPassword(): void 'email' => ['required', 'string', 'email'], 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], ]); + $this->email = Str::lower($this->email); // 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 diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php index 6a555bcb..abab23fa 100644 --- a/tests/Feature/Auth/AuthenticationTest.php +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -4,6 +4,7 @@ use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Str; use Livewire\Volt\Volt as LivewireVolt; use Tests\TestCase; @@ -48,6 +49,26 @@ public function test_users_can_not_authenticate_with_invalid_password(): void $this->assertGuest(); } + public function test_user_can_authenticate_with_uppercase_email(): void + { + $user = User::factory()->create([ + 'email' => 'user@example.com', + 'password' => bcrypt('password'), + ]); + + $response = LivewireVolt::test('auth.login') + ->set('email', Str::upper($user->email)) // USER@EXAMPLE.COM + ->set('password', 'password') + ->call('login'); + + $response + ->assertHasNoErrors() + ->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticatedAs($user); + } + + public function test_users_can_logout(): void { $user = User::factory()->create(); diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php index e4e0b59a..8c9ef4d9 100644 --- a/tests/Feature/Auth/PasswordResetTest.php +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -6,6 +6,7 @@ use Illuminate\Auth\Notifications\ResetPassword; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Str; use Livewire\Volt\Volt; use Tests\TestCase; @@ -52,6 +53,40 @@ public function test_reset_password_screen_can_be_rendered(): void }); } + public function test_password_can_be_reset_with_uppercase_email(): void + { + Notification::fake(); + + $user = User::factory()->create([ + 'email' => 'testuser@example.com', + ]); + + $uppercaseEmail = Str::upper($user->email); // => TESTUSER@EXAMPLE.COM + + Volt::test('auth.forgot-password') + ->set('email', $uppercaseEmail) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user, $uppercaseEmail) { + $response = Volt::test('auth.reset-password', ['token' => $notification->token]) + ->set('email', $uppercaseEmail) + ->set('password', 'password') + ->set('password_confirmation', 'password') + ->call('resetPassword'); + + $response + ->assertHasNoErrors() + ->assertRedirect(route('login', absolute: false)); + + $this->assertTrue(auth()->attempt([ + 'email' => $user->email, // => testuser@example.com + 'password' => 'password', + ])); + + return true; + }); + } + public function test_password_can_be_reset_with_valid_token(): void { Notification::fake(); diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php index 02f905ba..b4148d48 100644 --- a/tests/Feature/Auth/RegistrationTest.php +++ b/tests/Feature/Auth/RegistrationTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature\Auth; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Str; use Livewire\Volt\Volt; use Tests\TestCase; @@ -32,4 +33,27 @@ public function test_new_users_can_register(): void $this->assertAuthenticated(); } + + public function test_new_user_email_is_stored_as_lowercase_even_if_input_is_uppercase(): void + { + $uppercaseEmail = 'TEST@EXAMPLE.COM'; + + $response = Volt::test('auth.register') + ->set('name', 'Test User') + ->set('email', $uppercaseEmail) + ->set('password', 'password') + ->set('password_confirmation', 'password') + ->call('register'); + + $response + ->assertHasNoErrors() + ->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); + + $this->assertDatabaseHas('users', [ + 'email' => Str::lower($uppercaseEmail), + ]); + } + }