Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

Hello` everyone,
I migrated an application from Symfony 5.4 to Symfony 7.4. When I try to log in, the isPasswordValid function rejects the connection. I think it's the UserPasswordHasherInterface system that has changed, but I can't reproduce it.

Version 5.4:
security.yaml:

encoders:
     App\Entity\Local\User: sha512

Method for saving the password:

<?php

namespace App\Security;

use App\Entity\Local\User;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;

class PasswordUpdater
{
    private $encoderFactory;

    public function __construct(EncoderFactoryInterface $encoderFactory)
    {
        $this->encoderFactory = $encoderFactory;
    }

    /**
     * @param User $user
     * */
    public function hashPassword(User $user)
    {
        $plainPassword = $user->getPlainPassword();

        if (empty($plainPassword)) {
            return;
        }

        $encoder = $this->encoderFactory->getEncoder($user);
        $hashedPassword = $encoder->encodePassword($plainPassword, $user->getSalt());
        $user->setPassword($hashedPassword);
        $user->eraseCredentials();
    }
}

Login method:

public function loginAction(
        UserActivityDto $userActivityDto, 
        ConstraintViolationListInterface $validationErrors, 
        TranslatorInterface $translator, 
        UserPasswordHasherInterface $encoder, 
        UserTokenService $tokenService, 
        CaptchaService $captchaService, 
        Request $request
    ): Response
    {
        // Check constraints validation list
        $this->securityService->checkValidationErrors($validationErrors);
        
        // Get user from email of login field
        /** @var \App\Entity\Local\User $user */
        $user = $this->userRepo->findOneBy([
            'email'   => $userActivityDto->email,
            'enabled' => true])
        ;
        
        // Check login and password
        if (!$user || !$encoder->isPasswordValid($user, $userActivityDto->password))
        {
            throw new ValidationException($translator->trans('user.authentication_failed'));
        }

Version 7.4:
security.yaml:

     password_hashers:
        App\Entity\Local\User:
            algorithm: sha512

Method for saving the password:

<?php

declare(strict_types=1);

namespace App\Security;

use App\Entity\Local\User;

use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

final class PasswordUpdater
{
    public function __construct(
        private readonly UserPasswordHasherInterface $passwordHasher,
    ) {}

    public function hashPassword(User $user): void
    {
        // Get the new password.
        $plainPassword = $user->getPlainPassword();

        // Stop if there is no new password.
        if ($plainPassword === null || $plainPassword === '') 
        {
            return;
        }

        // Hash the new password.
        $hashedPassword = $this->passwordHasher->hashPassword(
            $user,
            $plainPassword
        );

        // Set the new password.
        $user->setPassword($hashedPassword);
        $user->eraseCredentials();
    }
}

Login method:

public function loginAction(
        Request $request
    ): Response
    {
        // Data deserialization.
        $userActivityDto = $this->serializer->deserialize(
            $request->getContent(),
            UserActivityDto::class,
            'json'
        );

        // Stop if any validation error.
        $this->securityService->checkValidationErrors(
            $this->validatorService->validate($userActivityDto)
        );

        // Get user from email of login field
        /** @var User|null $user */
        $user = $this->userRepository->findOneBy([
            'email'   => $userActivityDto->email,
            'enabled' => true
        ]);

        // Check login and password.
        if (
            !$user 
            || !$this->passwordHasher->isPasswordValid(
                $user, 
                $userActivityDto->password
            )
        )
        {
            return $this->json(
                [
                    'errors' => 
                    [
                        [
                            'property_path' => 'password',
                            'message'       => $this->translator->trans('user.authentication_failed')
                        ]
                    ]
                ], 
                Response::HTTP_BAD_REQUEST
            );
        }

An example of a user with whom I can connect in the old version but not in the new one:
plain password : }c6/U5!Eu*5De4NkLu8#
password (hash) : a9k+NwzMv4hwZ/uDDdoZ1JM2eLThA52eiy3fLosocl03r1eu+/yObawLBkc/3HFbrtDC5/QZJcSby9JvbZJCYA==
salt: ewO0G33GtrKc2Xk3FcIbSoZwVV4jPBVdiqXHUNG5

An example of a user with whom I can connect in the new version but not in the old one:
plain password : }c6/U5!Eu*5De4NkLu8#
password (hash) : BOoFutmaJ+rj1CDUHOhU3iBShHziXONPOKUCEaD1NFg2xmtdrJbVw1t0eOOMyvz/0QTM0ysxiHyqSI62Gp7dzA==
salt: ewO0G33GtrKc2Xk3FcIbSoZwVV4jPBVdiqXHUNG5

As we can see, the hashed password is not the same between the two versions, for the same plain password.

You must be logged in to vote

Replies: 1 comment · 1 reply

Comment options

Hello,

does your User class implement LegacyPasswordAuthenticatedUserInterface? This is what prompts user password hashers to get a salt from the user: https://symfony.com/blog/new-in-symfony-5-3-improvements-for-security-users#decoupled-passwords-from-users

You must be logged in to vote
1 reply
@michaelgastal
Comment options

Hello !
Thank you very much ! Yes it was the solution !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
2 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.