I am just upgrading my symfony 4.4 application to 5.3 to use some new cool stuff (UX, UUID, ..). So I started a new project and ran the make:auth
command to create the security components at latest defaults. Everything works perfect, except the remember me functionality. The cookie is just not set (regardless which browser). Maybe you can help me
security.yaml
security:
enable_authenticator_manager: true
password_hashers:
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\LoginFormAuthenticator
pattern: ^/
logout:
path: _logout
target: _index
remember_me:
secret: '%env(APP_SECRET)%'
lifetime: 31536000 # 1 year in seconds
always_remember_me: true
path: _index
switch_user: true
role_hierarchy:
ROLE_USER: ROLE_USER
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
access_control:
- { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/reset-password, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/datenschutz, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/impressum, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/worker, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/, role: ROLE_USER }
LoginFormAuthenticator
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = '_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): PassportInterface
{
$email = $request->request->get('email', '');
$request->getSession()->set(Security::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('_index'));
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
public function supportsRememberMe(): bool
{
return true;
}
}
login.html.twig
{% extends 'base.html.twig' %}
{% block title %}{{ ('meta.title.login')|trans }}{% endblock %}
{% block body %}
<h1>{{ ('security.login.header')|trans }}</h1>
<form method="post">
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<div class="mb-3">
<label for="inputEmail" class="form-label">{{ ('security.login.email')|trans }}</label>
<input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" autocomplete="email" required autofocus>
</div>
<div class="mb-3">
<label for="inputPassword" class="form-label">{{ ('security.login.password')|trans }}</label>
<input type="password" name="password" id="inputPassword" class="form-control" autocomplete="current-password" required>
</div>
<div class="mb-3">
<a href="{{ path('app_forgot_password_request') }}">
{{ ('button.forgot_password')|trans }}
</a>
</div>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
<button class="btn btn-lg btn-outline-success" type="submit">
{{ ('security.login.button')|trans }}
</button>
</form>
{% endblock %}
Thanks a lot in advance!!
--------------- EDIT ----------------------
I originally asked this question for version 5.4, but is relevant for 5.3 as well - I tried both without getting the cookie to be set
I setup a test app and confirmed the remember me cookie was not being sent but then I cheated and saw the hint on the Symfony Slack channel. When using the new passport based authentication system you need to use the remember me badge. It's documented here.
So update your Authenticator::authenticate method with:
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
new RememberMeBadge(),
]
);
It all seemed to work for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With