I'm having a big problem with Drupal 8, I want Drupal to trust a downstream reverse proxy that performs authentication against a third party provider, then passes a JWT in a X_ID_TOKEN header.

As I didn't find any module that behaves as an authentication provider that trusts HTTP headers to authenticate the user, I developed a provider myself which is implemented as follows:

public function applies(Request $request) {
  // If you return TRUE and the method Authentication logic fails,
  // you will get out from Drupal navigation if you are logged in.
  $trusted_host = "";
  $headers = $request->server->getHeaders();
  $server_addr = $request->server->get('SERVER_ADDR');
  return ($server_addr == $trusted_host) && $headers && $headers['X_ID_TOKEN'];

public function authenticate(Request $request) {
  $headers = $request->server->getHeaders();
  $jwt_token = explode('.', $headers['X_ID_TOKEN']);
  $user_data = json_decode(base64_decode($jwt_token[1]), true);
  if(!$user_data && !$user_data['email']){
    return NULL;
  $users = \Drupal::entityTypeManager()->getStorage('user')
    ->loadByProperties(['mail' => $user_data['email']]);
  $user = reset($users);
  if ($user) {
    $uid = $user->id();
    $rids = $user->getRoles();
    $account = User::load($uid);
    return $account;
    /** @var \Drupal\user\Entity\User $account */
    $account = User::create([
      'name' => $this->generate_username($user_data),
      'pass' => user_password(),
      'mail' => $user_data['email'],
      'init' => $user_data['email'],
      'status' => 1,
    // Store the newly created account.
    $this->save_user_info($account, $user_data);
    return $account;
  return NULL;

Then the service is registered like this:

    class: Drupal\trust_openid_auth\Authentication\Provider\OpenIDAuth
      - { name: authentication_provider, provider_id: open_idauth, priority: 0, global: TRUE }

This sort of works, but the pages we are rendering contain user personal data, and they are being cached by some Drupal internal cache. This cache is "per user" thanks to the native Drupal session cookie.

Problem: this cookie seems to be randomly set (when it's not there it randomly appears when navigating). When the session cookie is not set, the cache is global and results in rendering a page that contains the personal information of the last user that successfully rendered the page (not necessarily the current user!)

I have read that the session cookie starts existing when data is attached to the PHP session, which I'm not in control of.

I'm pretty lost at this point, the two solution I thought of are:

  • Tell Drupal to trust a remote Cookie (set by the downstream reverse proxy) as the session Cookie, this cookie would contain the user's JWT
  • Force the Drupal session Cookie even if there is no data attached to the session, just to contextualize the cache

The first solution would be ideal but I'm not sure how to achieve it. Any help on this would be welcome!


2 Answers 2


If you tag this as middleware and weight it in your service definition, you can control when this runs. If you search through core you will find the Session and Page Cache also run as middleware. The Ban module in core is also tagged as middleware.

Hopefully this will help point you in the right direction.


For some reason the Internal Page Cache module, which is supposed to "cache pages for anonymous users" kicks in before the authentication provider. I don't know why this module considers that a user is anonymous even before the authentication provider had a chance to do anything. Anyway, my solution was to disable this module.

  • The Internal Page Cache only checks for PHP session cookies, it can't handle other cookies or request headers. In general this is a normal issue when accelerating anonymous traffic. It's unlikely that a 3rd party reverse proxy or CDN would handle your traffic out of the box, you probably need make some adjustments. At least most 3rd party solutions are configurable in contrast to the single use case Internal Page Cache.
    – 4uk4
    Feb 22, 2021 at 8:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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