php
57 lines · 9 steps
Rate-limited login in Laravel
A login controller that validates credentials, throttles brute-force attempts, and hardens the session on success.
Explained by
highlit
1<?php
2
3namespace App\Http\Controllers\Auth;
4
5use App\Http\Controllers\Controller;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Auth;
8use Illuminate\Support\Facades\RateLimiter;
9use Illuminate\Support\Str;
10use Illuminate\Validation\ValidationException;
11
12class LoginController extends Controller
13{
14 public function store(Request $request)
15 {
16 $credentials = $request->validate([
17 'email' => ['required', 'email'],
18 'password' => ['required', 'string'],
19 ]);
20
21 $this->ensureIsNotRateLimited($request);
22
23 if (! Auth::attempt($credentials, $request->boolean('remember'))) {
24 RateLimiter::hit($this->throttleKey($request));
25
26 throw ValidationException::withMessages([
27 'email' => __('auth.failed'),
28 ]);
29 }
30
31 RateLimiter::clear($this->throttleKey($request));
32 $request->session()->regenerate();
33
34 return redirect()->intended('/dashboard');
35 }
36
37 protected function ensureIsNotRateLimited(Request $request): void
38 {
39 if (! RateLimiter::tooManyAttempts($this->throttleKey($request), 5)) {
40 return;
41 }
42
43 $seconds = RateLimiter::availableIn($this->throttleKey($request));
44
45 throw ValidationException::withMessages([
46 'email' => __('auth.throttle', [
47 'seconds' => $seconds,
48 'minutes' => ceil($seconds / 60),
49 ]),
50 ]);
51 }
52
53 protected function throttleKey(Request $request): string
54 {
55 return Str::transliterate(Str::lower($request->input('email')).'|'.$request->ip());
56 }
57}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Throttling login by email plus IP blunts brute-force attacks without locking out whole networks.
- 2Regenerating the session on successful login defends against session fixation.
- 3Returning identical validation errors for failed and throttled logins avoids leaking account state.
Related explainers
rust
use axum::{ body::Bytes, extract::State, http::{HeaderMap, StatusCode},
Verifying Stripe webhook signatures in Axum
hmac
webhooks
constant-time-comparison
Intermediate
8 steps
rust
use axum::{ body::Body, extract::Request, http::{header, StatusCode},
How bearer-auth middleware works in Axum
middleware
authentication
request extensions
Intermediate
7 steps
javascript
function validateSignup({ email, password, confirmPassword, username, age }) { const errors = {}; if (!email) {
Building a signup validator in JavaScript
validation
regex
guard-clauses
Beginner
7 steps
go
package auth import ( "net/http"
Building a JWT auth middleware in Gin
middleware
jwt
authentication
Intermediate
7 steps
ruby
class EmailValidator < ActiveModel::EachValidator EMAIL_FORMAT = /\A[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\z/i def validate_each(record, attribute, value)
Writing a custom email validator in Rails
validation
regex
custom validators
Intermediate
7 steps
php
<?php namespace App\Http\Controllers;
Streaming a large CSV export in Laravel
streaming
csv-export
memory-efficiency
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/rate-limited-login-in-laravel-explained-php-b38b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.