Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Gentle Force Laravel Package

maba/gentle-force

maba/gentle-force is a Laravel package for defending authentication against brute-force attacks. It gradually throttles repeated failed login attempts with configurable delays, helping slow attackers while keeping legitimate users unaffected. Simple middleware integration and cache-backed attempt tr...

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and start with the README’s usage section, then inspect src/ for the rate-limit manager and storage adapters. The package is small and framework-agnostic, so in Laravel you will usually wire it manually rather than rely on package-provided HTTP middleware.

composer require maba/gentle-force

First practical use case: failed-login throttling. Use Redis-backed storage in production, not file or in-memory storage, so limits are shared across PHP workers and app servers. Register the manager as a singleton in a service provider, then use a small application service to keep key naming and counting rules consistent.

// AppServiceProvider::register()
$this->app->singleton(\Maba\GentleForce\RateLimitManager::class, function ($app) {
    return new \Maba\GentleForce\RateLimitManager(
        new \Maba\GentleForce\Storage\RedisStorage(
            $app['redis']->connection()->client()
        )
    );
});

In a login controller or form request, check the bucket before authenticating, increment only on failure, and reset/clear on success. The exact method names come from the package README; the important pattern is check → attempt → increment failure / reset success.

$key = 'login:'.strtolower($request->input('email')).':'.$request->ip();

$limit = app(\Maba\GentleForce\RateLimitManager::class)
    ->getRateLimit($key, 5, 300); // 5 attempts / 5 minutes, illustrative API

if ($limit->isLimited()) {
    return back()->withErrors([
        'email' => 'Too many login attempts. Please try again later.',
    ]);
}

if (! Auth::attempt($request->only('email', 'password'))) {
    $limit->increase(); // or hit()/consume(), depending on package API

    throw ValidationException::withMessages([
        'email' => 'These credentials do not match our records.',
    ]);
}

$limit->reset(); // clear failed attempts after successful login

Implementation Patterns

Use separate buckets for separate attack surfaces. For login, keep a per-account bucket and a per-IP bucket. The account bucket stops password guessing against one user; the IP bucket stops credential stuffing across many users. Reset the account bucket on successful login, but generally do not reset the IP bucket on success.

Count failures, not every login request. Brute-force protection should increment on invalid credentials, expired 2FA codes, or failed password resets. Counting every request will lock out normal users who mistype once and then retry correctly. For request-rate limiting, the opposite is true: count every request.

Wrap request limiting in Laravel middleware. Apply it to route groups rather than individual controllers. Key by authenticated user ID when available, otherwise by IP plus route name. Return 429 with Retry-After and useful X-RateLimit-* headers if the package exposes remaining/retry data.

public function handle($request, Closure $next)
{
    $key = 'api:'.($request->user()?->id ?? $request->ip()).':'.$request->path();
    $limit = $this->limiter->getRateLimit($key, 60, 60); // illustrative

    if ($limit->isLimited()) {
        return response()->json(
            ['message' => 'Too many requests.'],
            429
        )->header('Retry-After', $limit->getRetryAfter());
    }

    $limit->increase();

    return $next($request);
}

Layer short and long windows. A common pattern is 10 requests / minute for burst control plus 300 requests / hour for sustained abuse. Keep the buckets independent and reject if either is exceeded. Do not try to express both rules as one counter.

Use it for error-rate limiting around unstable integrations. For example, protect a webhook sender or third-party API client: count exceptions per tenant/endpoint, short-circuit after a threshold, and reset on a successful call. This is useful for circuit-breaker-style behavior even if you already use Laravel’s HTTP client retries.

Centralize key construction. Create a small service such as LoginThrottle, ApiRateLimit, or IntegrationErrorLimit that owns key prefixes, normalization, limits, and reset rules. Controllers and middleware should not assemble raw Redis keys inline. This also makes it easy to switch to Laravel’s built-in RateLimiter later if the package no longer fits.

Bind the manager once. Create the manager as a singleton and inject it. Do not construct a new manager or Redis client per request. Reuse Laravel’s existing Redis connection so connection pooling, prefixes, and TLS configuration stay consistent.

Add observability around lockouts. Log or emit events when a limit is hit: key type, route, user ID/IP hash, remaining attempts, and retry-after. Avoid logging passwords, tokens, or raw PII. A spike in login-failure buckets is useful security telemetry.

Test with deterministic storage. Use the package’s in-memory or test storage adapter, or bind a fake manager in Laravel’s container. Assert that failed attempts increment, successful login resets, the window expires, and 429 responses include the correct retry metadata.

Gotchas and Tips

Check maintenance status before depending on it heavily. The last release was in 2021 and the package has few dependents. Verify PHP/Laravel version compatibility and wrap it behind your own interface so you can swap implementations without touching controllers.

Check-then-increment is not always atomic. For login failures this is usually acceptable because you only increment after a failed attempt. For request limiting, parallel requests can slip through if check and increment are separate operations. Prefer an atomic “consume”/increment-and-check operation if the storage adapter provides one.

Key normalization matters. Lowercase emails, trim whitespace, include tenant/workspace IDs where relevant, and include route or method names in request keys. Otherwise Login, login, and login become separate buckets, or unrelated endpoints share the same counter.

Avoid account enumeration and accidental global lockouts. Do not reveal whether an email exists through different rate-limit messages. Do not key only by email if attackers can lock out arbitrary users; combine account and IP buckets and consider progressive delays.

Choose fail-open vs fail-closed deliberately. If Redis is unavailable, login throttling may fail open with an alert, while payment or admin endpoints may fail closed. Document this per endpoint; do not let a storage outage silently disable all protection.

Redis TTLs and fixed windows are easy to misread. Confirm whether the package uses fixed windows, sliding windows, or leaking buckets. Test expiration from the storage side, not just application time. Ensure keys have a TTL so abandoned buckets do not accumulate.

Middleware order changes behavior. User-based limits must run after authentication. Guest login throttling should run before Auth::attempt but after basic request validation. Public API limits should generally run before expensive controller work.

Do not use file or in-memory storage in multi-server production. Limits will be inconsistent across PHP-FPM workers and app servers. Use Redis or another shared store. If using Redis Cluster, check how the package hashes keys.

Debug with the storage, not only the app. Inspect Redis keys with your configured prefix, check TTLs and counter values, and reproduce with a fixed test key. Log the calculated key and limit values when a bucket behaves unexpectedly.

Extension points are your escape hatch. If the package’s middleware or storage does not fit, implement a custom storage adapter or a thin application service around the manager. Keep the package’s RateLimit objects out of your public APIs so future replacement with Laravel’s RateLimiter or a Redis Lua script remains straightforward.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin