fix(auth): scope registration throttle to its own route (per-IP), not all Fortify endpoints

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
nexxo 2026-07-25 18:41:30 +02:00
parent 35a86c413c
commit 0b2d762b88
3 changed files with 13 additions and 5 deletions

View File

@ -45,6 +45,10 @@ class FortifyServiceProvider extends ServiceProvider
return Limit::perMinute(5)->by($request->session()->get('login.id')); 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) { RateLimiter::for('passkeys', function (Request $request) {
$credentialId = $request->input('credential.id'); $credentialId = $request->input('credential.id');

View File

@ -101,10 +101,7 @@ return [
| |
*/ */
// Throttle all Fortify endpoints (registration has no built-in limiter) to 'middleware' => ['web'],
// blunt signup spam / CPU-exhaustion via repeated password hashing. Login
// keeps its own stricter 'login' limiter on top.
'middleware' => ['web', 'throttle:20,1'],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -170,7 +167,9 @@ return [
// v1 customer portal: login + TOTP two-factor only. Public registration, // v1 customer portal: login + TOTP two-factor only. Public registration,
// password reset, profile/password management and passkeys are out of // password reset, profile/password management and passkeys are out of
// v1 scope (design handoff §2) — enable later as separate screens. // 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::resetPasswords(),
// Features::emailVerification(), // Features::emailVerification(),
// Features::updateProfileInformation(), // Features::updateProfileInformation(),

View File

@ -32,6 +32,11 @@ Route::prefix('legal')->name('legal.')->group(function () {
Route::middleware('guest')->group(function () { Route::middleware('guest')->group(function () {
Route::get('/login', Login::class)->name('login'); Route::get('/login', Login::class)->name('login');
Route::get('/register', \App\Livewire\Auth\Register::class)->name('register'); 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'); Route::get('/two-factor-challenge', TwoFactorChallenge::class)->name('two-factor.login');
}); });