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

Hashing Laravel Package

marko/hashing

A secure hashing package for Marko/Laravel PHP apps, offering a driver-based manager for bcrypt, argon2, and other algorithms. Easily hash and verify passwords, generate secure hashes, and swap drivers via simple configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a read-only split of marko-php/marko; all issues, PRs, and docs live at https://github.com/marko-php/marko. Treat this repository as the published artifact only.

Minimal setup:

  1. composer require marko/hashing
  2. If your app uses package discovery/registration, confirm the hashing service provider is registered (check the framework's config/app.php equivalent or your bootstrap file). If there's a publishable config, run the standard vendor:publish equivalent so you get a config/hashing.php.
  3. Set the default driver (typically bcrypt or argon2id) via config or env.
  4. First use case every developer hits is user registration + login:
// Registration
$user->password = app(Hasher::class)->hash($request->password);

// Login
if (app(Hasher::class)->verify($request->password, $user->password)) {
    // authenticated
}

Where to look first, in order:

  • The Hasher contract (usually Marko\Hashing\Contracts\Hasher) — this is the stable surface you should code against.
  • The driver implementations (Bcrypt, Argon2/Argon2id) to understand option keys like rounds, memory, time.
  • The config file, to see which driver is default and what options are accepted.
  • Any Hash manager/facade or helper if the framework exposes one.

If the repo looks empty or has only a src/ and composer.json, that's expected for a read-only split — go upstream for usage examples and changelogs.

Implementation Patterns

Inject the contract, not a concrete driver. Type-hint Hasher in constructors and resolve it from the container. Hardcoding new BcryptHasher(...) ties you to one algorithm and bypasses config.

final class RegisterUser
{
    public function __construct(private Hasher $hasher) {}

    public function handle(array $data): User
    {
        $data['password'] = $this->hasher->hash($data['password']);
        return User::create($data);
    }
}

Centralize hashing in one place. Hashing inside a controller and a model mutator/observer is the most common double-hash bug. Pick one layer — usually an action/service or an explicit setPasswordAttribute mutator — and hash only there.

Rehash on successful login. Algorithms and cost factors change over time; the package should expose needsRehash($hash). On a valid verify, transparently upgrade:

if ($this->hasher->verify($plain, $user->password)) {
    if ($this->hasher->needsRehash($user->password)) {
        $user->password = $this->hasher->hash($plain);
        $user->save();
    }
}

Testing. Hashing is intentionally slow; tests that create hundreds of users will crawl. Two options:

  • Override the config in the test environment to a low cost/rounds (e.g. bcrypt rounds 4) — never do this in production.
  • Or bind a fake fast Hasher implementation in the container for tests that don't assert real hashing behavior.

For tests that do assert round-tripping, use the real hasher and just a handful of fixtures.

Seeding and ops tooling. For admin users, a CLI command that prompts for a password and prints/stores the hash is safer than hardcoding hashes in seeders. Use the same Hasher so cost settings stay consistent.

Integration with auth packages. If marko/auth (or similar) exists, it may expect a Hasher binding — check it resolves your configured driver rather than a hardcoded one. Wire login flows to verify() only, never to == on hashes.

Tokens are not passwords. Password reset tokens and API tokens should be compared with hash_equals() on a random stored value, or stored hashed with a fast hash (SHA-256) — not with password hashing. Don't route them through this package.

Gotchas and Tips

Password column length. Bcrypt output is 60 chars, but Argon2/Argon2id output is longer (~95–100+ chars) and PHP's default can change. Use VARCHAR(255) (or TEXT) so switching drivers doesn't truncate hashes and silently break logins.

Never hash twice. The second hash produces a hash of a hash; verify() will then always fail against the original plaintext. Symptom: registration "works" but every login fails. Audit mutators, observers, and form requests.

verify() returns bool, don't throw on mismatch. Design login flows around a boolean. Also handle users with no password (SSO-only, invited-but-not-set) — a null hash should short-circuit, not be passed to verify().

Cost/rounds tuning is a deployment decision. Raising bcrypt rounds or Argon2 memory/time makes login slower for everyone; measure. Keep dev/test values low for speed, but read production values from env so you can bump them without a code deploy.

Timing safety. Rely on the package's verify(); don't reimplement comparison. It should use PHP's password_verify under the hood, which is constant-time. Hand-rolled hash_equals on stored hashes is a red flag.

Optional pepper/secret. If the driver supports a global secret (pepper), document it and store it outside the DB. Rotating it invalidates every existing hash, so treat rotation as a migration, not a config tweak.

Logging. Never log plaintext passwords, request payloads containing them, or full hashes at info level. Exception handlers that dump request data are a common leak.

Testing speed trap. Lowering rounds in a .env.testing is fine — accidentally shipping that config is not. Add a guard/assertion that production never runs at test cost.

Extension points. To add a custom algorithm (e.g. scrypt via a library), implement the Hasher contract and register it as a driver with the manager, then select it by name in config. This keeps call sites unchanged.

Read-only repo reality. You cannot open a PR here. File upstream at marko-php/marko; also pin a version in composer.json since this split's release cadence follows upstream and a dev-main constraint can drift.

Debugging checklist when verification fails: confirm the stored value starts with the expected prefix ($2y$ for bcrypt, $argon2id$ for Argon2id), confirm the column is long enough, confirm you aren't trimming/normalizing the password differently at registration vs login, and confirm the driver in config is the same one used to create the hash.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource