symfony/password-hasher
Symfony PasswordHasher provides secure password hashing and verification with modern algorithms like bcrypt and sodium. Use PasswordHasherFactory to configure multiple hashers and select the right one for your app’s needs.
Installation:
composer require symfony/password-hasher
Laravel users can skip this if using Laravel 8+ (included by default).
Basic Configuration (Laravel):
Update config/auth.php or config/app.php to leverage the package:
'hashers' => [
'default' => [
'algorithm' => 'bcrypt',
'cost' => 12, // Adjust based on performance/security tradeoffs
],
'admin' => [
'algorithm' => 'argon2id',
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 2,
],
],
First Use Case: Hashing a Password
Replace Hash::make() with the Symfony hasher in your User model or registration logic:
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
// In a service or controller
$passwordHasher = app(UserPasswordHasherInterface::class);
$hash = $passwordHasher->hashUserPassword($user, 'plain-text-password');
Verification:
if ($passwordHasher->isPasswordValid($user, 'submitted-password')) {
// Password is correct
}
Legacy Migration (if needed):
Use the rehash() method to upgrade old hashes:
$passwordHasher->rehashUserPassword($user, 'plain-text-password');
Illuminate\Auth\Passwords\PasswordBroker (uses Symfony under the hood).Use the PasswordHasherFactory to dynamically select algorithms:
$factory = new PasswordHasherFactory([
'bcrypt' => ['algorithm' => 'bcrypt', 'cost' => 14],
'argon' => ['algorithm' => 'argon2id', 'memory_cost' => 128 * 1024],
]);
$bcryptHasher = $factory->getPasswordHasher('bcrypt');
$argonHasher = $factory->getPasswordHasher('argon');
Assign algorithms per user role (e.g., admins get Argon2id):
// In User model
public function getPasswordHasherName(): string
{
return $this->isAdmin() ? 'argon' : 'bcrypt';
}
Bind the factory to the container in AppServiceProvider:
public function register()
{
$this->app->singleton(PasswordHasherFactory::class, function ($app) {
return new PasswordHasherFactory([
'default' => ['algorithm' => 'bcrypt', 'cost' => 12],
]);
});
}
Rehash legacy passwords during authentication:
public function attemptLogin(Request $request)
{
$user = User::where('email', $request->email)->first();
if ($user && $this->passwordHasher->isPasswordValid($user, $request->password)) {
$this->passwordHasher->rehashUserPassword($user, $request->password);
// Proceed with login
}
}
Extend PasswordHasherInterface for bespoke algorithms:
class CustomHasher implements PasswordHasherInterface
{
public function hash(string $plainPassword): string { /* ... */ }
public function verify(string $hashedPassword, string $plainPassword): bool { /* ... */ }
}
Register it in the factory:
$factory = new PasswordHasherFactory([
'custom' => new CustomHasher(),
]);
Hash Facade: Use UserPasswordHasherInterface directly for granular control.Illuminate\Auth\Passwords\PasswordBrokerManager to use Symfony’s hasher.UserPasswordHasherInterface in unit tests:
$hasher = $this->createMock(UserPasswordHasherInterface::class);
$hasher->method('hashUserPassword')->willReturn('$2y$10$hashed');
$this->app->instance(UserPasswordHasherInterface::class, $hasher);
symfony/security:hash-password CLI tool to test latency:
php bin/console security:hash-password
PasswordHasherFactory to avoid recreating hashers.User::chunk(100, function ($users) {
foreach ($users as $user) {
$this->passwordHasher->rehashUserPassword($user, $user->password);
}
});
Algorithm Mismatch Errors:
InvalidArgumentException when verifying hashes.getPasswordHasherName() or factory configuration.Argon2id Resource Usage:
memory_cost or time_cost values.memory_cost: 64MB, time_cost: 3) and benchmark.Legacy Hash Detection:
RuntimeException for unsupported hash formats (e.g., MD5).PasswordHasherFactory::supports() to check compatibility:
if (!$factory->supports($user->password)) {
$user->password = $factory->getPasswordHasher('bcrypt')->hash($user->password);
}
Laravel Caching Conflicts:
Auth::logoutOtherDevices() post-update.PHP Extensions:
ClassNotFoundException for Sodium or Argon2.php.ini:
extension=sodium
extension=php_argon2
php artisan tinker
>>> $hasher = app(Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface::class);
>>> $hasher->isPasswordValid($user, 'test-password') // true/false
$factory->getSupportedAlgorithms(); // ['bcrypt', 'argon2id', 'sodium']
$hasher->hashUserPassword($user, $password)
->then(function ($hash) { Log::debug("Password hashed for user {$user->id}"); });
Default Algorithm:
bcrypt. Explicitly define it to avoid surprises:
'hashers' => [
'default' => ['algorithm' => 'bcrypt'], // Force bcrypt
]
Sodium Fallback:
sodium algorithm) may not be available on all hosts. Test with:
if (!class_exists(Sodium::class)) {
throw new RuntimeException('Sodium extension required for sodium algorithm.');
}
Laravel Hash Facade:
Hash::make() and Symfony’s hasher. Stick to one for consistency.Custom Password Hasher:
Implement PasswordHasherInterface for proprietary algorithms (e.g., GPU-accelerated hashing).
Event Listeners: Trigger events for hash operations:
event(new PasswordHashed($user, $hash));
event(new PasswordVerified($user, $result
How can I help you explore Laravel packages today?