Search code examples
phpsymfonysulu

Remember me function for custom user login in Sulu CMS


I created a website with a user login using Sulu CMF. For the user login, I would like to create a "remember me" checkbox. According to Symfony docs, all that need to be done is

  • adjust firewall settings in security.yaml
  • add a checkbox to the form, with name="_remember_me"
  • add Remember Me Support to the Authenticator

Symfony docs link

The first two points are straight forward. For the third point, "add Remember Me Support to the Authenticator" I am a bit lost, as with sulu I am not using a custom authenticator, but Sulu rather provides the authentication mechanism, with Sulu users and roles an all that.

How can I tell the Sulu authenticator to add the RememberMeBadge to the authentication Passport as described in the docs?

Update: When loggin in, the server responds with the REMEMBERME cookie, but with the value "deleted".


Solution

  • Finally I got it working by creating a custom Authenticator. The reason why it did not work in the first place is due to the symfony FormLoginAuthenticator that creates the RememberMeBadge, but does not enable it. See https://github.com/symfony/security-http/blob/7.0/Authenticator/FormLoginAuthenticator.php

    Here you see that the RememberMeBadge is disabled by default: https://github.com/symfony/security-http/blob/7.0/Authenticator/Passport/Badge/RememberMeBadge.php

    That fact that the badge is created but not enabled, does not make sense to me, maybe this is a bug in the Symfony framework.

    However, a custom Authenticator that creates and enables the badge, solved the issue for me.

    <?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\Http\Authenticator\AbstractLoginFormAuthenticator;
    use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
    use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
    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\SecurityRequestAttributes;
    use Symfony\Component\Security\Http\Util\TargetPathTrait;
    
    class CustomAuthenticator extends AbstractLoginFormAuthenticator
    {
        use TargetPathTrait;
    
        public const LOGIN_ROUTE = 'app_login';
    
        public function __construct(private UrlGeneratorInterface $urlGenerator)
        {
        }
    
        public function authenticate(Request $request): Passport
        {
            $username = $request->request->get('_username', '');
    
            $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $username);
    
            return new Passport(
                new UserBadge($username),
                new PasswordCredentials($request->request->get('_password', '')),
                [
                    new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
                    (new RememberMeBadge())->enable(),
                ]
            );
        }
    
        public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
        {
            if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
                return new RedirectResponse($targetPath);
            }
    
            // redirect to homepage
            return new RedirectResponse('/');
        }
    
        protected function getLoginUrl(Request $request): string
        {
            return $this->urlGenerator->generate(self::LOGIN_ROUTE);
        }
    }
    

    The authenticator was created using the symfony cli command php bin/console make:auth with some minor adjustments.