How to bypass Drupal Page Cache for auth cookie

How we keep the cache strategy unmodified and at the same time allow certain scenarios that are not going to be handled by the page cache system.

The single sign-on example

The website has a single sign-on feature, which reads a cookie from another web service on the same domain and performs a logic check for a valid cookie and which user should be logged-in with that cookie. With Drupal cache enabled, we face a challenge to achieve this because it has a mechanism called “anonymous page cache” (also known as page_cache) that allows the server to deliver fast responses by skipping business logic that has been computed before and just serve the version that is stored in the cache.
With the page cache enabled the code that reads and checks the Single Sign-on cookie never gets executed.

The question is: how do we keep the cache strategy unmodified and at the same time allow certain scenarios that are not going to be handled by the page cache system?.

There are rules (also called policies) that we can define to avoid this cache mechanism in certain situations and just execute our custom logic when those happen. By default, Drupal skips the cache when it detects a session or that it is being executed in a different way than a normal brower request. For example, accessing Drupal using the command line. See DefaultRequestPolicy for a code example.

Show me the code

In the following example we will define a new rule that checks if the cookie SSO_OAUTH is set for the current request. When the conditions of the rule are met, the response from Drupal will not be cached. We will create three files inside a custom module that needs to be enabled.

First, we create a class for the policy at /mymodule/src/PageCache/RequestPolicy/SessionCookieRequestPolicy.php


<?php
namespace Drupal\mymodule\PageCache\RequestPolicy;
use Drupal\Core\PageCache\RequestPolicyInterface;
use Symfony\Component\HttpFoundation\Request;
/**
 * Reject caching when the user has the sso cookie.
 */
class SessionCookieRequestPolicy implements RequestPolicyInterface {
  /**
   * {@inheritdoc}
   */
  public function check(Request $request) {
    if ($request->cookies->has('SSO_OAUTH')) {
      return static::DENY;
    }
  }
}

Then we add this policy to the other policies at /mymodule/src/PageCache/DefaultRequestPolicy.php


<?php
namespace Drupal\mymodule\PageCache;
use Drupal\mymodule\PageCache\RequestPolicy\SessionCookieRequestPolicy;
use Drupal\Core\Session\SessionConfigurationInterface;
use Drupal\Core\PageCache\DefaultRequestPolicy as PageCacheRequestPolicy;
/**
 * Overrides the default page cache policy service.
 *
 * Add a custom rule for session cookies.
 * Disallows serving responses from page cache for requests with a
 * session cookie.
 */
class DefaultRequestPolicy extends PageCacheRequestPolicy {
  /**
   * Constructs the default page cache request policy.
   */
  public function __construct(SessionConfigurationInterface $session_configuration) {
    parent::__construct($session_configuration);
    $this->addPolicy(new SessionCookieRequestPolicy());
  }
}

There is still one more step because the page_cache module is not using our policy yet so we have to overwrite the default policy for the custom one just by changing the class for the service at /mymodule/src/MymoduleServiceProvider.php (namespace and filename must follow this pattern in order to be detected).


<?php
namespace Drupal\mymodule;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
/**
 * Modifies the page_cache_request_policy service.
 */
class MymoduleServiceProvider extends ServiceProviderBase {
  /**
   * {@inheritdoc}
   */
  public function alter(ContainerBuilder $container) {
    $definition = $container->getDefinition('page_cache_request_policy');
    $definition->setClass('Drupal\mymodule\PageCache\DefaultRequestPolicy');
  }
}

We should stress that the PHP code we add for the policy should be as simple as possible because this is going to run for every request on the site. Checking for a cookie is simple enough to be acceptable for this case.

This demonstrates how Drupal is flexible enough to add new business logic with a few lines of code while keeping the site as performant as usual with all caches enabled.

All posts by Rodrigo

Our most read posts

10 questions for Tom Hare

|
Today, Dev-Strategist Tom Hare is going to answer 10 questions about his work and life at the netzstrategen. 1. When did you become part of the team? I joined the netzstrategen team in May 2017, shortly after moving to Karlsruhe …

10 questions for Luca Pipolo

|
Today Dev-Strategist Luca Pipolo is going to answer 10 Questions about his work and life at the netzstrategen company. 1. Since when have you been part of the team? More or less since March of this year, when I left …

10 questions for Julià Mestieri

|
Today, Dev-Strategist Julià Mestieri is going to answer 10 questions about his work and life at the netzstrategen. 1. Since when have you been part of the team? I’ve met the netzstrategen about a year ago and we stayed in …