This has been an issue for me way too long.
I always hated the fact that users are logged out almost every few hours. Compared to other sites on the www it was ridiculously short, no matter how long you set timeout and cookieTimeout in core settings.
I looked into it more than twice in the last year coming up with almost nothing. After many hours of investigating I just postponed it again for times unknown.
It should be said that debugging the session/cookie based stuff – here Authentication – is quite a difficult past. Many things play into it making it hard to reproduce the issue.
The cake documentation also doesn’t tell us, that for php sessions to work this long it is also required to raise the internal max_lifetime. I stumpled across this by accident. Always thought this would be taken care of by cake itself.
But after switching to database as session container I didn’t notice any improvements either. So the server side garbage collector maybe wasn’t responsible after all. At least not all alone.
So, to sum it up:
- Setting up timeouts was a waste of time
- Security level, as well (I just left it at
low
) - Moving from php/cake to database didn’t work either – although it provides little more control
- Raising the php.ini settings for gc_maxlifetime didn’t work too well, either
I still get logged out after totally different lengths. Sometimes minutes, sometimes a few hours. It never lasts much longer.
Not only a deadly problem for a social network running with Cake.
I tried multiple times to add an encrypted cookie as RememberMe
functionally (to quicklogin such a logged out user again). But I failed – as I later found out due to a PHP bug with srand/mt_srand and suhosin (details).
Thanks to miles’ post regarding this bug I took a step forward and decided to take action (a lot of users have been complaining about unexplainable logouts the past months anyway).
Another not quire related problem is, that PHP (or Cake) stores the session cookies with a fixed expiring date. This date doesnt get updated with every request and therefore at some point will make the session cookie invalid. Nothing you can do about it at the moment…
AutoLogin? A way out?
I finally – after putting up with it for many years – adjusted Miles’ great spadework with his AutoLogin component to work with my apps.
Basically, this component saves the user data to a cookie on login. As soon as the session gets lost again (for whatever reason!?) the component re-logins the user. It happens silently without bothering the user.
The code can be found in my Tools plugin (the standalone rep. is deprecated).
Usage and Features
After successfully running it in a few cake2.x apps, I want to share the basic HOWTO.
The only required thing to do is adding the component globally in AppController:
public $components = array('Session', 'RequestHandler', 'AutoLogin', 'Auth', ...);
It is important to include it before any AuthComponent to avoid getting any "not logged in" error messages triggered by it.
You can use /Config/configs.php (or any other Configure place) to define its settings:
$config['AutoLogin'] = array(
'controller' => 'account',
'username' => 'login',
'requirePrompt' => false
... //see the component for details on other options
);
Please use the last one with caution. If requirePrompt
is disabled you use AutoLogin for ALL logins. This requires users to always log out correctly in public places (especially in internet cafes this can be quite dangerous as others opening the site could hijack this logged in user even after several days). So make sure you use is only for sites where all users are properly informed and educated on this.
So for the default case you will need to add a checkbox into your login form:
if (Configure::read('AutoLogin.active')) {
echo $this->Form->input('auto_login', array('type'=>'checkbox', 'label'=>__('Remember on this computer')));
}
That’s it.
You can easily test it using database sessions and truncating the session table after login.
It should log the user right back in as well as creating the new session table row. If everything is working right, the user shouldn’t even notice that he wasn’t logged in for a split second.
The if statement is optional. I use it to dynamically enable/disable the component based on the environment.
But if you use it, make sure you defined active
as true
in Configure.
Getting rid of the suhosin bug
As explained by Miles in the above links, most linux apache environments come with the suhosin patch which messes up srand by default. My localhost WAMP doesn’t. So there it worked right away. But to get it to work on the linux environment, all there is to do is:
In your /etc/php5/apache2/php.ini
add this line at the bottom:
suhosin.srand.ignore = Off
And don’t forget to restart apache or at least /etc/init.d/apache2 force-reload
.
TIP: I added a test case for this bug (see test file in the rep). Run it on every environment if you are not sure whether it is affected or not.
More tips
If you want to disable AutoLogin for specific sites you can simply set active
to false
for this site in your configs or dynamically in the controllers’ constructor.
Remember: Since you need to include it before Auth etc you cannot dynamically add it to the list of components. But you can always disable the included component this way 🙂
The debug mode is on auto-detect by default. If you develop locally (debug > 0) you have it enabled right away. But, of course, you can overwrite this value in your configs.
Another important setting is expires
– it defaults to 2 weeks
but can also easily be adjusted.
Alternatives
In 2.x there now are alternatives, as well. One is to use a custom CookieAuthenticate adapter for this.
Update 2013-06: Insight in the Cake session handling
Took me a while to realize how the sessions work in detail. Now I finally got it 🙂
I have to point out that using the above without knowing what you are doing will most likely just cloak the issue – or treat the symptoms.
It will work. But wouldn’t it be nice if it was working without the above remember me functionality?
For that we first have to understand how sessions are generated, validated and re-newed on page click:
- User visits the first time, session cookie will be generated (valid x seconds) and session timeout will be stored (y seconds)
- User clicks again: Of course we raise the session lifetime (valid y + z more seconds now) – stored inside the cookie as value
- Cookie itself does not get a new lifetime, though (still x seconds). That is a limitation in the cookie handling itself. This timeout limit is fixed.
Now that makes it pretty clear that even if you raise your session limit to years, if your cookie lifetime reaches the limit (it has been created with), the session will be destroyed either way.
Thus it is vital to understand that you want a high session limit and an even way higher cookie lifetime here for the settings to make sense:
Configure::write('Session', array(
'defaults' => 'php',
'timeout' => 14400, // 4 hours, refers to 'session.gc_maxlifetime' in PHP settings
'cookieTimeout' => 10 * 14400, // 20 hours, refers to 'session.cookie_lifetime' in PHP settings
// ...
));
Both values are set as minutes in CakePHP config.
With this the user is logged out if he has
- not shown any session (click, ajax) activity for consecutive 4 hours
- has reached the 20 hours cookie timeout (independent from your activity)
And as this will log him out after those 20 hours either way, it might make sense to set this limit to a huge value (months for example) or combine it again with the above remember me functionality.
So again:
'session.gc_maxlifetime' => relative value, will be raised on activity
'session.cookie_lifetime' => absolute value, can only be set at the beginning of the session
And don’t forget to check your ini settings regarding session.gc_divisor
and session.gc_probability
to make sure the garbage collector does his work once it a while.
Hope that clears things up.
Just FYI, I used Authsome and it includes "Remember me" functionality via a cookie (with a configurable expiry time).
https://github.com/felixge/cakephp-authsome
Hey Mark,
I’ve also been running into this issue with Cake. Some time ago I found this article: http://blog.room34.com/archives/4521. Did you try what is described there?
I stopped using php as session type years ago. Due to that problem. But it still remained a mystery – even with DB sessions etc.
So in the end nothing really helped – and the session never lasted as long as it was supposed to.
Well, with the Cookie solution this is now over, anyway 🙂
Hi Mark
i was also spending a lot of hours with this without finding a solution. so i’m happy to see this 🙂
i cant get it work up to now.
i shoud be one of these reasons:
i don’t use username, but "email"
how should i tell this to the component?
‘username’ => ’email’ ?
i have defined in Auth authenticate like this:
‘Auth’=>array(
‘authenticate’ => array(
‘Form’ => array(
‘userModel’ => ‘User’,
‘fields’ => array(
‘username’ => ’email’,
‘password’ => ‘password’
),
‘scope’ => array(
‘User.enabled’ => 1,
‘User.user_role_id in (1,2)’
)
)
),
do i have to change something in this config?
would be very happy about some help 🙂
exactly – I have an app with email, too.
with the same settings you have. that is supposed to work.
Thanks Mark, now it seems to work.
But i can’t find the cookie in my browser preferences… does it save the cookies in other places??
I have debug on and the email for debug also configured, but never see debug infos…
now i get the debug info via email.
do you know why i can’t see the cookie (f.e. in firefox preferences cookies)?
for local development.
You should find it under the local url, though. at least I do.
that’s it!
thanks very much mark!
it works, but now i get a ‘Cookie Mismatch’ error in email, everytime i navigate in my app (click on links) to a page which doesn’t requiere a login (f.e. pages/home, forgot password, etc..)
if i am loged in, there are no ‘Cookie Mismatch’ errors.
do you have an idea how i can get rid of that?
(if you are interested in seeing the site, i can give you the url by email).
Hi, after adding the Component, I can’t log in anymore. Because the salted PW doesn’t match the salted PW in the database anymore.
Any idea how this can happen. Could be not related to the Component, but is strange.
I dont think it does.
You would need to show your setup. But i am fairly certain that the component is separate from the normal login.
I’m using this component in your Tools object and it creates the cookie fine. Doesn’t seem to be doing the autologin though, like it’s not respecting that the cookie is there. Any ideas?
Great stuff.
Just to update you on the Suhosin thing. It’s not enough to play with the srand parameters on the Suhosin extension – you need to turn off transparent session and cookie encryption – this definitely breaks Cake 2.3 / PHP persistent sessions
suhosin.session.encrypt = Off
suhosin.cookie.encrypt = Off
suhosin.srand.ignore = Off
suhosin.mt_srand.ignore = Off