ZF2: Why are my sessions logging out? - php

My ZF2 application logs out after a short period of inactivity - say, 60 minutes or so - and I can't understand why.
I have an 'auth' object which is a singleton that composes an instance of Zend\Session\Container. Its constructor creates the container with this following line:
$this->session = new Container('Auth');
The auth object has a login() method that stores the current user with the following line:
$this->getSession()->userId = $user->id;
The auth object also has an isLoggedIn() method that tests the status as follows:
if ($this->getSession()->userId) {
return true;
}
return false;
That's all pretty straightforward. Yet, from time to time when the bootstrap is checking to see if we are logged in, it comes back with false. Why?
Here's a printout of the config from the session manager:
'cookie_domain' => '',
'cookie_httponly' => false,
'cookie_lifetime' => 604800,
'cookie_path' => '/',
'cookie_secure' => '',
'name' => 'MyApplication',
'remember_me_seconds' => 1209600,
'save_path' => '/var/lib/php5',
'use_cookies' => true,
As you can see, the remember_me_seconds and cookie_lifetime are set to 2 weeks and 7 days respectively. Is there some other setting that I should be looking at?
I read somewhere that the default save handler, 'file', does not support concurrency. My bootstrap also opens a session container to the auth namespace with new Container('Auth'). Could this be conflicting with the Container in the auth singleton ? I doubt it, since the problem would then be likely to occur in periods of high activity (not after a period of inactivity). Also, I would expect to see an exception.
Woe is me.
EDIT: It is also worth noting that the session ID does not change when logged out, or upon logging back in.

There are many points why a session can become invalid.
check always following points:
session cookie lifetime (should become invalid only when closing the browser)
session lifetime itself
cache_expire key in zf2 (should be higher than session lifetime)
Try to add this
//NEW SECTION
'cache_expire' => 60 * 26, <-- this may help
'gc_maxlifetime' => 60 * 60 * 24, <-- or this

Related

Default lifetime in Cache Component

I need configure default lifetime from cache Adapter, but something weird has been happening, the follows don't works!? :/
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
// in seconds; applied to cache items that don't define their own lifetime
// 0 means to store the cache items indefinitely (i.e. until the files are deleted)
$cache = new FilesystemAdapter('my_namespace', 5); // <-- default lifetime 5 seconds
$latestNews = $cache->getItem('latest_news');
if (!$latestNews->isHit()) {
$news = ['title' => '...', 'createdAt' => (new \DateTime())->format('Y-m-d H:i:s')];
$cache->save($latestNews->set($news));
} else {
$news = $latestNews->get();
}
Reference http://symfony.com/doc/current/components/cache/cache_pools.html#filesystem-cache-adapter
The first time, the cached file content shows:
2147483647 <-- 2038-01-18 22:14:07 :/ ?
latest_news
a:2:{s:5:"title";s:3:"...";s:9:"createdAt";s:19:"2016-10-07 09:16:50";}
and of course this item don't expire after 5 seconds :/ (I've cleared the cache directory manually).
On the other hand, if we use $latestNews->expiresAfter(5); all works fine:
1475849350 <-- 2016-10-07 10:09:10 \o/ OK
latest_news
a:2:{s:5:"title";s:3:"...";s:9:"createdAt";s:19:"2016-10-07 10:09:05";}
Reference http://symfony.com/doc/current/components/cache/cache_items.html#cache-item-expiration
5 seconds after the item expired correctly.
I tested that with Symfony\Component\Cache\Adapter\ApcuAdapter and occurs the same problem too.
What happens with default lifetime (constructor parameter) in cache adapters ? I missing something here :/ ?
Is an old issue [Cache] Fix default lifetime being ignored that affect framework version prior to the 3.1
Upgrading the Symfony framework should fix it.

ZF2 authentication session storage in memcached

