php 47 lines · 6 steps

Throttling login attempts in Laravel

A service class uses Laravel's RateLimiter to block brute-force logins per email and IP.

Explained by highlit
1<?php
2 
3namespace App\Services;
4 
5use App\Models\User;
6use Illuminate\Support\Facades\Cache;
7use Illuminate\Support\Facades\RateLimiter;
8use Illuminate\Validation\ValidationException;
9 
10class LoginThrottle
11{
12 private const MAX_ATTEMPTS = 5;
13 private const DECAY_SECONDS = 900;
14 
15 public function ensureNotLocked(string $email, string $ip): void
16 {
17 $key = $this->throttleKey($email, $ip);
18 
19 if (! RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) {
20 return;
21 }
22 
23 $seconds = RateLimiter::availableIn($key);
24 
25 throw ValidationException::withMessages([
26 'email' => trans('auth.throttle', [
27 'seconds' => $seconds,
28 'minutes' => ceil($seconds / 60),
29 ]),
30 ])->status(429);
31 }
32 
33 public function recordFailure(string $email, string $ip): void
34 {
35 RateLimiter::hit($this->throttleKey($email, $ip), self::DECAY_SECONDS);
36 }
37 
38 public function clear(string $email, string $ip): void
39 {
40 RateLimiter::clear($this->throttleKey($email, $ip));
41 }
42 
43 private function throttleKey(string $email, string $ip): string
44 {
45 return 'login:' . sha1(mb_strtolower($email) . '|' . $ip);
46 }
47}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Keying a throttle by both email and IP limits attackers without punishing every user sharing an address.
  2. 2Laravel's RateLimiter facade gives you attempt counting, decay, and remaining-time helpers for free.
  3. 3Returning HTTP 429 with a countdown message tells clients precisely when to retry.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Throttling login attempts in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code