nowo-tech/login-throttle-bundle
Symfony bundle that throttles login attempts to defend against brute-force attacks. Configure failed-attempt limits, lockout windows, and custom responses, with seamless integration into Symfony Security for safer authentication.
Scope correction first: the intake record labels this a "Laravel/PHP package," but the artifact is a Symfony bundle (nowo-tech/login-throttle-bundle) and will not install into a Laravel application. Laravel's equivalent control is RateLimiter + ThrottleRequests/Fortify lockout. Treat the mislabel as a data-quality defect in the intake pipeline; the assessment below assumes a Symfony fleet.
Architecture fit. This is a configuration-distribution artifact, not a platform component. It does not implement throttling; it exposes Symfony's native security.firewalls.<name>.login_throttling (max attempts, interval, lock interval, cache pool) with pre-set defaults, and exists primarily as a migration shim off the deprecated anyx/login-gate-bundle. That is a favourable architecture trade: the security logic stays in the framework, so the bundle cannot introduce a novel auth bypass, and its blast radius is limited to YAML shape and defaults. The corollary is low defensibility — the entire value is roughly 10–20 lines of firewall config that any team can inline into an application skeleton or recipe.
Integration feasibility. Mechanically easy: composer require, register the bundle, add login_throttling to the firewall (or accept bundle defaults via the DI extension's prepend(), depending on implementation). The real work is not installation but policy: choosing max_attempts, interval, and lock_interval per app, and confirming which cache pool backs the limiter. Symfony's throttling is only as strong as that pool — an array/per-request cache or an aggressively evicting Redis silently degrades the control to a no-op.
Technical risk.
login_throttling (it has moved before), upstream may not follow.login_throttling keys on username+IP for form_login/json_login. It does not stop distributed credential stuffing across many usernames, and per-username keying creates a targeted lockout (denial-of-service) vector.Key questions for the maintainer. What exactly does the extension configure, and is the target firewall name configurable or assumed? Does it prepend() security config, and how does that interact with apps that already define login_throttling? Which cache pool does it wire by default, and what happens if none supports locking? Is max_attempts/lock_interval mapping documented against anyx's max_attempts/delay? What is the support policy for Symfony 8 and PHP 8.4? Does it fail loudly when login_throttling is unavailable, or no-op silently? What is the test coverage and PHPStan level?
Stack fit. Appropriate for Symfony 6.4/7.x monoliths and API apps using form_login or json_login; not applicable to Laravel, and not applicable to apps with custom authenticators that bypass the firewall's login listener (those need a hand-rolled RateLimiterFactory).
Migration path. Sequence: (1) inventory every app still on anyx/login-gate-bundle and record current attempt/delay/storage semantics; (2) for one non-critical app, remove anyx, install this bundle, and re-express the same policy via login_throttling; (3) verify the cache-pool choice survives a deploy (cache:clear) without wiping lockout state — if it does not, decide whether that is acceptable or whether a dedicated persistent pool is required; (4) diff observed behaviour (attempt counting, lock duration, response code) against the old bundle before declaring parity.
Compatibility. Confirm PHP >=8.1 and Symfony 6/7 against each app's composer why-not. Expect collisions in older apps pinned below Symfony 5.2-style security config.
Sequencing. Do not bundle this migration with a Symfony major upgrade. Land it as an isolated change behind a feature flag, with the golden-path YAML committed to the internal app skeleton so new services get throttling without the dependency. Keep the bundle optional per app; standardise the configuration, not necessarily the package.
Build-vs-adopt position. Because the deliverable is config, the strongest option is to adopt the bundle only where the anyx shim saves real effort, and otherwise copy the equivalent YAML into your own skeleton. That removes the third-party risk entirely while preserving the security outcome.
Maintenance. Near-zero code maintenance, non-trivial policy maintenance: defaults drift from your risk appetite, and nobody owns them unless assigned. Assign an owner in the security/platform team; treat the bundle as a pinned, renovate-tracked dependency with a documented fork-and-inline fallback if it goes stale.
Support. Zero community means zero external support. Your on-call inherits the failure modes. Budget for reading Symfony's rate-limiter internals rather than the bundle's README.
Scaling. Login throttling puts the cache on the hot path for every authenticated attempt. In multi-node deployments this must be a shared Redis/Memcached pool; watch for eviction under memory pressure silently resetting counters, and for per-key contention on credential-stuffing bursts. Lockout state in a shared cache also means one app's cache outage affects all of them if pools are shared.
Failure modes.
lock_interval (default 15 min). Requires a helpdesk runbook.Ramp-up. Low for engineers — it is YAML. Higher for the organisation: security must define the lockout policy, support needs an unlock procedure, and audit needs evidence that the effective cache pool is persistent and monitored. Estimate one sprint for the pilot plus policy sign-off, then a low-effort rollout.
How can I help you explore Laravel packages today?