Skip to content

Enhancing Authentication: OpenID Connect Module for Backdrop CMS

Authentication is one of the cornerstones of modern web development—and getting it wrong can seriously hurt the user experience. Recently, our team was working on Single Sign On for the CMQCC website that was migrated from D7 to  Backdrop CMS last year.  CMQCC accounts server provides OpenID https://openid.net/ authentication, and this module was not ready for BackdropCMS. We had an option to either build a custom solution or port  the OpenID Connect module to Backdrop CMS(both D7 and D10 have versions of this module).  

With the help of coder_upgrader module and Cursor AI we were able to complete the port on a very slim budget and on schedule resulting in a reliable and user-friendly authentication tool for CMQCC and for the entire community.

The key success to this project was good use of Cursor AI.  One example where Cursor AI was instrumental (synonym of we cannot do it without it) - we added about 110 watchdog statements throughout the module.  Adding those manually would make porting time  20 times longer.

In this post, we’ll walk you through our journey and it’s milestones  

  • Debugging and logging transparency
  • Destination handling after login
  • User creation reliability
  • Session management stability

Let’s dive in.

Debugging That Actually Helps

The Problem

Tracking down authentication issues was frustrating due to limited logging.

The Fix

We went all-in on debugging and logging.    Now, every step in the authentication flow—user creation, token handling, session setup—is thoroughly logged. For example:

watchdog('openid_connect', 'User entity created, pre-save state: @account',
      array('@account' => print_r($account, TRUE)), WATCHDOG_DEBUG);

watchdog('openid_connect', 'Authorization parameters - Tokens: @tokens, Destination: @destination',
  array(
    '@tokens' => print_r($tokens, TRUE),
    '@destination' => print_r($destination, TRUE)
  ), WATCHDOG_DEBUG);
    

This level of visibility made it much easier to monitor authentication flow and drastically decreased the time that we spent finding the exact point where things break.

In total, we have added approximately 110 watchdog statements throughout the module. These cover various aspects of the authentication flow:

  • Session Management (~15 statements)
  • User Creation Process (~25 statements)
  • Authentication Flow (~20 statements)
  • Destination Handling (~10 statements)
  • State Token Management (~10 statements)
  • Account Connection (~15 statements)
  • Error Handling (~15 statements)

Making User Creation Reliable

The Problem

We ran into a quirky issue: sometimes user_save() would return value (1) instead of a full user object. This caused authentication to break silently.

The Fix

We added more robust logic to handle all possible return values. If user_save() returned an integer, we followed up with a database query to fetch the actual user object:

if ($save_result === 1) {
      $saved_account = db_select('users', 'u')
        ->fields('u', array('uid'))
        ->condition('name', $username)
        ->execute()
        ->fetchObject();
       
      if ($saved_account) {
        $saved_account = user_load($saved_account->uid);
      }
    }
    

This ensures the authentication process can always proceed with a fully loaded user object.

Rock-Solid Session Handling

The Problem

In rare cases, users would lose their session data mid-authentication, leading to incomplete logins or failed redirects.

The Fix

We made sure sessions were explicitly initialized and consistently populated:

   
    if (!backdrop_session_started()) {
            backdrop_session_start();
        }
    $_SESSION['uid'] = $account->uid;
    $_SESSION['roles'] = array_keys($account->roles);
    $_SESSION['name'] = $account->name;
    

Now, sessions are more stable and predictable—especially during complex login flows.

Redirects That Actually Make Sense

The Problem

Users were being redirected to the /user page after login—every time—regardless of the page they originally wanted to visit. Not great when you’re trying to access a specific node or admin page.

The Fix

We improved destination handling by ensuring that the authentication process remembers where users were headed in the first place. This meant properly formatting the destination array and parsing it effectively:

        if (isset($destination['destination'])) {
            $path = $destination['destination'];
            if (strpos($path, '?') !== FALSE) {
                $parts = backdrop_parse_url($path);
                $path = $parts['path'];
                $query = isset($parts['query']) ? backdrop_get_query_array($parts['query']) : array();
            }
            $destination = array(
                'path' => $path,
                'query' => $query,
            );
            }
    

Now, users are sent exactly where they need to go after logging in.

The Payoff

Thanks to these improvements, the OpenID Connect for BackdropCMS module is now:

  • More reliable
  • Easier to debug
  • Safer for user data
  • Smoother in user experience

Whether users are logging in for the first time or coming back after a session timeout, they’re now taken directly to their intended destination with fewer hiccups.

What’s Next?

We’re proud of the progress so far, but we’re already thinking ahead. Some ideas for future enhancements include:

  • Error recovery for failed logins
  • Two Factor Authentication  
  • Additional user mapping strategies
  • Additional security hardening

Dive Deeper

Want the technical nitty-gritty? Check out the supporting docs:

  • User creation with PKCE
  • Authentication without PKCE
  • PKCE vs non-PKCE comparison