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: /
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;