Create Custom Password Hasher in Symfony 6

Symfony provides PasswordHasher component for hashing and verifying passwords. This component supports various built-in password hashers. There can be a case when password hasher is not implemented for required hashing algorithm.

This tutorial provides example how to create custom password hasher in Symfony 6 application.

PasswordHasher component can be installed using the following command:

composer require symfony/password-hasher

The password hashing class should implement PasswordHasherInterface. It requires to define the hash, verify and needsRehash methods. In the hash and verify methods recommended to check the password length. It should be no longer than 4096 characters. For this purpose the isPasswordTooLong method can be used provided in the CheckPasswordLengthTrait.

src/Security/Hasher/CustomPasswordHasher.php

<?php

namespace App\Security\Hasher;

use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException;
use Symfony\Component\PasswordHasher\Hasher\CheckPasswordLengthTrait;
use Symfony\Component\PasswordHasher\PasswordHasherInterface;

class CustomPasswordHasher implements PasswordHasherInterface
{
    use CheckPasswordLengthTrait;

    public function hash(string $plainPassword): string
    {
        if ($this->isPasswordTooLong($plainPassword)) {
            throw new InvalidPasswordException();
        }

        return md5($plainPassword);
    }

    public function verify(string $hashedPassword, string $plainPassword): bool
    {
        if ('' === $plainPassword || $this->isPasswordTooLong($plainPassword)) {
            return false;
        }

        return md5($plainPassword) === $hashedPassword;
    }

    public function needsRehash(string $hashedPassword): bool
    {
        return false;
    }
}

For testing purpose create a User class that implements PasswordAuthenticatedUserInterface and has password property:

src/Entity/User.php

<?php

namespace App\Entity;

use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;

class User implements PasswordAuthenticatedUserInterface
{
    private string $password;

    public function getPassword(): string { return $this->password;}
    public function setPassword(string $password): void { $this->password = $password; }
}

Framework use

In the security.yaml file we can define custom password hasher using id option as follows:

src/Security/Hashconfig/packages/security.yaml

security:
    # ...

    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
            id: 'App\Security\Hasher\CustomPasswordHasher'

For testing purpose we can use the following controller:

src/Controller/TestController.php

<?php

namespace App\Controller;

use App\Entity\User;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;

class TestController
{
    #[Route('/')]
    public function index(UserPasswordHasherInterface $passwordHasher): Response
    {
        $user = new User();
        $plaintextPassword = 'pwd123';

        $hashedPassword = $passwordHasher->hashPassword($user, $plaintextPassword);
        $user->setPassword($hashedPassword);

        if (!$passwordHasher->isPasswordValid($user, $plaintextPassword)) {
            return new Response('Invalid password');
        }

        return new Response('Valid password. Hash: '.$hashedPassword);
    }
}

Standalone use

PasswordHasher component can be used in any PHP application independently from the Symfony framework. We can create an instance of password hasher using the PasswordHasherFactory class.

test.php

<?php

use App\Entity\User;
use App\Security\Hasher\CustomPasswordHasher;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;

require_once __DIR__.'/vendor/autoload.php';

$passwordHasherFactory = new PasswordHasherFactory([
    PasswordAuthenticatedUserInterface::class => [
        'class' => CustomPasswordHasher::class,
        'arguments' => [],
    ],
]);
$passwordHasher = new UserPasswordHasher($passwordHasherFactory);

$user = new User();
$plaintextPassword = 'pwd123';

$hashedPassword = $passwordHasher->hashPassword($user, $plaintextPassword);
$user->setPassword($hashedPassword);

if (!$passwordHasher->isPasswordValid($user, $plaintextPassword)) {
    echo 'Invalid password';
    die;
}

echo 'Valid password';

The 2 Comments Found

    • lindevs Reply

      Hi
      I updated the tutorial how to use custom password hasher standalone.

Leave a Comment

Your email address will not be published.