Correct Way To Detect Customer Login On Magento 2 Frontend With Full Page Cache

Sometimes we need to "detect"(check) the customer's login status and do something. For instance, display a coupon code to logged in customers only. Things become complicated when Full Page Cache(incl. Varnish, Fastly and other CDNs) is employed. In these cases, it is required to "detect" the login status on frontend. This Magento 2 customization tutorial will demonstrate the correct ways in different situations.

You may notice the word detect is double quoted, the reason will be explained in later section.

Prerequisites

Prior knowledge required to understand this blog post:

  • Be familiar with knockout.js and require.js

In customer and checkout controllers

These are special cases since pages in the mentioned controllers are not cacheable and Full Page Cache service will be bypassed. There is no need to use frontend detection. You can just do a backend detection and output the HTML in PHP templates.

Example

/** @var \Magento\Customer\Model\Session $customerSession */
if ($customerSession->isLoggedIn()) {
    // ...do something...
}

In Full Page Cache activated controllers

For example, in a product page.

The well-known Http Context way

Again, actually it is a backend detection.

Example

/** @var \Magento\Framework\App\Http\Context $httpContext */
$customerLoggedIn = (bool)$httpContext->getValue(\Magento\Customer\Model\Context::CONTEXT_AUTH);
if ($customerLoggedIn) {
    // ...do something...
}

Http Context way Limitation

I personally do not recommend the Http Context way. This way requires using Varnish and Fastly with correct configuration. If you are using other CDN services such as Cloudflare Free Plan, it won't work(Cloudflare simply doesn't support).

(I also saw some stores cannot configure Fastly correctly. Finally they gave up and deactivated the caching service, which resulted in a huge performance lose and vulnerable to DDoS attack.)

BTW, if you are interested in Cloudflare Free Plan + Magento 2. Feel free to contact us. We have existing customization that can let Magento work with Cloudflare Free Plan. Although it is not a 100% replacement of Fastly, you can enjoy switching to a Free CDN.

The frontend javascript way(Core part of this tutorial)

The universal way. In other words, it works everywhere regardless of Full Page Cache service. So I recommend this way.

Why the word detect is double quoted in the introduction???

The short answer is because you CAN'T detect it, synchronously.
???What are you talking about???

Okay, let's start with some incorrect examples first. You must know why those methods are incorrect.

Incorrect Example 1

Use isCustomerLoggedIn global variable.

if (window.isCustomerLoggedIn) {
    // ...do something...
}

The most wrong way. window.isCustomerLoggedIn is only available in checkout controller actions. You cannot use it in other pages such as a product page.

Incorrect Example 2

Use Magento_Customer/js/model/customer dependency.

require([
    'Magento_Customer/js/model/customer' // model, be careful
], function (customer) {
    if (customer.isLoggedIn()) {
        // ...do something...
    }
});

Check this line. The customer.isLoggedIn is just a knockout observable of window.isCustomerLoggedIn.
So it is the same as the above example.

Incorrect Example 3

Use Magento_Customer/js/view/customer dependency.
If the customer data has fullname, then the customer is already logged in.

require([
    'Magento_Customer/js/view/customer' // view, be careful
], function (customer) {
    if (customer.customer() && customer.customer().fullname) {
        // ...do something...
    }
});

This time is much better and the developer chose the Magento_Customer/js/view/customer dependency which is a correct choice.
However, it still cannot correctly detect the login status. To be more specific, if the customer is actually logged in, this script will detect as not logged in.

A good hint though

The Magento_Customer/js/view/customer dependency is a good hint, let's looking into it.
The customer property is set as customerData.get('customer'). Okay, let's check Magento_Customer/js/customer-data then.
As you can see in Line 143 and Line 134, The customer property is also an knockout observable.

Incorrect Example 4

Listen to the customer-data-reload event. This event is triggered here.

require([
    'jquery'
], function ($) {
    $(document).on('customer-data-reload', function (event, sectionNames) {
        // ...do something...
    });
});

Did you notice the line just below the line that triggers the event?

buffer.update(sections); // sections contain customer data

So when the event is triggered, customer data has not been updated.
This event only tells you a reload from server happened.

So what's the real problem again?

You can check other code in Magento_Customer/js/customer-data, the customer data is retrieved asynchronously. So when the customer.customer() is evaluated, it may not contain customer data yet(i.e., the Ajax request is not finished).

Since the login status is represented by a knockout observable, the correct thing is to don't check or compute it synchronously, but to tell the the customer data management system(Magento_Customer/js/customer-data) to do something after successfully retrieving the customer data, ASYNCHRONOUSLY.
This should answer the question Why the word detect is double quoted and Why you can't detect.

If you read this tutorial carefully, you should notice the common mistaken approach in those 4 Incorrect Examples is that the developer is trying to check the login status synchronously.

Now, it's time to reveal correct examples.

Correct Example 1

To make things simpler, we just use Magento_Customer/js/customer-data dependency, and subscribe the observable.

// You can also use 'Magento_Customer/js/view/customer' dependency
require([
    'Magento_Customer/js/customer-data'
], function (customerData) {
    customerData.get('customer').subscribe(function (customer) {
        if (customer && customer.fullname) {
            // ...do something...
        }
    });
});

Correct Example 2

This one may have some practical use. You can make small modifications to adapt the below code to .phtml or .html templates.

<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Ui/js/core/app": {
                "components": {
                    "customer": {
                        "component": "Magento_Customer/js/view/customer"
                    }
                }
            }
        }
    }
</script>
<!-- ko scope: 'customer' -->
    <!-- ko if: customer().fullname  -->
    <span data-bind="visible: customer().fullname"
        style="display: none;"
    >
        <!-- ko i18n: 'Hello, ' --><!-- /ko -->
        <span data-bind="text: customer().fullname"></span>
    </span>
    <!-- /ko -->

    <!-- ko ifnot: customer().fullname  -->
    <span data-bind="
        visible: !customer().fullname,
        i18n: 'Please login!'"
        style="display: none;"
    >
    </span>
    <!-- /ko -->
<!-- /ko -->
Correct Example 2 Output
  • When logged in
    Hello, {{Firstname}} {{Lastname}}
  • When not logged in
    Please login!

Final Thoughts

  • When the development involves logic with customer login status and/or other customer data, you can just focus on Magento_Customer/js/customer-data dependency and subscribe those sections' knockout observable.
  • The Magento_Customer/js/customer-data sends an uncacheable Ajax request to the server to retrieve customer data, that is why it always works in CDN employed environments.

(Incorrect examples in this blog post were collected from other development teams.)

In the next blog, we are going to talk about how to force update mini cart programmatically on frontend.

If this blog post/tutorial did help you, please share it! If you have a better idea/solution regarding this topic, please share it with us, too!