4

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.

Community
  • 1
  • 1
user2143356
  • 5,467
  • 19
  • 51
  • 95
  • Have you tried to create an event listener using onKernelRequest event and to insert your code in this function ? – Goku Feb 03 '15 at 16:19
  • Thanks for responding. A cookie can only be set in the Response. A lot of this is mentioned in my question. – user2143356 Feb 03 '15 at 16:42

2 Answers2

3

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.

sjagr
  • 15,983
  • 5
  • 40
  • 67
0

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>)
Community
  • 1
  • 1