From ee0824302adc1c75e35508b2ca2b494690c4a84b Mon Sep 17 00:00:00 2001 From: boban Date: Sat, 16 May 2026 00:38:32 +0200 Subject: [PATCH] fix(auth): remove dummy login route, validate locale allowlist - Remove no-op POST /login route that caused named-route collision and was testing throttle:5,1 middleware against a null handler. - Rewrite LoginRateLimitTest to drive the real Volt login component via Volt::test(), verifying the RateLimiter blocks the 6th attempt with auth.throttle message; throttle is Laravel core, test confirms wiring. - Add supported_locales allowlist to config/app.php ['de', 'en']. - LocaleFromUser middleware now validates user locale against allowlist and falls back to app.locale for unknown/unsupported values. Co-Authored-By: Claude Sonnet 4.6 --- app/Http/Middleware/LocaleFromUser.php | 6 ++++- config/app.php | 2 ++ routes/auth.php | 2 -- tests/Feature/Auth/LoginRateLimitTest.php | 33 +++++++++++++++++++---- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/Http/Middleware/LocaleFromUser.php b/app/Http/Middleware/LocaleFromUser.php index 2037dd0..66da2d5 100644 --- a/app/Http/Middleware/LocaleFromUser.php +++ b/app/Http/Middleware/LocaleFromUser.php @@ -11,7 +11,11 @@ class LocaleFromUser public function handle(Request $request, Closure $next) { if ($user = $request->user()) { - App::setLocale($user->locale ?? config('app.locale')); + $supported = config('app.supported_locales', [config('app.locale')]); + $locale = in_array($user->locale, $supported, true) + ? $user->locale + : config('app.locale'); + App::setLocale($locale); } return $next($request); } diff --git a/config/app.php b/config/app.php index 423eed5..dfabe8c 100644 --- a/config/app.php +++ b/config/app.php @@ -80,6 +80,8 @@ return [ 'locale' => env('APP_LOCALE', 'en'), + 'supported_locales' => ['de', 'en'], + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), diff --git a/routes/auth.php b/routes/auth.php index 70f2dc6..131252e 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -11,8 +11,6 @@ Route::middleware('guest')->group(function () { Volt::route('login', 'pages.auth.login') ->name('login'); - Route::post('login', fn () => null)->middleware('throttle:5,1')->name('login.post'); - Volt::route('forgot-password', 'pages.auth.forgot-password') ->name('password.request'); diff --git a/tests/Feature/Auth/LoginRateLimitTest.php b/tests/Feature/Auth/LoginRateLimitTest.php index 0e00773..db53367 100644 --- a/tests/Feature/Auth/LoginRateLimitTest.php +++ b/tests/Feature/Auth/LoginRateLimitTest.php @@ -1,11 +1,34 @@ withoutMiddleware(\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class); +use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Str; +use Livewire\Volt\Volt; +it('throttles login after 5 failed attempts', function () { + $email = 'test@test.com'; + + // The throttle key LoginForm uses: Str::transliterate(Str::lower($email) . '|' . request()->ip()) + // In the test environment request()->ip() returns '127.0.0.1' + $throttleKey = Str::transliterate(Str::lower($email) . '|127.0.0.1'); + RateLimiter::clear($throttleKey); + + // Make 5 failed attempts through the real Volt component for ($i = 0; $i < 5; $i++) { - $this->post('/login', ['email' => 'test@test.com', 'password' => 'wrong']); + Volt::test('pages.auth.login') + ->set('form.email', $email) + ->set('form.password', 'wrong' . $i) + ->call('login'); } - $response = $this->post('/login', ['email' => 'test@test.com', 'password' => 'wrong']); - $response->assertStatus(429); + + // 6th attempt must be rate-limited — error on form.email with throttle message + $component = Volt::test('pages.auth.login') + ->set('form.email', $email) + ->set('form.password', 'wrong6') + ->call('login'); + + $component->assertHasErrors(['form.email']); + + $errors = $component->errors(); + expect($errors->has('form.email'))->toBeTrue(); + expect($errors->first('form.email'))->toContain('Too many login attempts'); });