Symfony 5.3 - why is my remember me functionality not working?
Asked Answered
A

3

7

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

Ascendancy answered 11/7, 2021 at 11:21 Comment(6)
5.4 hasn't been released yet.Signature
Same behaviour for 5.3 which is releasedAscendancy
Should path not be the actual path and not the name of the path?Avalanche
This has no effect to be honest. I can leave it blank for the default value or enter / or anything elseAscendancy
The config looks okay. Use your browser's network tools to verify the REMEMBERME cookie is being sent as expected.Counterpoison
No, it isn't. It is not set and I don't know whyAscendancy
C
15

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.

Counterpoison answered 11/7, 2021 at 13:41 Comment(1)
That is true, I just added that badge and it is working for me now as wellAscendancy
B
0

I had same problem, cookie REMEMBERME was set but not called when PHPSESSID was deleted . After spending time I found in my User entity class this (I added after an upgrade of Symfony without thinking about it) :

public function getUserIdentifier() {
  return $this->id;
}

But in secutiy.yml, my user provider is :

    app_user_provider:
        entity:
            class: App\Entity\User
            property: email

So I changed my getUserIdentifier function to this and it works now :

public function getUserIdentifier() {
  return $this->email;
}
Bambibambie answered 11/8, 2021 at 16:14 Comment(2)
Not working in Symfony >=5.3.Rozalin
This works for me. Since the changes with UserInterface and PasswordAuthenticatedUserInterface in SF 5.3, the comments for the function getUserIdentifier is Returns the identifier for this user (e.g. its username or email address)..In the past the function was getUsername but it's deprecated/removed. This was confusing for me.Botulism
I
0

The solution for me was to delete cookies from localhost, it seems that it conflicted with cookies from other projects.

Iciness answered 10/1 at 8:46 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Parker

© 2022 - 2024 — McMap. All rights reserved.