In our intranet application(s) we use SSO (single sign on) login while the sessions both on client and auth origin applications are stored in memcached.
The sessions are set to live for 12h before the garbage collector may consider them as for removal. Both applications are written using ZF2.
Unfortunately, the problem is, that after certain period of time (I don't have the exact value) the browser loses the session which causes the redirection to auth origin, where the session is still alive thus user is redirected back to client and the browser session is refreshed. This is not a big deal if the user has no unsaved work as these two redirects happen within 1 second and user even may not notice them.
But it really is a big deal when user has unsaved work and even an attempt to save it leads to redirects and the work is gone.
Here is the configuration of session in Bootstrap.php:
class Module
{
public function onBootstrap(MvcEvent $e)
{
// ...
$serviceManager = $e->getApplication()->getServiceManager();
$sessionManager = $serviceManager->get('session_manager_memcached');
$sessionManager->start();
Container::setDefaultManager($sessionManager);
// ...
}
public function getServiceConfig()
{
return array(
'factories' => array(
// ...
'session_manager_memcached' => function ($sm) {
$systemConfig = $sm->get('config');
$config = new SessionConfig;
$config->setOptions(array(
'phpSaveHandler' => 'memcache',
'savePath' => 'tcp://localhost:11211?timeout=1&retry_interval=15&persistent=1',
'cookie_httponly' => true,
'use_only_cookies' => true,
'cookie_lifetime' => 0,
'gc_maxlifetime' => 43200, // 12h
'remember_me_seconds' => 43200 // 12h
));
return new SessionManager($config);
},
// ...
);
}
}
The authentication service is defined as
'authService' => function ($sm) {
$authService = new \Zend\Authentication\AuthenticationService;
$authService->setStorage(new \Zend\Authentication\Storage\Session('user_login'));
return $authService;
},
the session storage uses the same memcached session manager.
Then anywhere within the application a session value needs to be retrieved or set I just use a \Zend\Session\Container like this:
$sessionContainer = new \Zend\Session\Container('ClientXYZ');
$sessionContainer['key1'] = $val1;
// or
$val2 = $sessionContainer['key2'];
The SSO is requested for the active session at any action using the token from session which contains PHPSESSID from the auth origin. It's quite complicated to describe here within this question.
Additionally an authentication service stores a user identity (with roles for ACL) also in memcached session - using the same settings. Obviously this is now the place which causes confusion. Apparently the session storage of authentication service times out prematurely causing the ACL to retrieve no user identity to check leading into SSO logout sequence (but because user didn't really log out, SSO redirects the user back as described above).
I'm not sure how much code should I (and can I) share here, maybe you'll lead me to the solution straight away or just by asking me some questions. I am quite helpless right now after many hours of debugging and trying to identify the problem.
Somewhere I have read that memcached wipes out the memory once the session cookie gets 1MB in size - may this be the case? For the user identity we save just general user information and array of roles, I'd guess this could be max. up to few kb in size...
EDIT 1: To dismiss all guesses and to save your time, here few facts (to keep an eye on):
only memcached is used
cookies serve only to transport the PHPSESSID between the browser and server and it's value is the key for memory chunk in memcached where the data is stored
client and SSO auth apps are running on one server (be it integration, staging or live environment, still just one server)
session on client app goes off randomly causing it to redirect to SSO auth app, but here the session is still alive thus user is redirected back to client app which gets new session and user stays logged in
this should dismiss discussion about memcached being wiped off or restarted
also observation on telneted memcached directly shows both data chunks (for client and auth apps) are established almost at the same time with the same ttl
I am going to implement some dies in PHP and returns in JS parts to catch the moment when the session is considered gone and further inspect the browser cookie, memcached data, etc. and will update you (unless somebody comes with explanation and solution).
public function initSession()
{
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions([
'cookie_lifetime' => 7200, //2hrs
'remember_me_seconds' => 7200, //2hrs This is also set in the login controller
'use_cookies' => true,
'cache_expire' => 180, //3hrs
'cookie_path' => "/",
'cookie_secure' => Functions::isSSL(),
'cookie_httponly' => true,
'name' => 'cookie name',
]);
$sessionManager = new SessionManager($sessionConfig);
// $memCached = new StorageFactory::factory(array(
// 'adapter' => array(
// 'name' =>'memcached',
// 'lifetime' => 7200,
// 'options' => array(
// 'servers' => array(
// array(
// '127.0.0.1',11211
// ),
// ),
// 'namespace' => 'MYMEMCACHEDNAMESPACE',
// 'liboptions' => array(
// 'COMPRESSION' => true,
// 'binary_protocol' => true,
// 'no_block' => true,
// 'connect_timeout' => 100
// )
// ),
// ),
// ));
// $saveHandler = new Cache($memCached);
// $sessionManager->setSaveHandler($saveHandler);
$sessionManager->start();
return Container::setDefaultManager($sessionManager);
}
This is the function I use in order to create a cookie for X user. The cookie lives for 3 hours, no matter if there are redirects or if the user has closed the browser. It's still there. Just call this function in your onBootstrap() method from Module.php.
While logging, I use The ZF2 AuthenticationService and the Container to store and retrieve the user data.
I suggest you to install these module for easier debugging.
https://github.com/zendframework/ZendDeveloperTools
https://github.com/samsonasik/SanSessionToolbar/
Memcached & gc_maxlifetime
When using memcached as session.save_handler, garbage collection of session will not be done.
Because Memcached works with a TTL (time to live) value, garbage collection isn't needed. An entry that has not lived long enough to reach the TTL age will be considered "fresh" and will be used. After that it will be considered "stale" and will not be used any longer. Eventually Memcached will free the memory used by the entry, but this has nothing to do with session garbage collection of PHP.
In fact, the only session.gc_ setting that's actually used in this case is session.gc_maxlifetime, which will be passed as TTL to Memcached.
In short: garbage collection is not an issue in your case.
Memcached & Cronjobs
As you are using Memcached as storage for your sessions, any cronjobs provided by the OS that will manually clean session folders on disk (like Ubuntu does) will have no effect. Memcached is memory storage, not disk storage.
In short: cronjobs like this are not an issue in your case.
Issue of app, not SSO
You state that the SSO server/authority is on the same machine as the SSO client (the application itself), is using the same webserver / PHP configuration, and is using the same instance of Memcached.
This leads me to believe we have to search in how session management is done in the application, as that is the only difference between the SSO authority and client. In other words: we need to dive into Zend\Session.
Disclaimer: I've professionally worked on several Zend Framework 1 applications, but not on any Zend Framework 2 applications. So I'm flying blind here :)
Configuration
One thing I notice in your configuration is that you've set cookie_lifetime to 0. This actually means "until the browser closes". This doesn't really make sense together with remember_me_seconds set to 12 hours, because a lot of people will have closed their browser before that time.
I suggest you set cookie_lifetime to 12 hours as well.
Also note that remember_me_seconds is only used when the Remember Me functionality is actually used. In other words: if Zend\Session\SessionManager::rememberMe() is called.
Alternative implementation
Looking at the way you've implemented using Memcached as session storage, and what I can find on the subject, I'd say you've done something different than what seems to be "the preferred way".
Most resources on this subject advise to use Zend\Session\SaveHandler\Cache (doc, api) as save-handler, which gives you the ability to use Zend\Cache\Storage\Adapter\Memcached (doc, api). This gives you much more control over what's going on, because it doesn't rely on the limited memcached session-save-handler.
I suggest you try this implementation. If it won't immediately resolve your issue, there are at least a lot more resources to find on the subject. Your chances of finding a solution will be better IMHO.
This answer might not immediately address the cause of your memcache issue, but because of the unreliable nature of memcache I would suggest to make a backup of your memcached data in some persistent storage.
Memcaching your data will help you to improve performance of your application but it is not fail-safe.
Maybe you can make a fallback (persistent) storage in your AuthenticationService instance. Then first you try to get your authentication data from your memcache and if nothing is found you check if there is something available in your persistent storage.
This will at least solve all issues with unexpected memcache loss issues.

Yii: $_SESSION undefined, but Yii::app()->session works

I have enabled session within my application like this (in config.php, components section):
'session' => array(
'class' => 'system.web.CDbHttpSession',
'connectionID' => 'db',
'timeout' => 86400,
'sessionName' => 'SOMEABSTRACT_PHPSESSID',
),
In UserIdentity I try to do this:
$_SESSION['userid'] = $model->id;
Which means that I will set that new session after I log in my user. But when I try to access the $_SESSION['userid'] or just $_SESSION — for reading — I get this exception:
Undefined variable: _SESSION
This is the way I'm trying to access this session array:
echo '<pre>';
die(var_dump($_SESSION, $_SESSION['userid']));
I don't have any clues about this, so my two simple questions for this issue are:
Why is this happening only when Yii's sessions are enabled and autoStart => true?
How to fix this?
PS I need this for CometChat integration with Yii framework.
You can have #session_start();. This will suppress the warnings if it session already starts and also set the variable like this Yii::app()->session['userid'] = $model->id; and retrieve the same with Yii::app()->session['userid'] wherever it is required.
Also you can set the logged in user like this after session start
$this->_id=$model->id;
$this->setState('title', $model->username);
$this->setState('role', $model->role);
Above are the default variables in yii for logged in users. To create new user entry in session you can have this Yii::app()->session['variablename']
Try like this, in your UserIdentity.php -
//After successfully authentication, create session
Yii::app()->session;
Yii::app()->session['userid'] = $user->id;
//Now you can get anywhere like this -
echo Yii::app()->session['userid'];

Zend Framework 2 (ZF2) auth session refresh using storage

I have looked all over and I can see where people have created the initial session for ZF2 auth, remember me's, etc, but I can't find where people are updating the session when there is activity. Basically, I already have an authentication (with doctrine) system and my current solution and I set up the following configuration setting:
return array (
'session' => array(
'cookie_lifetime' => 1800, // 30 min
'remember_me_seconds' => 1800, // 30 min
'use_cookies' => true,
),
);
Then what I am trying to do is RELOAD this on every request like this:
NOTE: I have code that only does this if the user is already logged in.
class Module
{
public function onBootstrap(EventInterface $e)
{
$this->getEventManager()->attach('route', array($this, 'onRoute'), -100);
}
public function onRoute(EventInterface $e)
{
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
$sessionManager = new SessionManager($sessionConfig);
$sessionManager->rememberMe($config['session']['remember_me_seconds']);
$sessionManager->start();
}
}
My basic need is I'm trying to refresh the session (server and client) anytime there is a request, but 1. it feels like I'm re-creating it every time and 2. Sometimes the session seems to randomly die. I think this is because the original session dies after the 30 min I set it to.
Any advice?
PHP should be updating the session time for you, you don't need to do it manually.
Also, don't call rememberMe() on every request, as this will generate a new session token (assuming the session already exists).

Error message while logging in in Kohana

I'm using Kohana 3 and I have an issue while logging in with an user.
I use this line to log in:
$success = Auth::instance()->login($_POST['login_user'], $_POST['login_password'], $remember);
And I got this error message:
Session_Exception [ 1 ]: Error reading session data. ~ SYSPATH/classes/kohana/session.php [ 326 ]
I have the sessions table created with the follow SQL:
CREATE TABLE `sessions` (
`session_id` varchar(24) NOT NULL,
`last_active` int(10) unsigned DEFAULT NULL,
`contents` text,
PRIMARY KEY (`session_id`),
KEY `sessions_fk1` (`last_active`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
And also the session.php inside the config folder:
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'database' => array(
/**
* Database settings for session storage.
*
* string group configuation group name
* string table session table name
* integer gc number of requests before gc is invoked
* columns array custom column names
*/
'group' => 'default',
'table' => 'sessions',
'gc' => 500,
'columns' => array(
/**
* session_id: session identifier
* last_active: timestamp of the last activity
* contents: serialized session data
*/
'session_id' => 'session_id',
'last_active' => 'last_active',
'contents' => 'contents'
),
),
);
?>
What might be the problem here?
Thanks!
I don't know if this will help, but I had a similiar problem.
The cause of this was that using one library (Facebook SDK), session was initialized on it's onw, and session handling was done using the $_SESSION variable. I noticed that there were two cookies - session (Kohanas session id) and PHPSESSID. This probably was the problems cause.
I modified the library so that id doesn't start the session on its own and the problem was solved.
So, you should probalby check if session isn't started elsewhere.
Session_Exception [ 1 ]: Error reading session data. ~ SYSPATH/classes/kohana/session.php [ 326 ]
Depends what version you're running, but this is caused by an exception being thrown when session data is being unserialized in read. You can see the bug report about it here: Session read errors not properly ignored. The solution would be to upgrade to the latest version if you haven't already.
Something else you need to look at is your session data. You'll have to see why your data is corrupt and can't be read properly. This could be an error generated from code in __sleep.
Workaround or solution for me was setting php.ini
session.auto_start = 0
Of course, restart your web-server
Not sure if you figured this out. But I had the same issue and it was related to my php config. I'm using NGNIX and php-fpm. By default my session files were trying to get saved to a directory that didn't exist. So I changed the session.save_path to a valid path and that fixed it.
One way to solve this is to instantiate a session instance before you create a Facebook SDK instance. For example:
$this->_session = Session::instance('native');
$this->_facebook = new Facebook(array(
'appId' => 'app_id',
'secret' => 'app_secret',
));
If you take a look at the code inside the constructor of the Facebook class you'll see it checks if a session has been started:
public function __construct($config) {
if (!session_id()) {
session_start();
}
parent::__construct($config);
if (!empty($config['sharedSession'])) {
$this->initSharedSession();
}
}
So if you create the session first it'll skip that block of code.
I have had such problems when switching to an online server (more than once :(, so it should be better put some clear guidance).
Recommendations:
§) If you are using Database session adapter:
Session::$default = 'database';
i.- Check that your DB credentials are correct.
ii.- Check that the table assigned to sessions data has correct type and size.
§) If you are using Encryption for your sessions data (config/session.php or config/.../session.php):
return array(
'cookie' => array(
// ...
'encrypted' => TRUE,
// ...
),
'database' => array(
// ...
'encrypted' => TRUE,
// ...
),
);
i- Check that you have mcryptinstalled:
$ php -m|grep mcrypt
mcrypt // installed
ii- Check that you are using the same key was used to encrypt data (config/encrypt.php or config/.../encrypt.php):
return array(
'default' => array(
'key' => 'yabadabadoo!',
'cipher' => MCRYPT_RIJNDAEL_128,
'mode' => MCRYPT_MODE_NOFB,
),
Workarounds
§) If is possible delete all sessions data and try again.
i) For native adapter: Delete all (or just the corresponding to your app) sessions files located in...
Stores session data in the default location for your web server. The
storage location is defined by session.save_path in php.ini or defined
by ini_set.
ii) For cookie adapter: Manually delete the sessions cookies in the browsers affected or programmatically (in case of many users affected): (PHP) How to destroy the session cookie correctly?
iii) For database adapter: TRUNCATE TABLE sessions (delete all records of sessions table)

Categories