user2143356
user2143356

Reputation: 5607

How do I refresh/extend the remember_me cookie (expiry) after logging in using the remember_me feature?

I'm using Symfony 2.3 LTS.

I can fully use the remember_me feature in Symfony, including setting it and logging back in after session expiry. However my issue is that once the user is logged back in with the remember_me feature then the expiry isn't refreshed.

In other words, if you set the "lifetime" of the remember_me cookie to 14 days then regardless of how many time the user visits the site they will always need to re-authenticate with a full username/password (after 14 days.) I don't want to set a longer cookie as 14 days without visiting seems right.

I know how to manually set the remember_me cookie. I just need to know where to put that code.

I've tried these:

  1. This SO question doesn't do what I want and is very different.

  2. There doesn't appear to be any settings in the security.yml configuration (to refresh expiry.)

  3. Hooking in on the processAutoLoginCookie method in Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices, but that can't work as there's no access to a Response (to set the cookie manually.)

  4. Listening on the SecurityEvents::INTERACTIVE_LOGIN event looks a good idea, but InteractiveLoginEvent doesn't have access to the Response either. Here's an example.

  5. I could get messy by setting a request attribute using one of the above and then setting a response listener to detect for that, but I think that's too messy. There must be a better way to do it.

  6. I could use one of the above to listen on the request, generate a response (e.g. redirect), set the cookie, perform the redirect, but again that's not good enough.

Upvotes: 5

Views: 1423

Answers (2)

Johan Sölve
Johan Sölve

Reputation: 1

The expiry timestamp and the password are both part of the hash of the cookie value, so it seems that the rememberme cookie can only be set at login since the password must be available to extend the timestamp.

From https://stackoverflow.com/a/9069098:

If you are setting the rememberme cookie directly, you have to use the following format:

base64_encode(<classname>:base64_encode(<username>):<expiry-timestamp>:<hash>)

where the hash will be:

sha256(<classname> . <username> . <expiry-timestamp> . <password> . <key>)

Upvotes: 0

sjagr
sjagr

Reputation: 16502

My proposed approach:

  1. Set up a kernel.response listener
  2. Pass the SecurityContext to the listener constructor
  3. Detect a logged in user
  4. Manually set your remember_me cookie
  5. Set a session var to prevent future cookie sets
  6. Your new cookie will piggyback on the next page a user loads when logged in using the remember_me cookie.

services.yml

services:
    acme.response_listener:
        class: AcmeBundle\EventListener\ResponseListener
        arguments: ['@security.context']
        tags:
            - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }

ResponseListener

namespace AcmeBundle\EventListener;

use Symfony\Component\Security\Http\Event\FilterResponseEvent;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Cookie;

class ResponseListener
{
    /** @var \Symfony\Component\Security\Core\SecurityContext */
    private $securityContext;

    /**
     * Constructor
     * 
     * @param SecurityContext $securityContext
     */
    public function __construct(SecurityContext $securityContext)
    {
        $this->securityContext = $securityContext;
    }

    public function onKernelResponse(FilterResponseEvent $event)
    {
        $session = $event->getRequest()->getSession();
        if (
            $this->securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') // remember_me cookie used
            && !$session->get('cookie_extended') // cookie hasn't been extended
        ) {
            $response = $event->getResponse(); // get the Response object
            $cookie = new Cookie(...); // your cookie
            $response->headers->setCookie($cookie); // set that cookie!
            $session->set('cookie_extended', true); // prevent future cookie sets for the current session
        }
    }
}

I haven't tested this code but it should be enough theory to get you started in this direction.

Upvotes: 3

Related Questions