abkrim's avatar
Level 13

Laravel Intercept throttle exceptions

I need to capture the exceptions produced by excessive throttling on a route.

I tried using a catch in the bootstrap/app.php (Laravel 11), but I don't see it reaching there.

Route

Route::middleware('throttle:4,1')->get('/', function () {
    if (auth()->check()) {
        return redirect()->route('dashboard'); // If cookie logged valid
    }

    return view('home'); // If a user is not logged and they need a magic link
})->name('login');

Bootstrap

I think that middleware with trhtolle thriows a ThrottleRequestsException

->withExceptions(function (Exceptions $exceptions) {
        $exceptions->report(function (ThrottleRequestsException $e, $request) {
            ray($e->getMessage(), $request->all()); 
            $msg = 'Excessive throttling attempts';
            Log::channel('login_errors')->error("$request->ip() $msg");
            $this->auditLogin(
                ip: $request->ip(),
                action: AuditAction::getValueByKey('too_many_requests'),
                is_fail: true,
                message: $msg,
            );

            return response()->json([
                'message' => 'Too many login attempts. Banned for 1 hour',
            ], 429);
        });
    })->create();

What is my error ?

0 likes
3 replies
LaryAI's avatar
Level 58

To intercept and handle throttle exceptions in Laravel, you can create a custom middleware to catch the ThrottleRequestsException. The approach you're trying to use in bootstrap/app.php is not the correct place to handle route-specific exceptions. Instead, you should create a middleware that wraps around the throttle middleware to catch and handle the exception.

Here's how you can achieve this:

  1. Create a Custom Middleware:

    First, create a new middleware using the Artisan command:

    php artisan make:middleware HandleThrottleException
    
  2. Implement the Middleware Logic:

    Open the newly created middleware file located at app/Http/Middleware/HandleThrottleException.php and modify it to catch the ThrottleRequestsException:

    namespace App\Http\Middleware;
    
    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Routing\Middleware\ThrottleRequests;
    use Illuminate\Http\Exceptions\ThrottleRequestsException;
    use Illuminate\Support\Facades\Log;
    
    class HandleThrottleException extends ThrottleRequests
    {
        public function handle(Request $request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
        {
            try {
                return parent::handle($request, $next, $maxAttempts, $decayMinutes, $prefix);
            } catch (ThrottleRequestsException $e) {
                // Log the exception or perform any other action
                Log::channel('login_errors')->error("{$request->ip()} Excessive throttling attempts");
    
                // Return a custom response
                return response()->json([
                    'message' => 'Too many login attempts. Banned for 1 hour',
                ], 429);
            }
        }
    }
    
  3. Register the Middleware:

    Register your custom middleware in app/Http/Kernel.php. You can add it to the $routeMiddleware array:

    protected $routeMiddleware = [
        // Other middleware
        'handleThrottle' => \App\Http\Middleware\HandleThrottleException::class,
    ];
    
  4. Apply the Middleware to Your Route:

    Finally, apply your custom middleware to the route:

    Route::middleware('handleThrottle:4,1')->get('/', function () {
        if (auth()->check()) {
            return redirect()->route('dashboard');
        }
    
        return view('home');
    })->name('login');
    

By following these steps, you can effectively intercept and handle throttle exceptions in your Laravel application.

abkrim's avatar
Level 13

@laryai Thanks but i think is a bad response

Request $request Declaration must be compatible with ThrottleRequests->handle(request: \Illuminate\Http\Request, next: \Closure, [maxAttempts: int|string = 60], [decayMinutes: float|int = 1], [prefix: string = ''])

abkrim's avatar
Level 13

No need your logic.

The topic of AI use is getting worse every day. Too much nonsense.

This iis a solution for use teh bootsrap and owned services.

Botstrap

bootstarp/app.php

->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (ThrottleRequestsException $e){
            if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $ipArray = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
                $ip = trim($ipArray[0]);
            } else {
                $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
            }

            $msg = $e->getMessage();
            
            Log::channel('login_errors')->error("$ip $msg");
            $auditService = new App\Services\AuditService();
            $auditService->audit(
                ip: $ip,
                action: 'too_many_requests',
                message: $msg,
                is_fail: true,
            );
        });
    })

Please or to participate in this conversation.