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

Password Hasher Laravel Package

symfony/password-hasher

Symfony's PasswordHasher component provides secure password hashing utilities, with a factory to configure multiple algorithms like bcrypt and sodium. Hash plain passwords and verify them easily in your PHP applications.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Reframe first, before any stakeholder deck: symfony/password-hasher is a Symfony component, not a Laravel package. Laravel does not consume it. Illuminate\Hashing wraps PHP's native password_hash() / password_verify() and ships its own BcryptHasher, ArgonHasher, and Argon2IdHasher drivers behind the Hash facade. Any claim that this is a drop-in Laravel upgrade is wrong and should be corrected in the intake ticket.
  • In a Laravel codebase the realistic integration is additive: implement Illuminate\Contracts\Hashing\Hasher on top of Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory, bind it in a service provider, and select it per-tenant or per-role. It changes the hashing policy layer, not the framework's auth stack.
  • Where the package genuinely earns its place: the PasswordHasherInterface contract (hash / verify / needsRehash) plus MigratingPasswordHasher and LegacyPasswordHasher. needsRehash() is the actual product value — it turns "transparently rotate every password hash on next login" from a bespoke subsystem into a one-line check inside an auth listener.
  • Where it does not fit: the majority case — one algorithm, one cost factor, all users. Laravel's built-in bcrypt/Argon2id drivers already satisfy NIST SP 800-63B and OWASP ASVS guidance. Adopting this package there is dependency surface with zero behavioural gain, and I would close the evaluation.
  • Legitimate fit scenarios: per-tenant algorithm tiers (enterprise tenants on Argon2id, long-tail on bcrypt), an inherited legacy estate (MD5/SHA-1/$2a$ variants) that must remain verifiable during a migration window, or a polyglot platform where one hashing-policy abstraction is worth more than per-framework drivers.

Integration Feasibility

  • Technically small. The factory API is three calls. The adapter class is roughly 40 lines; the service-provider binding and a policy config array are trivial. No schema change, no data backfill, no re-index.
  • The real work is not the adapter, it is the surrounding correctness:
    • the rehash-on-login hook (Illuminate\Auth\Events\Login listener → needsRehash() → re-hash → persist → handle the fact that the session was already established),
    • the legacy verifier bridging pre-existing non-bcrypt rows,
    • the test matrix proving an old hash still authenticates and that a failed rehash write does not lock the user out.
  • Effort estimate: ~0.5 day for a proof of concept on one auth path; 1–2 weeks for production rollout across every auth surface (web login, API/Passport password grant, password reset, admin console, impersonation) including observability, feature flag, and rollback rehearsal.
  • Roughly 60% of that calendar time is review and QA, not coding — the diff is small but it touches authentication, so it needs a security reviewer and a deliberate release window.

Technical Risk

  • Security-critical blast radius. A defect here is an account-takeover or account-lockout incident, not a cosmetic bug. Mitigant: this is a subtree split of symfony/symfony, maintained by the Symfony core team, MIT licensed, and exercised by effectively the entire Symfony install base. The code quality risk is low; the integration risk is what we own.
  • Misleading telemetry — flag explicitly for procurement and SCA tooling. "807 stars" and "Dependents: 0" are both artifacts of the subtree-split packaging. Adoption happens through symfony/security-bundle and the monolithic symfony/symfony repo, not here. Do not let a supply-chain scanner read "0 dependents" as abandonment, and do not read 807 stars as independent popularity. Judge it on the parent project's maintenance record.
  • Version-line fragmentation. v8.x requires PHP >= 8.4; v7.x targets 8.2+; v6.4 is the LTS line for older runtimes. A mixed-version fleet means pinned minors per service and a real upgrade treadmill. This is the single largest unbudgeted cost.
  • Environment parity. Sodium/Argon2id depends on ext-sodium (or libsodium Argon2 support) present in dev, CI, staging, and every production container. A missing extension surfaces as a container-instantiation error — and the dangerous failure mode is someone "fixing" it by falling back to a weaker hasher in one environment, producing silent policy drift.
  • Configuration footguns. Cost/memory parameters tuned on a developer laptop will either weaken production or turn the login endpoint into a self-inflicted DoS. Bcrypt's 72-byte truncation is real behaviour that a legacy bridge can paper over rather than fix.
  • Supply chain: low. Single-purpose package, no transitive dependencies beyond PHP extensions, MIT.
  • Documentation depth is thin (README + releases only in-repo); real docs live on symfony.com under the Security chapter. Budget for one engineer to read the source of MigratingPasswordHasher — the migration semantics are where teams get surprised.

