Drupal Tips: Changing session lifetime for users

Written on March 30, 2022

Category: Drupal Tips

Author: David Rodríguez, @davidjguru

Picture from Unsplash, by @malvestida
Picture from Unsplash, user Malvestida, @malvestida

The Session time management is an important aspect in any type of application: it allows to determine under which processes the user login will be performed, how the user session will be managed and how the user logout will take place. In the case of Drupal, as in other technologies, it is essential to know the most important aspects of sessions and also, if the architecture is “decoupled”, we will have to take them into account to connect sessions opened from the frontend but managed in the backend. This only increases its complexity and importance.

That is why we are going to share some concepts and ideas. Unfortunately, as I don’t have all the necessary time and it would be a more extensive subject, I’m going to focus only on Drupal session management as a monolith. The “decoupled” variant is more extensive and complex and involves more intermediate tools, such as token management, SAML, etc. Perhaps for a more advanced article in www.therussianlullaby.com in the future. For now, just a sketch here.

Acknowledgments

This post was composed from my current position as Senior Drupal Developer at Digitalist Sweden, one of the biggest companies oriented to Open Source in Europe and specifically focused in Drupal.

Goals and basic concepts

A Drupal installation uses the built-in PHP session mechanism, which is based essentially on a cookie. The cookie name is SESS and is followed by a long string of characters, and its value is another long string of characters, as you can see in the next caption from a new Drupal local deploy based in DDEV tooling:

Cookie values for session in Drupal installation
Cookie values for sessions in a Drupal installation

PHP can inspect this cookie and makes it available to Drupal’s session implementation, using the database table sessions for storage, with the value of the cookie as the key session identification (sid). Then the session entry is also saving records using the current user ID. In the Drupal bootstrap sequence the session is loaded with the related user object, and is used to manage access to resources and other stuff, using the user’s roles and other specific attributes. Here are the mechanics.

The stored Data in the PHP global array $_SESSION will be transferred and saved to the current session’s database entry in the Drupal backend at the end of a single page request. Such a process can be observed to store important data such as items in a shopping cart and/or other related stuff.

By the way (and as it is very logical): You can work using other cookies that can be saved and retrieved by code. of course. In addition, it is also important to know that cookies with an expiry time of 0 are also often know as “session cookies”, cause these are deleted when you close your browser. This will be part of our goals in this case: to get session cookies.

For this case, we have only a couple of basic goals:

  1. Identify the time session management in backend (Drupal).
  2. Make sure the user is logged out when close tab / window / session.

For which we will first need to understand some basic concepts:

gc_maxlifetime
cookie_lifetime
gc_probability

What do they mean? Let’s see…

GC_MAXLIFETIME

This value is for the server: It is a setting for Session Garbage Collection. Initially If the user’s last visit happened before 200,000s then this session is eligible for garbage collection. Since it is GC, the session value may be discarded and not compulsory. If a GC action happens after the session was made eligible for the GC, it will be deleted.

GARBAGE COLLECTOR (GC values)

The PHP’s Garbage Collector is an internal system that allows PHP to keep the memory clean. You can enable or disable the associated functions using some parameters from code or from internal config files. How it works? Well, we already know that a variable in PHP is stored in a container called “zval” (Zend Value), from C language. This is a data structure implemented in C and it represents any PHP value (number, string, array, etc.). Here we’re storing also the type of variable, its value and some pieces of info:

  • If the variable is part of the reference set.
  • If the variable is referenced from another places.

So, periodically, some algorithms are reviewing the variables and resources, deleting the unused registers. For Garbage Collection, we have some params available in php.ini file: php.net/session.configuration.php

In general terms, we’re configuring this in the php.ini file:

session.gc_maxlifetime = 3600
session.gc_probability = 1
session.gc_divisor = 1000

But in the Drupal context, we can change these values from a Drupal installation, inside the file default.services.yml in web/sites/default/ or in core.services.yml in web/core/:

parameters:
  session.storage.options:
    gc_probability: 1
    gc_divisor: 100
    gc_maxlifetime: 200000
    cookie_lifetime: 2000000

As you can see expanded in the original block: from default.services.yml:

session.storage.options:
    # Default ini options for sessions.
    #
    # Some distributions of Linux (most notably Debian) ship their PHP
    # installations with garbage collection (gc) disabled. Since Drupal depends
    # on PHP's garbage collection for clearing sessions, ensure that garbage
    # collection occurs by using the most common settings.
    # @default 1
    gc_probability: 1
    # @default 100
    gc_divisor: 100

The gc_probability and gc_divisor are two interrelated values that defines the probability that the gc (garbage collection) process is started on every session initialization. The probability is calculated doing: gc_probability/gc_divisor (1/100 for us) and it means there is a 1% chance that the GC process starts on each request.

This value is for the browser: This is the absolute maximum time till which a browser can keep this cookie active. A 0 value here will means immediate or when the browser will be closed.

Cookie values for creation and expiration dates
Cookie values for creation and expiration dates

