ziming/laravel-zxcvbn
Laravel wrapper for zxcvbn, the realistic password strength estimator. It exposes zxcvbn-php through a simple facade and validation rule, returning scores, guesses, crack times, and actionable feedback so you can enforce stronger passwords in your app.
ziming/laravel-zxcvbn is a thin Laravel service-provider wrapper around bjeavons/zxcvbn-php, the offline port of Dropbox's zxcvbn password-strength estimator. The release under review (2.4.6) adds a Laravel 13 compatibility constraint plus CI/dependabot housekeeping — there are no API changes, no new features, and no deprecations. Treat this as a maintenance release: safe to take, no code edits required.
composer require ziming/laravel-zxcvbn
On Laravel 11+/12/13 the provider and facade are auto-discovered. For older skeletons, register Ziming\LaravelZxcvbn\LaravelZxcvbnServiceProvider in bootstrap/providers.php (or config/app.php) and alias the facade.
First use case — score a password inside a Form Request:
use Ziming\LaravelZxcvbn\Facades\LaravelZxcvbn;
$result = LaravelZxcvbn::password($this->input('password'))->check([
$this->input('email'),
$this->input('name'),
]);
if ($result->getScore() < 3) {
// reject, surface $result->getFeedback()
}
Where to look first, in order:
php artisan vendor:publish --tag=zxcvbn-config) — check the config key/alias names before you hardcode the facade in a dozen classes.src/ — it is only a few lines; confirm which methods are proxied and whether check() accepts user inputs.vendor/bjeavons/zxcvbn-php — this is where Result, Feedback, getScore(), getCrackTimeSeconds() and getSequence() actually live. Almost all your day-to-day work happens against that object, not the wrapper.Because the release only widened framework support, the real upgrade question is the PHP floor: Laravel 13 raises the minimum PHP version, so run composer why-not ziming/laravel-zxcvbn ^2.4 before bumping if you are still on PHP 8.1/8.2 or Laravel 10/11.
Custom validation rule (the 90% case). Wrap the check in a rule object so it composes with confirmed, min:8, and the rest of your pipeline:
class StrongPassword implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$userInputs = array_filter([
request()->input('email'),
request()->input('name'),
config('app.name'),
]);
$result = LaravelZxcvbn::password($value)->check($userInputs);
if ($result->getScore() < 3) {
$fail($result->getFeedback()->getWarning()
?: 'This password is too easy to guess.');
}
}
}
Score thresholds as config, not magic numbers. Persist the minimum score (0–4) in config/auth.php or a dedicated config/password.php. Registration, password reset, and profile-update flows drift apart otherwise, and you will want to tighten the threshold without redeploying business logic across five controllers.
Feed user inputs, always. The single biggest quality win is passing email, username, and app name into check() as user inputs. zxcvbn penalises passwords containing them, which is exactly the class of weak password a length-only rule misses.
Two-stage validation: local score + breach check. zxcvbn is an offline entropy heuristic and will happily score Tr0ub4dor&3x! highly even if it appears in every breach corpus. Pair the score gate with a k-anonymity HaveIBeenPwned range query (or a bundled breach list) and reject on either failure. Keep the two concerns in separate rules so failures stay distinguishable in logs.
Feedback as UX, not as validation. Map getScore() to a strength meter client-side (debounced), but always re-run the check server-side. getFeedback()->getSuggestions() is a plain string array; render it under the field and cap it at two or three items.
Testing. The library is deterministic for a given password and user-input set, so assert on the score directly rather than mocking the facade — it is fast enough for unit tests on short strings. Where you do need isolation (e.g. asserting the rule's failure message), LaravelZxcvbn::shouldReceive('password') works, but over-mocking hides real regressions in the score thresholds.
Laravel 13 upgrade path. Because 2.4.6 only widens constraints, the migration is: bump the package, run the suite, and confirm your composer.json require block now resolves to a single zxcvbn version. If you support both Laravel 12 and 13 in a matrix, no conditional code is needed — the wrapper API is identical on both.
^2.4 range will pull it silently. Read it as "Laravel 13 support + CI bumps," nothing more. The dependabot PRs (actions/checkout, composer-install, git-auto-commit-action, fetch-metadata) never touch runtime code.composer show ziming/laravel-zxcvbn after install; a surprise PHP 8.3+ floor breaks CI images pinned to older runtimes.getCalcTime() breaks array equality. toArray() on the result includes calculation timing, so snapshots and assertSame against a full array fail intermittently. Assert on getScore() and getFeedback()->getWarning() instead.required|min:8 before the strength rule so users don't see a blank feedback message.userInputs must be an array of scalars. Passing an Eloquent model or a nested array silently degrades the penalty calculation (or throws inside the matcher). array_filter() your inputs and cast to string.Log::debug($result); the result includes the matched sequence, which can embed fragments of the password. Log the score and the warning only.Zxcvbn (custom ranked dictionaries, e.g. your product's brand terms) via a service provider binding. That is the cleanest way to teach the estimator your domain vocabulary without touching the package.bjeavons/zxcvbn-php, not from Laravel.How can I help you explore Laravel packages today?