Key Questions

  1. Do we actually need multi-algorithm hashing, or is the requirement simply "bcrypt/Argon2id with a defensible cost factor"? If the latter, close this and tune config/hashing.php.
  2. What is the PHP version floor across every service that would adopt this? That determines branch (6.4 / 7.x / 8.x) and the ongoing upgrade burden.
  3. Who owns the needsRehash() login hook, and what happens if the rehash write fails after the session is established? (Answer must be: login succeeds, rehash is retried next time, never a lockout.)
  4. Do we have an audit obligation (SOC 2 / ISO 27001) to log algorithm changes per user? This package emits no events — we would have to instrument the listener ourselves.
  5. What is the p95 login latency SLO at peak concurrency, and which hash cost fits inside it? Get this from the platform team, not from a benchmark blog post.
  6. How many legacy hash formats must stay verifiable, and for how long? That defines the legacy bridge's lifespan and its removal ticket — which must exist before the bridge ships.

Integration Approach

Stack Fit

  • Laravel 10/11/12 on PHP 8.1+ (6.4 / 7.x line) or PHP 8.4+ (8.x line). Symfony applications already receive this transitively via symfony/security-bundle and should not install it directly — duplicating a transitive dependency is how version conflicts start.
  • It sits below the auth layer, so it composes cleanly with Fortify, Breeze, Jetstream, custom guards, and Passport's password grant.
  • Explicit non-goal: this does not touch Sanctum/Passport API token hashing, signed-URL hashing, or session/remember-token storage. Leave those on their existing drivers.
  • Deliverable shape: a PasswordHasherManager service exposing hash(), verify(), needsRehash(), resolving a named hasher from a policy map (e.g. tenant_tier → hasher_name). One class, one binding, one config block — that is the entire surface a reviewer needs to read.

Migration Path

  • There is no bulk rehash. Hashes cannot be recomputed without plaintext. The only viable model is verify-old → rehash-on-success → retire-when-quiet. Any plan that promises a cutover date is wrong; this is traffic-driven, not calendar-driven.
  • Phase 0 — decide and document (1 week). Confirm the fit case from the key questions above. If it's a single-algorithm app, stop here.
  • Phase 1 — shadow write (1 week). New registrations and password resets use the target policy via the new manager. Existing rows untouched and still verified by Hash. Feature-flagged, dark-launched on one non-critical tenant or the internal admin console.
  • Phase 2 — rehash-on-login (1–2 weeks). Add the Login listener. Every successful login upgrades its own row. Emit a counter metric per (old algorithm → new algorithm) transition. This is the phase that needs the security review and the rollback plan.
  • Phase 3 — monitor and retire (90-day window). Watch the legacy-verifier hit rate decay toward zero. Users who haven't logged in for 90 days will still hit the legacy path — decide now whether they get forced through password reset at the window's end, and communicate that to support before it fires.
  • Phase 4 — remove the bridge. Delete LegacyPasswordHasher wiring and the old dependency pin. File the removal ticket in Phase 1, not Phase 4.
  • Sequencing dependency: Phase 2 cannot start until ext-sodium is confirmed present in every target environment's image, verified by a CI health check rather than a verbal assurance.

