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
ruby
class ApplicationController < ActionController::Base ALLOWED_REDIRECT_HOSTS = [nil, ENV.fetch("APP_HOST", "app.example.com")].freeze def store_return_to(location = request.fullpath)
Safe post-login redirects in Rails
open-redirect
session
authentication
Intermediate
9 steps
php
<?php namespace App\View\Components;
Building a breadcrumb component in Laravel
blade components
url parsing
string manipulation
Intermediate
8 steps
java
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class TenantResolutionFilter extends OncePerRequestFilter {
How a tenant-resolution filter works in Spring
multi-tenancy
servlet-filter
thread-local
Intermediate
8 steps
php
<?php namespace App\Experiments;
Weighted random selection in PHP
weighted-random
cumulative-sum
sampling
Intermediate
8 steps
go
package events import ( "net/http"
Two-pass JSON dispatch in Gin
polymorphic-json
request-binding
validation
Intermediate
8 steps
php
final class FieldEncryptor { private string $key;
Authenticated field encryption with libsodium
encryption
libsodium
authenticated-encryption
Intermediate
7 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.