Search code examples
phpsymfonycookiessymfony4remember-me

Symfony4 does not find user according to remember me cookie


I have configured symfony 4 to set a remember me cookie when the users checks a checkbox. This works as it should. But when I restart the Browser and return to the Website the website deletes the cookie. The symfony log looks like this:

[Application] Oct 21 14:14:12 |DEBUG  | SECURI Remember-me cookie detected. 
[Application] Oct 21 14:14:12 |INFO   | SECURI User for remember-me cookie not found. 
[Application] Oct 21 14:14:12 |DEBUG  | DOCTRI SELECT t0.id AS id_1, t0.email AS email_2, t0.roles AS roles_3, t0.password AS password_4, t0.is_verified AS is_verified_5, t0.pending_surfpoints AS pending_surfpoints_6, t0.surfpoints_total AS surfpoints_total_7, t0.balance AS balance_8, t0.username AS username_9, t0.ref_earning AS ref_earning_10, t0.ref_id AS ref_id_11 FROM user t0 WHERE t0.email = ? LIMIT 1 0="myUsername"
[Application] Oct 21 14:14:12 |DEBUG  | SECURI Clearing remember-me cookie. name="REMEMBERME"

I configured the User login to work with an email and a username (my changes are mentioned below). I think now the problem is, that the remember me cookie is searching for the username in the username section of my database.

I changed the getUser function in the UserAuthentificatorAuthenticator.php to look for an email, and if that fails for the username:

public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);


        if (!$user) {
            //Email could not be found - try username
            $user = $this->entityManager->getRepository(User::class)->findOneBy(['username' => $credentials['email']]);
            if (!$user){
                // fail authentication with a custom error
                throw new CustomUserMessageAuthenticationException('Email/Username konnten nicht gefunden werden.');
            }

        }
        if (!$user->isVerified()){
            throw new CustomUserMessageAuthenticationException('Der Account wurde noch nicht aktiviert.');

        }

        return $user;
    }

Where can I now adapt the cookie authentificator to make it work?

Edit: My security.yaml

security:
    encoders:
        App\Entity\User:
            algorithm: auto

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            lazy: true
            provider: app_user_provider
            guard:
                authenticators:
                    - App\Security\UserAuthentificatorAuthenticator
            logout:
                path: app_logout
                # where to redirect after logout
                target: app_default_start
            remember_me:
                secret:   '%kernel.secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /

Solution

  • The built-in EntityUserProvider you are using now looks up the User object according to the property specified in the configuration. The remember me feature uses the Provider to look up the user, and as your user can log in using either email or username, when looking up by email only, the user is not found with the default one. You should move the lookup logic out of the Authenticator and into a UserProvider.

    Add the following function to your UserRepository and make it implement UserLoaderInterface:

    public function loadUserByUsername($identifier)
    {
        $qb = $this->createQueryBuilder('u');
        return $qb->where('u.username = :identifier')
            ->orWhere('u.email = :identifier')
            ->setParameter('identifier', $identifier)
            ->getQuery()
            ->getOneOrNullResult();
    }
    

    In security.yaml, remove property:

    app_user_provider:
        entity:
            class: App\Entity\User
    

    Change your authenticator:

    $token = new CsrfToken('authenticate', $credentials['csrf_token']);
    if (!$this->csrfTokenManager->isTokenValid($token)) {
        throw new InvalidCsrfTokenException();
    }
    
    $user = $userProvider->loadUserByUsername($credentials['email']);
    
    if (!$user) {
        throw new CustomUserMessageAuthenticationException('Email/Username konnten nicht gefunden werden.');
    }
    
    if (!$user->isVerified()){
        throw new CustomUserMessageAuthenticationException('Der Account wurde noch nicht aktiviert.');
    }
    
    return $user;