Compatibility

  • Backward compatible by construction during Phases 1–3: old hashes keep verifying, no user is forced to act.
  • Forward compatibility governed entirely by the PHP floor. Pin the package minor explicitly in composer.json; do not float on ^8.0 if any service runs PHP < 8.4.
  • Cross-check the Laravel Hash driver contract — Hash::check() argument order is (plain, hashed), while Symfony's verify() is ($hash, $plain). This inversion is the single most likely source of a subtle, test-passing, production-breaking bug. Write the adapter test first.

Operational Impact

Maintenance

  • Low steady-state. The package is mature, single-purpose, and released on Symfony's predictable cadence (patch releases are frequently "no significant changes"). Expect roughly quarterly minor bumps and occasional security-driven patches.
  • The maintenance cost is not the package — it is our adapter and the legacy bridge. Budget a standing owner. An unowned hashing-policy layer is how you end up with three algorithms in production and nobody able to say which users are on which.
  • Track the PHP floor as a first-class roadmap item: the jump from 7.x to 8.x is a PHP 8.4 migration, and it will arrive whether or not it is planned for.

Support

  • Escalation path is the Symfony issue tracker (issues and PRs are filed against the monorepo, not this repo — a support engineer who files on symfony/password-hasher will get redirected). No commercial SLA. No vendor to call.
  • Internal runbook needed for three scenarios, and only three: (a) login failing for a cohort, (b) rehash listener throwing, (c) environment missing ext-sodium. If the runbook grows past that, the integration is too clever.
  • Support-desk script for the Phase 3 forced-reset cohort. This is the highest-volume ticket generator in the whole project and it is entirely predictable.

Scaling

  • Hashing is deliberately CPU-expensive and is a login-path cost, not a request-path cost. At high concurrency the login endpoint becomes the bottleneck first. Size the cost factor against p95 login latency at peak, not average.
  • Argon2id/Sodium is memory-hard by design — concurrent logins multiply memory pressure. A login burst (post-incident, post-marketing-email, morning peak in a single timezone) is the realistic DoS vector. Rate-limit the login endpoint independent of this package's settings.
  • No horizontal scaling concern beyond container memory. No shared state, no cache, no network hop. This is a pure-CPU library and scales linearly with login workers.

Failure Modes

  • Rehash write fails mid-login → user must still be logged in; rehash retries on next login. Never surface this as an auth error. This is the top-priority test case.
  • ext-sodium absent in one environment → container-instantiation fatal. Loud and safe. The dangerous variant is a fallback to a weaker hasher, which is silent and is a policy violation. Forbid fallbacks in code review.
  • Legacy verifier removed too early → hard lockout for the dormant-user cohort, in bulk, discovered via support tickets rather than monitoring. Gate removal on the metric reaching zero, not on the calendar.
  • Cost factor raised without load testing → login latency spike, then timeouts, then a retry storm that looks like an outage. Treat any cost-factor change as a capacity change requiring a load test.
  • Argument-order inversion in the adapter → verification silently fails or, worse, a permissive path passes. Constant-time comparison inside the library mitigates timing-oracle risk, but not logic errors. Fuzz the adapter.
  • Two hashers configured for the same user cohort (config drift across services) → users intermittently unable to log in depending on which node they hit. Enforce a single source of truth for the policy map.

Ramp-Up

  • An engineer familiar with Laravel auth needs ~2 days to be productive here: half a day on the README and the factory API, half a day reading MigratingPasswordHasher and PasswordHasherInterface source, one day writing the adapter and its tests.
  • The scarce knowledge is not "how to hash" — it is "which cohort is on which algorithm, and why." That must live in the repo as a policy doc, not in someone's head.
  • Recommended first assignment: the proof of concept on the internal admin console (lowest external blast radius, real traffic, easy rollback). Ship it behind a flag, measure p95 login latency, and use that data to answer the cost-factor question before committing to the full rollout.
  • Decision checkpoint after the PoC: if the measured latency headroom is thin or the multi-algorithm requirement can't be articulated in one sentence, recommend not proceeding and closing with a config/hashing.php tune-up instead. A "no" here is a successful outcome, not a failed evaluation.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core