8

this will be a long one...

I've got a bad case of erratic login failure, due to wrong cookie management. First of all, I'm managing a closed store (B2B) in which customers have to login before they can see the catalog. Every unregistered access gets redirected to the login page, but once in a while the customer can't login even if username and password are correct. I say 'username' because I use Diglin_Username extension and StoreRestricition plugin to achieve the desired behavior. What happens is that sometimes I found two different sets of cookie left by Magento, and they refer to two different domains (.www.abc.com and .abc.com for instance).

After reading this article from the great Alan Storm on early session instantiation, and finding the dreaded PHPSESSID cookie in my browser I investigated in some depth the issue.

What I found is two-faced. First I put a Mage::Log() call in the function start() in Mage_Core_Model_Session_Abstract_Varien class to log the various attempt made by Magento to start a new session and noticed that following the first Mage::run() invocation the preDispatch(), dispatch() and postDispatch() methods of Mage_Core_Controller_Front_Action class get called in the usual sequence but it seems that when postDispatch() executes it can't find the session started by preDispatch() and proceeds to create a new session. To this regard I found a difference in the code between Magento 1.7.x and 1.8.x version and I think that could maybe take care of the issue:

Magento 1.7.x - Mage_Core_Model_Session_Abstract_Varien class:

public function start($sessionName=null)
{
    if (isset($_SESSION)) {
        return $this;
    }
    .
    .
}

Magento 1.8.x - Mage_Core_Model_Session_Abstract_Varien class:

public function start($sessionName=null)
{
    if (isset($_SESSION) && !$this->getSkipEmptySessionCheck()) {
        return $this;
    }
    .
    .
}

I just can't find where to set SkipEmptySessionCheck property though, so I ended up patching Mage_Core_Controller_Front_Action class in this way:

public function postDispatch()
{
    parent::postDispatch();
    if (!$this->getFlag('', self::FLAG_NO_START_SESSION )) {
        if (session_id()) {
            Mage::getSingleton('core/session')->setLastUrl(Mage::getUrl('*/*/*', array('_current'=>true)));
        }
    }
    return $this;
}

to have postDispatch() not calling Mage::getSingleton('core/session') (that would have created a new session) if it can't find a session already started. So long to the PHPSESSID cookie and all done, I thought...

But not so. Now I got rid of PHPSESSID cookie but still goto two different set of cookies (erratically) saved in the browser. Only deleting the wrong cookies I can successfully login, or I get redirected to the login page without even a message. I tried to state the cookie domain explicitly in the system config but this didn't resolve the issue.

Deep in the codebase again, and I found that in the various places when Magento sets a cookie it takes the domain to use from the function getDomain() in the Mage_Core_Model_Cookie class:

public function getDomain()
{
    $domain = $this->getConfigDomain();
    if (empty($domain)) {
        $domain = $this->_getRequest()->getHttpHost();
    }
    return $domain;
}

Now, if you look at the page you get from Magento in your browser you can find in the 'head' section something like this:

<script type="text/javascript">
//<![CDATA[
Mage.Cookies.path     = '/';
Mage.Cookies.domain   = '.www.abc.com';
//]]>
</script>

These lines come from app/design/frontend/base/default/template/page/js/cookie.phtml:

<script type="text/javascript">
//<![CDATA[
Mage.Cookies.path     = '<?php echo $this->getPath()?>';
Mage.Cookies.domain   = '<?php echo $this->getDomain()?>';
//]]>
</script>

and in turn this code references the getDomain() function in Mage_Page_Block_Js_Cookie class:

public function getDomain()
{
    $domain = $this->getCookie()->getDomain();
    if (!empty($domain[0]) && ($domain[0] !== '.')) {
        $domain = '.'.$domain;
    }
    return $domain;
}

So if I sets the cookie domain in system config as, for instance, 'www.abc.com' I end up with:

Mage.Cookies.domain   = '.www.abc.com'

and finding in my browser both 'www.abc.com' and '.www.abc.com' cookies I thought, "ok, I'll set '.abc.com' in the system config and will always end up with '.abc.com' cookies!!"...

But no way. Now in my HTML page I always get '.abc.com' but nonetheless I still erratically got a 'www.abc.com' cookie and no login.