By default is set with 2,000,000 seconds, equals to almost 24 days. See the previous caption with creation and expiration date, so for a cookie created on March 27th, we can see an expiration date of April 19th, which is just a 23-day life cycle, the marked 2,000,000 seconds. You can think about change this data in service.yml files, but It’s true that these kind of files is out of control for Git so your changes will not be shared with your team, and it won’t be recreated in common environments (stage, preprod, prod, live, etc). You’ll need a more “structural” and persistent solution, so we will need to create a new custom module in next steps.

The two main values related to sessions are coming from a static way in a services.yml file, generated in a Drupal installation from Composer. When I launch the build of a new Drupal site, I’m getting a set of values marked by-default. This file is setting the session lifetime with 200,000 seconds => 55.55 hours => 2.31 days. And the cookie lifetime by default with 2,000,000 seconds => 555.55 hours => 23,15 days.

They are the values setted in the default.services.yml file as you can see for gc_lifetime or cookie_lifetime values in your Drupal installation:

    # Set session lifetime (in seconds), i.e. the grace period for session
    # data. Sessions are deleted by the session garbage collector after one
    # session lifetime has elapsed since the user's last visit. When a session
    # is deleted, authenticated users are logged out, and the contents of the
    # user's session is discarded.
    # @default 200000
    gc_maxlifetime: 200000
    #
    # Set session cookie lifetime (in seconds), i.e. the time from the session
    # is created to the cookie expires, i.e. when the browser is expected to
    # discard the cookie. The value 0 means "until the browser is closed".
    # @default 2000000
    cookie_lifetime: 2000000

As we have already said, these values are in the default configuration of the Drupal installation. Besides this is part of the Container in Drupal, so can only be overwriting from a custom Drupal module using a pair of classes. We need something like:

gc_maxlifetime: 3600 (only one hour of lifetime for the session).
cookie_lifetime: 0 (deleted when user close the browser tab).

Overwriting default values by switching service class

Well, it seems that the default configuration used by the service responsible for managing sessions does not work for me and I need modifications from the container… What can I do?

The bad news is that the above values are statically part of the service container after a Drupal installation, and cannot be modified “dynamically”. And these previous values are in the by-default configuration of the Drupal installation.

The good news is that we can add others values by switching with another class to replace the service provision.This is part of the Container in Drupal, so it can only be changed from a custom Drupal module using a pair of classes. We can do something like add the next values:

  • gc_maxlifetime: 3600 (only one hour of lifetime for the session, and then make the session available for the garbage collector).
  • cookie_lifetime: 0 (deleted when user closes the browser tab).

And here we can “decorating” existing service for overriding an existing service, as in Symfony (see “Read More” section with links) or we can provide another Service class and do the switch. To modify existing services, we have to implement a class extending ServiceProviderBase, a new Service class and the alter() method. In short, all we need is only change the class responsible for the sessions management, used in service session_configuration as described in core.services.yml:

 session_configuration:
    class: Drupal\Core\Session\SessionConfiguration
    arguments: ['%session.storage.options%']

From a ServiceProvider class, by linking the service to our new custom class:

public function alter(ContainerBuilder $container) {
    $definition = $container->getDefinition('session_configuration');
    $definition->setClass('Drupal\custom_session_manager\StSessionConfiguration');
  }

Here you can see and download the custom module implemented as example and uploaded to my Gitlab repository. Check out the code and test the feature! (not for production - live environments, please). .

So now:

// Allows the cookie to be destroyed when closing browser.
$options['cookie_lifetime'] = 0;            
// Allows the session to be aligned with the frontend session.     
$options['gc_maxlifetime'] = 3600; 

And just enabling this new custom module, you will have a key change here: now the max-age of the session cookie will be set for expiring by session, see:

Max-Age for cookie set to Session
Max-Age for cookie set to Session

Now when you close your browser, the session cookie will be deleted, the user logout will be executed and on the next URL load, the user session will still be closed.

Session data & Garbage Collector & Cron

You can load the session time with the values you want. But this only let available the session data for the Garbage Collector of Drupal/PHP. It means that from 1 hour the session can be opened until the Garbage Collector was executed. Who’s executing the Garbage Collector? essentially, the cron in system. The “Automated Cron” core module can be enabled and set to 1 hours. It means that really, the inactive sessions (more than 1 hour without activity) will be deleted every 1 hour now, just when cron runs and launches the Garbage Collector.

But perhaps, we need a little more “granularity.” that is, to launch the cron only for these specific tasks, because launching it completely and for everything very frequently may not be the good idea we were thinking of. We need to be able to filter tasks associated with the cron in a more atomic way. For this we can rely on certain Drupal contributed modules that can provide us with functionality.

Useful Modules

Useful Drupal contrib modules we can use for more control over the session timing, roles and garbage collector from cron executions:

  1. Session Limit: drupal.org/session_limit. You can limit the number of sessions available for a role / user in different environments.

  2. Automated Logout: drupal.org/autologout. You can configure sessions settings values in order to force the logout in users.

  3. Ultimate Cron: drupal.org/ultimate_cron. It Allows refinement in cron timing and execution’s tasks.

Futhermore, we can create some specific cron-tab tasks on the server by prompt, if we need more control over the session data. Cron is a time-based job scheduling daemon in Linux Operating Systems. You can learn more about how to configure tasks in background using cron in the next section.

Read More

:wq!


Written on March 30, 2022