Weave Code
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 PasswordHasher provides secure password hashing and verification utilities. Configure multiple algorithms via PasswordHasherFactory (bcrypt, sodium/Argon2, etc.), hash plain passwords, verify hashes, and support upgrades with modern best practices.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel

  1. Install the package (via Symfony’s Composer repository):
    composer require symfony/password-hasher
    
  2. Create a Laravel service provider (e.g., HashServiceProvider) to bind the Symfony factory:
    use Illuminate\Support\ServiceProvider;
    use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory;
    use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
    
    class HashServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $factory = new PasswordHasherFactory([
                'default' => ['algorithm' => 'bcrypt'],
                'admin'   => ['algorithm' => 'argon2id'],
            ]);
            $this->app->singleton(UserPasswordHasherInterface::class, function () use ($factory) {
                return $factory->getPasswordHasher('default');
            });
        }
    }
    
  3. Register the provider in config/app.php:
    'providers' => [
        // ...
        App\Providers\HashServiceProvider::class,
    ],
    
  4. First use case: Replace Laravel’s Hash::make() with the Symfony hasher:
    use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
    
    $hasher = app(UserPasswordHasherInterface::class);
    $hash = $hasher->hashPassword($user, 'plain-text-password');
    

Where to Look First

  • Documentation: Symfony Password Hasher
  • Key classes:
    • PasswordHasherFactory (configures algorithms)
    • UserPasswordHasherInterface (core hashing/verification)
    • HashUtil (utility methods for hash analysis)
  • Laravel integration: Focus on HashServiceProvider and UserPasswordHasherInterface binding.

Implementation Patterns

Core Workflows

1. Algorithm-Specific Hashing

Use the factory to assign different algorithms to user roles:

$factory = new PasswordHasherFactory([
    'users'   => ['algorithm' => 'bcrypt'],
    'admins'  => ['algorithm' => 'argon2id', 'memory_cost' => 64],
    'api_keys'=> ['algorithm' => 'sodium'],
]);

// Retrieve hasher for a role
$adminHasher = $factory->getPasswordHasher('admins');
$hash = $adminHasher->hash('secure-password');

2. Laravel Facade Wrapper

Extend Laravel’s Hash facade to delegate to Symfony:

// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Hash as LaravelHash;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

public function boot()
{
    LaravelHash::macro('makeSymfony', function ($password, $user = null) {
        $hasher = app(UserPasswordHasherInterface::class);
        return $hasher->hashPassword($user, $password);
    });
}

// Usage:
$hash = Hash::makeSymfony('password', $user);

3. Auto-Upgrade on Login

Leverage Symfony’s needsRehash() to upgrade hashes during authentication:

use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

public function login(Request $request, UserPasswordHasherInterface $hasher)
{
    $user = User::find($request->user_id);
    if ($hasher->needsRehash($user->password)) {
        $user->password = $hasher->hashPassword($user, $request->plain_password);
        $user->save();
    }
    // Proceed with auth...
}

4. Hash Format Detection

Use HashUtil to analyze existing hashes (e.g., for migrations):

use Symfony\Component\PasswordHasher\Hasher\HashUtil;

if (HashUtil::checkPrefix($hash, 'bcrypt')) {
    // Migrate bcrypt to Argon2
    $newHasher = $factory->getPasswordHasher('argon2id');
    $user->password = $newHasher->hashPassword($user, $user->password);
}

Integration Tips

  • Database Schema: Ensure columns can store Argon2/Sodium hashes (e.g., VARCHAR(255)).
  • Configuration: Centralize algorithm settings in config/hash.php:
    return [
        'algorithms' => [
            'default' => ['algorithm' => 'bcrypt'],
            'secure'  => ['algorithm' => 'argon2id', 'memory_cost' => 128],
        ],
    ];
    
  • Testing: Mock UserPasswordHasherInterface in unit tests:
    $this->partialMock(UserPasswordHasherInterface::class, ['hashPassword', 'verify'])
         ->shouldReceive('verify')
         ->with($user->password, 'wrong')
         ->andReturn(false);
    

Gotchas and Tips

Pitfalls

  1. Hash Length Mismatch:

    • Argon2/Sodium hashes are longer than bcrypt (e.g., 255 chars vs. 60). Ensure your database column supports this.
    • Fix: Use VARCHAR(255) or TEXT for password fields.
  2. Algorithm Auto-Detection:

    • Symfony’s needsRehash() may fail if the hash format isn’t recognized (e.g., custom bcrypt parameters).
    • Fix: Use HashUtil::checkPrefix() to explicitly check hash types before upgrading.
  3. Sodium/Argon2 Dependencies:

    • Sodium requires PHP extensions (php-sodium). Argon2 needs php-argon2 (PECL).
    • Fix: Add to composer.json:
      "require": {
          "ext-sodium": "*",
          "php-argon2": "^1.0"
      }
      
  4. Laravel’s Hash Facade Conflicts:

    • Overriding Laravel’s Hash facade can break third-party packages (e.g., Laravel Fortify).
    • Fix: Use a namespace alias (e.g., SymfonyHash) or conditional logic:
      if (config('hash.use_symfony')) {
          return $symfonyHasher->hashPassword($user, $password);
      }
      return Hash::make($password);
      
  5. Memory Cost Tradeoffs:

    • High memory_cost in Argon2 improves security but slows down authentication.
    • Tip: Start with memory_cost: 64 and monitor performance.

Debugging Tips

  • Verify Hashes:

    $hasher->verify($hash, 'wrong-password'); // Throws \Symfony\Component\PasswordHasher\Exception\InvalidArgumentException
    

    Catch exceptions to handle invalid inputs gracefully.

  • Check Hash Format:

    if (!HashUtil::checkPrefix($hash, ['bcrypt', 'argon2id'])) {
        throw new \RuntimeException('Unsupported hash format');
    }
    
  • Log Hashing Operations:

    $hasher->hashPassword($user, $password, function ($hash) {
        \Log::debug('Generated hash', ['hash' => substr($hash, 0, 10).'...']);
        return $hash;
    });
    

Extension Points

  1. Custom Hashers: Implement PasswordHasherInterface for bespoke algorithms:

    use Symfony\Component\PasswordHasher\Hasher\PasswordHasherInterface;
    
    class CustomHasher implements PasswordHasherInterface
    {
        public function hash(string $plainPassword): string { /* ... */ }
        public function verify(string $hashedPassword, string $plainPassword): bool { /* ... */ }
        public function needsRehash(string $hashedPassword): bool { /* ... */ }
    }
    

    Register via PasswordHasherFactory:

    $factory->addPasswordHasher('custom', new CustomHasher());
    
  2. Hash Metadata: Store algorithm parameters in the hash (e.g., argon2id$memory_cost=64$...):

    $hasher->hashPassword($user, $password, ['memory_cost' => 128]);
    
  3. Event-Driven Hashing: Trigger events on hash generation/verification:

    $hasher->hashPassword($user, $password, function ($hash) {
        event(new PasswordHashed($user, $hash));
    });
    
  4. Rate-Limited Hashing: Use Symfony’s PasswordHasherInterface to integrate with Laravel’s rate-limiting middleware:

    if (config('hash.rate_limit')) {
        $this->middleware('throttle:hash,1')->only(['store']);
    }
    

Laravel-Specific Quirks

  • Model Events: Listen for retrieved/saved events to auto-upgrade hashes:
    User::retrieved(function ($user) {
        $
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport