Stop ACTA

CakePHP and the infamous "remember me" cookie

Posted in CakePHP on 13.02.2008.

There have been some posts and numerous questions on how to implement the "remember me" checkbox on user login. It seems like I've managed to accomplish this. It may not be the best way to do it, but it works.

Update (29/04/2008): This feature has been improved and packed as a component! Read about it here.

Update (03/07/2008): And again of course: remember me component - the final word

After reading A Hopefully Useful Tutorial For Using CakePHP’s Auth Component and the follow-up, it became apparent that this should be an easy thing to do. And it really is.

I've reused the code from the above links to write the cookie after user login. I'm assuming you have a UsersController with login and logout actions.

class UsersController extends AppController
{
    // your other stuff here..

    function login()
    {
        if ($this->Auth->user())
        {
            if (!empty($this->data))
            {
                if (empty($this->data['User']['remember_me']))
                {
                    $this->Cookie->del('User');
                }
                else
                {
                    $cookie = array();
                    $cookie['username'] = $this->data['User']['username'];
                    $cookie['token']    = $this->data['User']['password'];
                    $this->Cookie->write('User', $cookie, true, '+2 weeks');
                }

                unset($this->data['User']['remember_me']);
            }
            
            $this->redirect($this->Auth->redirect());
        }
    }

    function logout()
    {
        $this->Cookie->del('User');
        $this->redirect($this->Auth->logout());
    }
}

In your model, you can define the following function:

class User extends AppModel
{
    var $name = 'User';

    // your other stuff here..

    function checkLogin($username, $passhash)
    {
        $user = $this->find(array('username' => $username, 'password' => $passhash), array(), null, 0);

        if ($user)
        {
            $this->data = $user;
            $this->id = $user['User']['id'];
            return true;
        }

        return false;
    }
}

Now you have everything ready for the final piece of code. In your AppController, write the following beforeFilter() code:

class AppController extends Controller
{
    var $components = array('Auth', 'Cookie');
    var $uses = array('User');

    function beforeFilter()
    {
        Security::setHash('md5');

        $this->Auth->loginAction    = array('controller' => 'users', 'action' => 'login');
        $this->Auth->logoutRedirect = '/';
        $this->Auth->loginError     = 'Wrong username / password combination';
        $this->Auth->authError      = 'You must be logged in before you try to do that';
        $this->Auth->authorize      = 'controller';
        $this->Auth->autoRedirect   = false;

        $cookie = $this->Cookie->read('User');

        if (is_array($cookie) && !$this->Auth->user())
        {
            if ($this->User->checkLogin($cookie['username'], $cookie['token']))
                if (!$this->Auth->login($this->User))
                    $this->Cookie->del('User');
        }
    }
}

And you're done! This should auto-login any valid user before any action is taken.

There's a price of course, a slight overhead of reading a cookie and calling $this->Auth->user() on each request, but it's not horrible enough for me to avoid it. At least not yet :-)

Happy baking!

Article comments — View · Add


Page 1 of 2
1 · 2

Jasper :: 20.02.2008 03:49:04
data is an array with your username and password...

why not just use $this->Auth->login($data) ?

Auth component should take care of the rest
lecterror :: 20.02.2008 04:40:15
Do you mean the $cookie var? I've tried that and it didn't work. I couldn't figure out why, so I (ab)used the fact that Auth->login accepts model as a parameter. I'm still not sure what's the difference though..
Jelmer :: 09.03.2008 06:51:48
Interesting, just what I'm looking for! I've tried implementing this code into my own auth system which is pretty similar I think, but can't get it to work yet. I'm not sure what the problem is, but I think the cookie isn't being set, after login I look at the cookies set by entering this in my browser:

javascript:alert("Cookie:"+document.cookie)

Unfortunately all i get is something like Cakephp= "random string" I don't think this is correct, or is it?? Anyway it's not working yet, when I close my browser and visit my site again I'm logged out :(, any ideas on what I need to change??

Thanks in advance!
lecterror :: 10.03.2008 03:23:31
Hi, actually, the random string should be OK because the cookie is encrypted. Did you check your browser settings? It should keep the cookies until they expire. Also, is cookie system _sometimes_ works a bit weird: it doesn't log you in at first and you need to refresh the page (don't know the cause yet).
Jelmer :: 10.03.2008 10:00:48
Thanks for the reply, I've done some more testing using more simple cookie->write and read examples. I've found out that cookies are working, they are hashed but not stored in cakephp = "random value" so I'm pretty sure that the cookie I'm trying to set in the users login action isn't working right??

Maybe you could contact me to have a look at my code if you have some spare time, for now I'll try some more and see what I can come up with, any help would be appreciated though!

btw, maybe you could adjust your comments a bit so line breaks are displayed, comments can become a bit hard to read like this.