harden(auth): capture login IP once, assert 2fa-failed audit

feat/v1-foundation
boban 2026-06-20 18:06:11 +02:00
parent 968d8c48c9
commit 01867dba09
2 changed files with 4 additions and 2 deletions

View File

@ -66,13 +66,14 @@ class Login extends Component
RateLimiter::hit($pairKey, 60); // 5 / minute RateLimiter::hit($pairKey, 60); // 5 / minute
RateLimiter::hit($ipKey, 60); // 20 / minute RateLimiter::hit($ipKey, 60); // 20 / minute
RateLimiter::hit($acctKey, 900); // 30 / 15 minutes RateLimiter::hit($acctKey, 900); // 30 / 15 minutes
app(BruteforceGuard::class)->record(request()->ip(), 'login'); $ip = request()->ip();
app(BruteforceGuard::class)->record($ip, 'login');
AuditEvent::create([ AuditEvent::create([
'user_id' => null, 'user_id' => null,
'actor' => 'system', 'actor' => 'system',
'action' => 'auth.login_failed', 'action' => 'auth.login_failed',
'target' => $this->email, 'target' => $this->email,
'ip' => request()->ip(), 'ip' => $ip,
]); ]);
throw ValidationException::withMessages(['email' => __('auth.invalid_credentials')]); throw ValidationException::withMessages(['email' => __('auth.invalid_credentials')]);
} }

View File

@ -81,5 +81,6 @@ class BruteforceHooksTest extends TestCase
} }
$this->assertDatabaseHas('banned_ips', ['ip' => '203.0.113.6', 'reason' => '2fa']); $this->assertDatabaseHas('banned_ips', ['ip' => '203.0.113.6', 'reason' => '2fa']);
$this->assertDatabaseHas('audit_events', ['action' => 'auth.2fa_failed']);
} }
} }