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.
## Getting Started
### Minimal Setup in Laravel
1. **Install the package** (if not already included via Symfony/Laravel):
```bash
composer require symfony/password-hasher:^8.1.0-BETA3
Note: Laravel 8+ includes this as a dependency; ensure you’re on ^8.1.0-BETA3 or later for new features.
Configure algorithms in config/password-hasher.php (or create it):
return [
'default' => 'bcrypt',
'algorithms' => [
'bcrypt' => ['cost' => 12],
'argon2id' => [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 2,
],
'sodium' => [],
],
];
First use case: Interactive password hashing
Use the new security:hash-password Artisan command (via Symfony CLI integration):
php artisan security:hash-password
echo "my_secure_password" | php artisan security:hash-password
First use case: Replace Laravel’s Hash facade
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory;
// In AppServiceProvider
$this->app->singleton(UserPasswordHasherInterface::class, function ($app) {
$factory = new PasswordHasherFactory($app['config']['password-hasher.algorithms']);
return new UserPasswordHasherInterface($factory, $app['config']['password-hasher.default']);
});
$factory = new PasswordHasherFactory(config('password-hasher.algorithms'));
$hasher = $factory->getPasswordHasher('argon2id'); // Explicit selection
$hash = $hasher->hashPassword($user, $plainPassword);
public function login(Request $request, UserPasswordHasherInterface $hasher) {
$user = User::where('email', $request->email)->first();
if ($user && $hasher->isPasswordValid($user, $request->password)) {
// Rehash on login (optional)
$user->password = $hasher->hashPassword($user, $user->password);
$user->save();
}
}
php artisan tinker
>>> $factory = new \Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory(config('password-hasher.algorithms'));
>>> $hasher = $factory->getPasswordHasher('default');
>>> User::chunk(100, function ($users) use ($hasher) {
... $users->each(function ($user) use ($hasher) {
... $user->password = $hasher->hashPassword($user, $user->password);
... });
... $users->save();
... });
php artisan tinker
>>> $hasher = app(\Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface::class);
>>> $hash = $hasher->hashPassword((object) [], readline("Enter password: "));
Hash FacadeHash facade to use Symfony’s UserPasswordHasherInterface:// app/Providers/AppServiceProvider.php
public function register() {
$this->app->bind(\Illuminate\Contracts\Hashing\Hasher::class, function ($app) {
$factory = new PasswordHasherFactory($app['config']['password-hasher.algorithms']);
return new class($factory, $app['config']['password-hasher.default']) implements \Illuminate\Contracts\Hashing\Hasher {
public function make($value, array $options = []) {
return $this->hasher->hashPassword((object) [], $value, $options);
}
// Delegate other methods...
};
});
}
public function hashPassword(User $user, string $plain) {
$algorithm = $user->isAdmin() ? 'argon2id' : 'bcrypt';
$hasher = $this->passwordHasherFactory->getPasswordHasher($algorithm);
return $hasher->hashPassword($user, $plain);
}
security:hash-password.public function test_stdin_hashing() {
$command = new \Symfony\Component\PasswordHasher\Command\HashPasswordCommand();
$input = new \Symfony\Component\Console\Tester\CommandTester($command);
$input->setInput('my_password');
$input->execute([]);
$this->assertStringStartsWith('$2y$', $input->getDisplay());
}
# Compare bcrypt vs. argon2id
hyperfine --warmup 3 'php artisan security:hash-password --algorithm=bcrypt'
hyperfine --warmup 3 'php artisan security:hash-password --algorithm=argon2id'
security:hash-password may hang if stdin is not properly piped (e.g., in some IDEs).--interactive flag for manual input:
php artisan security:hash-password --interactive
--password option:
php artisan security:hash-password --password="my_secret"
getPasswordHasherFromHash() fails on unsupported algorithms (e.g., SHA-1).try {
$hasher = $factory->getPasswordHasherFromHash($hash);
} catch (\Symfony\Component\PasswordHasher\Exception\UnsupportedAlgorithmException) {
$hasher = $factory->getPasswordHasher('bcrypt');
Log::warning("Legacy hash detected for user ID {$user->id}");
}
memory_cost/time_cost temporarily during migration.Queue::push(new RehashUserPassword($userId, $plainPassword));
cache()->put("user:{$user->id}:password_hash", $hash, now()->addDays(30));
cat passwords.txt | xargs -I {} php artisan security:hash-password --password="{}"
php artisan security:hash-password --algorithm=argon2id
readline() or --interactive for manual input.hyperfine before full migration.PasswordHasherInterface for proprietary algorithms.$hash = $hasher->hashPassword($user, $plain, [
'user_id' => $user->id,
'timestamp' => now()->timestamp,
]);
$hasher = $factory->getPasswordHasherFromHash($hash);
How can I help you explore Laravel packages today?