maba/gentle-force-bundle
maba/gentle-force-bundle adds brute force protection to your login forms by tracking failed authentication attempts and throttling repeated failures, helping block attackers while keeping legitimate users signed in smoothly.
Reality check first: maba/gentle-force-bundle is a Symfony bundle, not a Laravel package. It requires symfony/framework-bundle, relies on Symfony's DI container and CachePoolPass compiler passes, and auto-registers via config/bundles.php. If you run composer require maba/gentle-force-bundle in a Laravel app today, nothing will happen unless you also bring in a Symfony kernel — so treat this as a reference implementation / pattern source, not a drop-in dependency. The valuable part for a Laravel team is the core idea and the three protections it implements: login brute-force, error rate, and request rate limiting.
If you are in a Symfony app (or a mixed project), the minimum setup is:
composer require maba/gentle-force-bundle
// config/bundles.php
return [
// ...
Maba\Bundle\GentleForceBundle\MabaGentleForceBundle::class => ['all' => true],
];
Then point it at a cache pool (Redis recommended for multi-node) and enable the guards you want under maba_gentle_force: in config/packages/. Look first at the bundle's DependencyInjection/Configuration.php and the Annotation/ directory — that's where the actual knobs and entry points live, and it's a short read.
First use case to internalize: limiting failed login attempts by (username, IP) with a decay window. That is the textbook scenario the bundle was built around, and it's the one you'll reproduce in Laravel with RateLimiter.
The bundle models throttling as three orthogonal guards. Translate each into a Laravel pattern:
1. Login brute-force. Symfony: listen to authentication failures, increment a counter keyed by identifier + IP, lock out after N attempts, decay after the period, clear on success. Laravel equivalent — put it in your auth pipeline:
RateLimiter::for('login', fn (Request $r) => [
Limit::perMinute(5)->by($r->input('email').'|'.$r->ip()),
]);
// routes/web.php
Route::post('/login', [LoginController::class, 'store'])->middleware('throttle:login');
If you use Fortify or Breeze, this is already wired via LoginRateLimiter. Don't duplicate it.
2. Request rate limiting. Symfony: annotate controller actions or apply a listener with per-IP / per-user keys and configurable period. Laravel: throttle:60,1 on route groups, or named limiters bound to by() keys for tenant/user/API-token scoping.
3. Error rate limiting. The distinctive one — count 4xx/5xx responses and throttle clients that keep hammering bad endpoints. Laravel's throttle middleware only counts requests, not outcomes, so this is the guard worth copying. Pattern:
// middleware: after response, if status >= 400, hit a RateLimiter
$key = 'errors:'.$request->ip().':'.$response->status();
if (RateLimiter::tooManyAttempts($key, 20)) {
return response()->noContent(429);
}
if ($response->isClientError() || $response->isServerError()) {
RateLimiter::hit($key, 300);
}
Storage & integration tips. Symfony-side, swap the default cache pool for Redis so counters survive across containers. Laravel-side, RateLimiter already uses your cache store; if you're running multiple app nodes, back it with Redis so limits are consistent. Always namespace keys (login:, errors:) — the bundle does this with a prefix, and you should too.
RateLimiter already does 80% of what this bundle does.@RateLimit), not PHP 8 attributes; if you fork it, you'll likely need to add attribute support yourself. For any code sitting on the authentication path, read the source before trusting it — 50 stars and a stale release cadence is not a strong security posture.cache.app (or whatever you assign). If you rename the pool, it silently no-ops until you hit the limit — check the container compile output, not just runtime behavior.mariusbalcytis/gentle-force) exposes a strategy/storage interface — a clean pattern for plugging in custom key resolvers (e.g., per-tenant instead of per-IP) or alternative storages. Mirror that when you write your own middleware: interface for the key resolver, cache-backed implementation, tests with a fake clock.RateLimiter::attempts($key) and Cache::get('limiter:'.$key) during tests are your fastest path to the bug.RateLimiter, throttle, and Fortify's login throttling as first-class citizens. What you gain by reading this bundle: a proven mental model for separating request-rate, error-rate, and brute-force guards — and a reminder that error-rate limiting is a gap in Laravel's defaults worth filling with a small custom middleware.How can I help you explore Laravel packages today?