Convio Drupal : Single Sign-On

Convio Drupal > Single Sign-On

Overview

Convio Drupal implements single sign-on (SSO) such that a user who signs in to Drupal is automatically signed in to COM and vice versa.

Requirements

  1. Convio is the master database of record.
    This implies that all user records are maintained in C360 -- a user account in Drupal only acts as a proxy for the C360 user record.

  2. A user who is logged in to C360 is also logged in to Drupal.
    Users may log in to C360 from both the COM website and the Drupal website. Both the COM website and the Drupal website should reflect changes to the C360 user context (e.g., a user logs out or a new user logs in).

  3. User permissions are maintained in both C360 and Drupal.
    C360 permissions are assigned through COM's Security Categories interface. CMS-specific permissions are assigned through Drupal's User Permissions interface.

  4. For security reasons, the Convio super user account is never proxied in Drupal.

Design

The Drupal SSO implementation consists of two major parts:

  1. A Login Form for authenticating directly into C360
  2. Synching the user context with the C360 session

1. Login Form

Submitting the sign-in form results in a login API call to COM. If the authentication credentials are valid, then COM will establish the C360 session and redirect back to Drupal to establish the corresponding user session. If the credentials are not valid, then COM will redirect back to the Drupal user login page with friendly error messages.

The convio module hijacks the built-in sign-in form so that it authenticates into C360. (Yes, this is standard practice in Drupal 6.)

//modules/convio/convio.module

function convio_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'user_login_block' || $form_id == 'user_login') {
    ConvioLoginHandler::getInstance()->getLoginForm($form, $form_state, $form_id);
  }
  return $form;
}

The following flowchart explains how the Drupal login form authenticates in C360.

login.jpg

Theme Example: Handling authentication errors

By modifying some of the template engine pre-processors, it's possible to enhance the C360 login form to show authentication errors returned by the SSO APIs.

//template.php

/**
 * Implementation of HOOK_theme().
 */
function lucha_theme(&$existing, $type, $theme, $path) {
  $hooks = zen_theme($existing, $type, $theme, $path);

  // Separate template for the user login page.
  $hooks['user_login'] = array('template' => 'user-login',
                               'arguments' => array('form' => NULL),
                               );

  return $hooks;
}

function lucha_preprocess_user_login(&$variables) {
  $variables['rendered'] = drupal_render($variables['form']);

  // Login error messages.
  $code = $_REQUEST["code"];
  $message = $_REQUEST["message"];
  $has_errors = FALSE;
  if (! empty($code) && ! empty($message)) {
    $has_errors = TRUE;
    $variables['error_msg'] = $message;
    $variables['error_code'] = $code;
  }
  $variables['has_errors'] = $has_errors;
}

2. Synching the user context

At the start of each request, if Drupal has no knowledge of the C360 user context, it establishes a new user context by via a GET to the loginTest API call. The new user context -- either anonymous or authenticated -- is stored in the Drupal session (/convio/session/validate). If Drupal knows about the C360 user context, then the requested page is served.

Note: This part of the SSO implementation also contains the Source Code Tracking implementation.

In addition, on each request, Drupal asynchronously validates its user session against the C360 user session via a cross-domain POST to the loginTest API call. If the user contexts are in sync, then no further action is taken and the requested page is served. If the user contexts are out of sync, then the asynchronous callback clears the Drupal user session (/convio/session/clear) and re-establishes a new user context (see "2. Establishing a new user context").

The following flowchart explains how the SSO session-synch mechanism ties in to each page request.

sso.jpg

The Convio SSO implementation involves including C360 library dependencies and tracking "beacons". This is all accomplished in the module code, so there is no need to modify the theme. The includes are:

  • C360 Open API JS client library (served from COM)
  • C360 Open API JS client init script
  • Session-status tracking beacon
  • Session keep-alive beacon

Note that the theme must contain a header and footer region in order for the dependencies to be included automatically.

//modules/convio.module.php

/**
 * Adds standard includes to every page.
 *
 * 1) Open API JS lib and init script -> <head>
 * 2) Session tracking beacon -> header region
 * 3) Keep-alive beacon -> footer region
 */
function _convio_add_includes() {
  static $included = FALSE;
  if (!$included) {

    // Establish a new C360 session if one doesn't exist already.
    if (ConvioLoginHandler::getInstance()->newUserSession()) {
      return;
    }

    $api_config = ConvioConfiguration::getInstance();
    $api_uri = $api_config->getURI();
    $api_key = $api_config->getPublicKey();

    // TODO: allow the regions to be overriden in the module configuration.
    $region_header = "header";
    $region_footer = "footer";

    // Add Convio Open API JS library and init script.
    drupal_set_html_head('<script type="text/javascript" src="' . $api_uri . '/../api/ConvioApi.js"></script>');

    $script = '<script type="text/javascript">';
    $script .= '  var CONVIO = {};';
    $script .= '  CONVIO.OPEN = new ConvioApiClient("' . $api_key . '","/api_client.html","json");';
    $script .= '</script>';
    drupal_set_html_head($script);

    // Add the session tracking beacon to the body of every page.
    $trackerUrl = url('convio/session/status', array('query' => drupal_get_destination()));
    $tracker = '<script type="text/javascript" src="' . $trackerUrl . '"></script>';
    drupal_set_content($region_header, $tracker);

    // Add a keepalive beacon to the bottom of every page.
    $img = '<img src="' . $api_uri . '/PixelServer" />';
    drupal_set_content($region_footer, $img);
  }
  $included = TRUE;
}

// Include Convio module JS libraries on every page.
_convio_add_includes();

Caveats

There are several caveats to be aware of when integrating Drupal with C360:

  • The SSO implementation is not designed to be fault tolerant should COM become unavailable or unresponsive. Authentication and even anonymous page views will hang because of the timed-out SSO redirects.

  • When re-establishing the Drupal and C360 user contexts, it is possible to lose POST data to a Drupal form.

  • Drupal user accounts act as a proxy to the C360 user record. The accounts are linked by username. C360 user records and Drupal user accounts that share the same username are considered to be the same user.

  • Because C360 is the database of record, pre-existing Drupal user account email addresses will be overwritten.


-- Michael Pih (mpih@convio.com) - 20 July 2010
Copyright (C) 2010 Convio, Inc. All rights reserved. This program is free open source software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU General Public License is available at http://www.gnu.org/licenses/.