Symfony sfGuardUser “remember me” checkbox/cookie does not work

It seems that the sfGuard plugin only checks the “remember me” cookie if the user attempts to access a secure module. This is fine if your site requires a login for any access, and thus is always is_secure: on, however if you have any “public” pages which logged in and non-logged in users can access, users that have selected the “remember me” box will not be logged in automatically.

Solution:

Of course this could be fixed in the plugin, but if you do not want to (and shouldn’t) modify the plugin code directly, you can add another filter which will check the cookie. Assuming you have already installed the sfGuardPlugin, and have a “remember me” checkbox implemented, this is all you will need to do to get the automatic logins on non-secure pages:

in apps/yourapp/config/filters.yml add the remember me filter

rendering: ~
web_debug: ~
security:
  class: sfGuardBasicSecurityFilter
 
# generally, you will want to insert your own filters here

remember:
  class: rememberMeFilter
 
cache:     ~
common:    ~
flash:     ~
execution: ~

Now create rememberMeFilter.class.php in apps/yourapp/lib
This example uses Propel

class rememberMeFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    // execute this filter only once, and if the user is not already logged in, and has a cookie set
    if ($this->isFirstCall() && !$this->getContext()->getUser()->isAuthenticated()
        && $this->getContext()->getRequest()->getCookie(sfConfig::get('app_sf_guard_plugin_remember_cookie_name', 'sfRemember')))
    {
      // See if a user exists with this cookie in the remember database
      $c = new Criteria();
      $c->add(sfGuardRememberKeyPeer::REMEMBER_KEY, $this->getContext()->getRequest()->getCookie(sfConfig::get('app_sf_guard_plugin_remember_cookie_name', 'sfRemember')));
      $c->add(sfGuardRememberKeyPeer::IP_ADDRESS, $this->getContext()->getRequest()->getHttpHeader ('addr','remote'));
 
      if ($resultArray = sfGuardRememberKeyPeer::doSelectJoinsfGuardUser($c))
      {
        $resultRow = current($resultArray);
        $this->getContext()->getUser()->signIn($resultRow->getSfGuardUser());
      }
    }
    // execute next filter
    $filterChain->execute();
  }
}

That should do it, now your users will be logged in on any page if they have the cookie set. The sfGuardPlugin will still take care of setting the cookie and clearing it on logout.

Remember when using these cookies, it’s good practice to ask the user to re-enter their password when doing anything sensitive, like submitting an order or changing any personal details like their password.

A follow up by Shiny explains that you can also get this working by securing modules with empty permission sets like this:

all:
  is_secure: on
 
index:
  credentials: []

Be careful with setting that globally though if you’re trying to set a “secure it first, grant permissions later” style system, because with the above – everything is accessible until secured.

15 Comments so far

  1. Junni on July 24th, 2008

    Thanks for this piece of code. I will try it immediately in my projects.

  2. sh1ny on July 24th, 2008
  3. Jacques Philip on July 25th, 2008

    The problem with this solution is that there is code checking for the remember cookie and authenticating both in the sfGuardBasicSecurityFilter and the rememberMeFilter, thus duplicating the code.
    I did something similar, but modified the sfGuardBasicSecurityFilter to remove the cookie authentication code, then placed the rememberMeFilter before the SecurityFilter in the chain.
    Of course, the disadvantage is the the custom security filter does not get updated when sfGuard is updated

  4. Guglielmo Celata on July 25th, 2008

    It seems to me that you can avoid the duplication of code.
    The sfGuardBasicSecurityFilter and the rememberMeFilter seem to do quite exactly the same thing. Apart from the fact that the last checks over the IP stored in the sf_guard_temember table.

    I have the sfGuardBasicSecurityFilter class working, by simply changing the filters.yml file.

    I leave the security: section blank (using a ~ just to be sure). And I add a remember: section with class: sfGuarfBasicSecurityFilter.

    That seems to do the job.
    Probably the security filter is only invoked for secured pages, while all others are always invoked. As a matter of the fact, even if you rename the filter section as foo: (instead of rememebr:) everything works properly.

  5. Russ on July 30th, 2008

    On the duplication front – I’m not too concerned, because the entry point is most likely the home page, which is not secure, so only the remember filter will run in those cases, and for logged in users the first “if” statement will stop the execution early.

    Interesting point on the filters.yml file though, I haven’t tried that but it does make sense that there is some logic somewhere which skips the security: section if a page is secure: no/off/false

    I’ll be sure to try that on my next project, but for now it’s working and time is short 😉

    Thanks for the links Sh1ny – in fact I saw those pages while I was searching around – and they helped me to get some ideas. The last section of the article was added after I found your follow up article – I’ll add a credit. (Should have done before really – sorry)

  6. sh1ny on July 30th, 2008

    Nothing to sorry about. All of this is really a trivial problem, that is not documented anywhere and i suspect has been around since ages. After digging around for the problem, i found out the guy who did symfonians.net had this solved using the first approach ( with adding a new filter ), but i couldn’t find any referent posts to this. Maybe some people see it as an obvious solution or too easy to figure, but im sure the average symfony newcomer won’t guess it first try. So the more people write about it, the higher the chance that we help someone, even for a small task like this 🙂

  7. […] Symfony sfGuardUser “remember me” checkbox/cookie does not work […]

  8. […] Does your remember me cookie not work? […]

  9. Christian on September 11th, 2008

    Great remark, Guglielmo Celata! I followed your advise and the remember me cookie works! But I got another problem: this modification seems to trigger a global secure: on! That is, once I m not anymore logged, I have the login page by default and cannot access any other non-secure page anymore without relogging. Any idea why? Does anybody have the same problem?

    Thanks again for this insightful post!

  10. Russ on September 11th, 2008

    Hi Christian,

    You probably have to explicitly set security: off in all your “insecure” modules with this approach, I think I have seen it mentioned elsewhere also.

    Regards,
    Russ.

  11. Christian on September 11th, 2008

    Hi Ross,

    thanks for your instant reply. I tried to set

    all:
    is_secure: off

    for a module that does not need to be secure, but it didn t change anything. I also tried

    all:
    is_secure: off
    credentials: []

    without success. Very strange indeed… Any other suggestions? Thank you in advance!

  12. fullo on December 5th, 2008

    I fixed overriding the isAuthenticated() method in the myUser class:

    I’ve added the code in a trac comment http://trac.symfony-project.org/ticket/3868

  13. […] recorded first by twhid on 2009-03-08→ Symfony sfGuardUser “remember me” checkbox/cookie does not work […]

  14. saturn1 on September 6th, 2009

    Thank you, very usefull 🙂

  15. charles on October 9th, 2009

    Hi,

    I’m concerned about this IP checking thing. Your solution worked like a charm for me but for a day only, since most ISPs renew their IPs each day when you get back to your site the day after your cookie value no longer matches your IP which has probably changed over night. I’m working on a solution that does not rely on your IP. Keep you guys posted…

Leave a reply