From 0b2d762b88c0d532480f5bd2526f876f28f4c192 Mon Sep 17 00:00:00 2001 From: nexxo Date: Sat, 25 Jul 2026 18:41:30 +0200 Subject: [PATCH] fix(auth): scope registration throttle to its own route (per-IP), not all Fortify endpoints Co-Authored-By: Claude Opus 4.8 --- app/Providers/FortifyServiceProvider.php | 4 ++++ config/fortify.php | 9 ++++----- routes/web.php | 5 +++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index e48ab1a..182bcdf 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -45,6 +45,10 @@ class FortifyServiceProvider extends ServiceProvider return Limit::perMinute(5)->by($request->session()->get('login.id')); }); + // Registration is public (no per-account key), so throttle per IP to blunt + // signup spam / CPU-exhaustion via repeated password hashing. + RateLimiter::for('registration', fn (Request $request) => Limit::perMinute(10)->by($request->ip())); + RateLimiter::for('passkeys', function (Request $request) { $credentialId = $request->input('credential.id'); diff --git a/config/fortify.php b/config/fortify.php index 0681407..b88737f 100644 --- a/config/fortify.php +++ b/config/fortify.php @@ -101,10 +101,7 @@ return [ | */ - // Throttle all Fortify endpoints (registration has no built-in limiter) to - // blunt signup spam / CPU-exhaustion via repeated password hashing. Login - // keeps its own stricter 'login' limiter on top. - 'middleware' => ['web', 'throttle:20,1'], + 'middleware' => ['web'], /* |-------------------------------------------------------------------------- @@ -170,7 +167,9 @@ return [ // v1 customer portal: login + TOTP two-factor only. Public registration, // password reset, profile/password management and passkeys are out of // v1 scope (design handoff §2) — enable later as separate screens. - Features::registration(), + // Registration is exposed via an app route (routes/web.php) with a + // registration-scoped throttle instead of Fortify's unthrottled route. + // Features::registration(), // Features::resetPasswords(), // Features::emailVerification(), // Features::updateProfileInformation(), diff --git a/routes/web.php b/routes/web.php index 90d5d13..dc7e868 100644 --- a/routes/web.php +++ b/routes/web.php @@ -32,6 +32,11 @@ Route::prefix('legal')->name('legal.')->group(function () { Route::middleware('guest')->group(function () { Route::get('/login', Login::class)->name('login'); Route::get('/register', \App\Livewire\Auth\Register::class)->name('register'); + // Registration POST goes through Fortify's controller but with our own + // registration-scoped throttle (Fortify's built-in route has no limiter). + Route::post('/register', [\Laravel\Fortify\Http\Controllers\RegisteredUserController::class, 'store']) + ->middleware('throttle:registration') + ->name('register.store'); Route::get('/two-factor-challenge', TwoFactorChallenge::class)->name('two-factor.login'); });