nidhognit/pass-security-bundle
A password security bundle for PHP applications that validates credentials against configurable strength rules and compromised-password checks. Drop it into registration and password-change flows to enforce safer passwords with reusable validators and simple configuration.
First, confirm the stack mismatch. Despite the Laravel framing, nidhognit/pass-security-bundle is a Symfony bundle, not a Laravel package — it ships with a *Bundle class, DependencyInjection/Extension.php, and service definitions meant for Symfony's container. There is no service provider, no facade, and no Laravel auto-discovery. If you are strictly on Laravel, you will not "install and use" this; you will read its checker logic and port it into a small standalone class (see Implementation Patterns).
If you are on Symfony (or a Laravel project that already hosts a Symfony container for some reason):
Install and pin. Last release was 2018-05-26, so constrain the version explicitly and check PHP compatibility against your runtime:
composer require nidhognit/pass-security-bundle
Register the bundle manually. There is no Flex recipe. Symfony 4+: add to config/bundles.php:
Nidhognit\PassSecurityBundle\NidhognitPassSecurityBundle::class => ['all' => true],
Symfony 3 / legacy: $bundles[] = new Nidhognit\PassSecurityBundle\NidhognitPassSecurityBundle(); in AppKernel.php.
Look here first, in this order:
DependencyInjection/Configuration.php — the actual config keys and defaults (length, character classes, dictionary checks, thresholds).Resources/config/services.* — the service ID you inject. This is the only reliable way to know the checker's public method names.DependencyInjection/*Extension.php — how config maps onto the service (constructor args vs. setter calls).Validator/ (if present) — a Symfony Constraint + ConstraintValidator you can drop directly onto a form field.First use case: reject weak passwords at registration. After registering the bundle and copying the default config, inject the checker service into your registration controller and call its check method, e.g.:
public function register(Request $request, /* checker service */ $checker): Response
{
$password = $request->request->get('password');
if (!$checker->check($password)) { // confirm method name in the service class
// surface a validation error, do not 500
}
// only now: password_hash($password, PASSWORD_DEFAULT)
}
Do not treat a false return as an exception path — surface it as a normal form validation error.
Inject the service, don't new it. The value of the bundle is the DI wiring plus config; instantiating the checker class yourself bypasses both. Autowire by the service ID if the class is not autowirable:
# config/services.yaml
services:
App\Controller\RegistrationController:
arguments:
$passwordChecker: '@<service.id.from.services.yml>'
Prefer the Validator constraint if one ships. A Symfony Constraint lets you attach the rule declaratively and get it rendered by the form error mapper for free:
$builder->add('plainPassword', RepeatedType::class, [
'constraints' => [new NotWeakPassword()], // name per the bundle's Validator/ dir
]);
This is the pattern that survives refactors; controller-level checks tend to get copied inconsistently across login, reset, and invite flows.
Centralize the policy. Register a single application-level PasswordPolicy service that wraps the bundle's checker and your own rules (common-password dictionary, email/username reuse, haveibeenpwned-style checks). Call sites then depend on your interface, not the bundle — which is what makes the eventual removal (this package is unmaintained) a one-file change.
Registration flow, end to end:
password_hash() and persist. Never store or log the plaintext.Live-strength endpoint. Expose a small authenticated, rate-limited controller that runs the checker and returns a score/label as JSON for the signup form. Keep the authoritative check server-side on submit; the AJAX endpoint is UX only and must not be trusted.
Testing pattern. Because the bundle likely ships zero tests, pin behavior with your own data-provider test around the wrapper:
/**
* @dataProvider weakPasswords
*/
public function testRejectsWeak(string $password): void
{
self::assertFalse($this->policy->isAcceptable($password));
}
public static function weakPasswords(): array
{
return [['password'], ['123456'], ['qwerty'], /* ... */];
}
Laravel adaptation. If you cannot run the Symfony container, extract the checker class from the bundle (it is typically a plain PHP object with a couple of constructor scalars) into app/Support/PasswordSecurity, bind it in a PasswordSecurityServiceProvider, and expose it via a custom Rule object implementing Illuminate\Contracts\Validation\Rule. That gives you Laravel-idiomatic validation ('password' => ['required', new NotWeakPassword]) with the bundle's logic and none of its framework baggage.
bundles.php registration is the #1 cause of "service not found" on first install.Resources/config/services.* before trying to autowire. If the service is private (Symfony 4+ default), you must alias it or inject by ID; $container->get() will throw.Configuration.php. Don't guess names — read the tree builder. Unknown keys silently fail or throw depending on ignoreExtraKeys, which makes YAML typos painful to debug.decorates: <service.id> in Symfony and keep the original as the inner service — that composes cleanly and keeps the upgrade/removal path simple.PasswordPolicy::isAcceptable(): bool and never let the raw return value leak into controllers.password_hash with PASSWORD_DEFAULT and a needsRehash upgrade on login are non-negotiable.How can I help you explore Laravel packages today?