Create Custom Password Hasher in Symfony 7

Create Custom Password Hasher in Symfony 7

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 the required hashing algorithm.

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

PasswordHasher component can be installed using the following command:

composer require symfony/password-hasher

The password hashing class should implement PasswordHasherInterface. It requires defining the hash, verify and needsRehash methods. In the hash and verify methods recommended checking 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\Attribute\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 of 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. Hash: '.$hashedPassword;

The 3 Comments Found

Leave a Comment

Cancel reply

Your email address will not be published.