I'm puzzled, and my customer is starting to think I'm not so good as he thought I was (I'm starting to think that, too...) :(

Do some of you guys (and gals) have some hint?

UPDATE: I've seen someone relating issues with sessions and cookies to the use of Varnish as a cache for Magento. As I'm using Varnish too I'll try if disabling it the issue can be solved.

  • Hi Marius, why the edit? Am I breaking some forum rule? – slamarca Jun 6 '14 at 10:35
  • we are seeing the same behaviour (login and customer looses session), except we can't reproduce the issue in any way! This really complicates any attempt at troubleshooting, let alone resolving the issue. How did you reliably replicate the issue? @Sander Mangel - That's just the thing, I haven't been able to reproduce the issue so I can't be sure what the different cookies look like. I would be a lot happier if I can reproduce it so I can verify any fix made to solve the issue. I was hoping if any of you could point me in the right direction on how to reproduce the issue. Thanks! – user27594 Jun 29 '15 at 13:15
  • @Zhulak same issue with www. and non www. cookies? – Sander Mangel Jun 29 '15 at 13:55
8

This is an article from NovusWeb: http://www.novusweb.com/fix-for-passing-magento-session-ids/

Fix for Passing Magento Session IDs

Author: Brett Williams

Posted November 9, 2011

Fixing Magento Session IDs

We often use shared SSL’s when building e-commerce sites. It’s a convenient way of hosting multiple stores without having to purchase separate SSL certificates for each site. Most of our e-commerce clients manage multiple stores within a single Magento or OpenCart installation. Recently, we found a problem with Magento where the customer’s session ID was not being passed successfully between their initial visit to the site and their page views after logging into the store as a registered customer. Magento was not passing the same session IDs, and this meant that a customer who had previously logged in and added items to their cart, would lose the contents of their cart after returning later and logging in. Not a great situation.

In looking at the cookies created during a session, I found that when going from an unsecure domain (i.e., http://) to a secure domain (i.e., https://), the session ID was being passed successfully and a new cookie for the secure domain was created with the same session ID as the unsecure domain. However, when the customer logged in, a new cookie was created for the secure domain with an entirely new session ID. Magento was now using the newer cookie, and whenever the customer clicked to go back into an unsecure domain page (e.g. product detail page), they were no longer logged into Magento as the unsecure domain was using its cookie/session ID, not the new session ID created at login. The solution would be to find where the new session ID was being created and prevent that from occurring.

So, I began digging into the code to see if I could find where Magento was creating the new session.

In app/code/core/Mage/Customer/Model/session.php, I found this at lines 177-189 (Magento CE 1.5.1):

public function login($username, $password)
{
/** @var $customer Mage_Customer_Model_Customer */
$customer = Mage::getModel('customer/customer')
->setWebsiteId(Mage::app()->getStore()->getWebsiteId());

if ($customer->authenticate($username, $password)) {
    $this->setCustomerAsLoggedIn($customer);
    $this->renewSession();
    return true;
}
return false;
}

My solution was to comment out the line: $this->renewSession():, so that Magento would not create a new session when the customer logged in. The changed code looks like this:

public function login($username, $password)
{
/** @var $customer Mage_Customer_Model_Customer */
$customer = Mage::getModel('customer/customer')
->setWebsiteId(Mage::app()->getStore()->getWebsiteId());

if ($customer->authenticate($username, $password)) {
    $this->setCustomerAsLoggedIn($customer);
    //$this->renewSession();
    return true;
}
return false;
}

So far in our testing, everything is working just fine, and the customer’s session is being retained between domains. Now, before you rush to change this core file, do the following:

Backup your databases (you should always do this before making any modifications). Build the following directory hierarchy: app/code/local/Mage/Customer/Model/. Put a copy of session.php into this new directory. Comment out the appropriate line, shown above, and save your file. By putting your modifications into the app/code/local directory, you’re telling Magento to use these files instead of the core files. More importantly, you’re preventing the loss of your modifications should you update Magento in the future.

It also provides a convenient way to store and manage your code modifications, as you only need to keep modified files in the app/code/local directory.

Be sure to leave a comment if you know of a more elegant solution, or if you find this works or doesn’t work for you.

  • 4
    For modifications stored in app/code/local/Mage/*. Prior to Magento upgrade, extract the code from the installer, compare against your modified code to see if it is different. If so modify the new version to be put in place after the upgrade. Nothing like having it preserved on upgrade to have the site fall over because of incompatible changes in the contents. – Fiasco Labs Aug 29 '14 at 14:08
  • 3
    Agreed. This article would only apply to pre 1.8 installs anyway since they moved $this->renewSession(); to the setCustomerAsLoggedIn() function. – seanbreeden Aug 29 '14 at 14:13
  • 1
    For more current Magento versions, just search for "renewSession()" and you'll find it in code/core/Mage/Core/Model/Session/Abstract.php and code/core/Mage/Admin/Model/Session.php where it can be commented out. In a local copy of the model, of course. @FiascoLabs even better, perform a proper override of just the function you need to modify, and leave the rest of the file intact in core :) – WackGet Aug 25 '15 at 0:54
  • 1
    this has helped us out after 3 weeks of trying to troubleshoot, 4 years later. The problem manifested for us (Magento 1.9.3.2) when we installed Amasty FPC and load tested our server. i.e. unable to log-in with facebook and/or normal login, unable to add to cart when server is under load. After which, even with no load the problem manifested. Now, it currently SEEMS like the problem is rectified after following your answer. Thanks so much @seanbreeden. You have breathed new life into very tired developers. <3 – ali Jan 29 at 15:18

protected by Community Jun 30 '15 at 20:10

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.