Mozilla's Session API Tutorial


Warning: This post is no longer current, for the most up to date information, see MDN or the Identity Wiki.

With the introduction of BrowserID, Mozilla is beginning a big push towards securing and simplifying the management of the user's identity.

The Problem

Up to this point, browsers have played a very small role in helping users sign in, sign out, and see their session information for the current site. We think that by providing a single place within the browser to perform these functions, we can offer a streamlined user experience.

To do this, we need a little help from sites themselves.

The Solution - Introducing the Sessions API!

By using the Sessions API, sites indicate their willingness to work with the browser to help manage and display session information.

High Level Flow

We want the user to have a single location with which to do all of their session management. Currently, we are using a small portion of the URL bar next to the favicon as the focal point for this process. Once a site has indicated that it supports the Sessions API, a "Sign In" button will be displayed. If the user clicks on this button, the user will be logged in via the site's authentication facilities. Once the user is logged in, a button with their current session information will replace the "Sign In" button. The user can click on their username to see more session information as well as to sign out. Pressing the "Sign Out" button will trigger the site's sign out mechanism. The session information will be reset and display the "Sign In" button again.

The API

Below is a walk through of how the Session API can be used for a static site.

Indicating Support

First off, the site has to indicate that it supports the Session API. Supporting the Session API means that it both sets sessions information and responds to the login and logout events.

/**
* Indicate support for the Sessions API by setting 
* navigator.id.sessions to an empty array.  Browser will
* display a "Sign-In" button in a conspicuous place.
*/
if(navigator.id) {
  navigator.id.sessions = [];
}

Setting navigator.id.sessions to an empty array indicates support. Notice that all code examples are wrapped in the "if(navigator.id) { /* code here */ }" block. This is so that we do not cause errors on browsers that do not yet support the API.

When a site indicates that it supports the Sessions API, a button will be displayed in the URL bar.

"Sign In" in the URL Bar

Login event

If the user presses the "Sign In" button, a (drum roll) login DOM event on the page's document is triggered. To handle this, a login event handler must be registered on the window's document. When login is triggered, the site should take appropriate action such as redirect to a login page or call BrowserID.

if(navigator.id) {
/**
* Listen for the login event.  Take appropriate action to log the 
* user in once the event is triggered.
*/
  ...

  // `login` is triggered whenever the user clicks "Sign In" 
  // from within their browser.
  document.addEventListener("login", function(event) {
    // Redirect to login page, call BrowserID, etc.
    document.location.href = ...;
  }, false);
}

Setting a Session

Once the user is logged in, the site should set the session information to be displayed in the browser. Session information should be set as soon as possible when the user lands at the page following the login page, preferably before the load event is triggered.

If session information is set, the user's current login name will be displayed in the URL bar providing a visual link between the site and the user's identity.

"stomlinson@mozilla.com" shown logged in in URL Bar

/**
* Indicate the user is logged in, not bound to any cookie.
* navigator.id.sessions will have to be updated on every page load.
* Browser will show the user's current login name where the 
* "Sign-In" button was. 
*/
if(navigator.id) {
  navigator.id.sessions = [{
      email: "stomlinson@mozilla.com"
  }];
}

Using the above method of setting the sessions, the session information MUST be set on every subsequent page load or else the previous page's session information will be removed on the new page's load event. While session information can be set at any time, it is recommended to set it before the window load event to avoid flicker.

Using Sessions with Bindings

To avoid setting session information on every page, all you have to do is bind the session information to a cookie. If a session is bound to a cookie, as long as no new session information is set and the cookie value remains the same, the session will be considered active.

An important caveat to the above is that the cookie information bound to an exact match domain name. This means that if the original session information was set at http://foo.com, but the user is now at http://mail.foo.com, the cookie binding will be ignored and the session information must be set again.

/**
* Indicate the user is logged in, bind session to a cookie.
* Session will remain valid until the value of the cookie
* changes, navigator.id.sessions is updated, or user 
* changes domains.
*/
if(navigator.id) {
  navigator.id.sessions = [{
    email: "stomlinson@mozilla.com",
    bound_to: {
      type: "cookie",
      cookie_name: "SID"
    }
  }];
}

Logout event

If there is session information set, the site should then listen for the logout event. The logout event is triggered on the document whenever the user selects the "Sign Out" button from within the browser.

Logout dialog from URL Bar

if(navigator.id) {
...

/**
* `logout` event is triggered whenever the user selects the 
* Sign Out button from within their browser.
*/
  document.addEventListener("logout", function(event) {
    // redirect to logout page
    document.location.href = ...;
  }, false);
}

Resetting the Session Information

Once the user has logged out, the sessions information should be reset so that the user's previous session is no longer displayed in the browser.

/**
* Clear the session information, display "Sign-In" button.
*/
if(navigator.id) {
  navigator.id.sessions = [];
}
/**
* Putting it all together.
*/
if(navigator.id) {
  // indicate support
  navigator.id.sessions = [];

  // user logged in, no cookies
  navigator.id.sessions = [{ email: "stomlinson@mozilla.com" }];

  // user logged in, bound to cookie named SID.
  navigator.id.sessions = [{
    email: "stomlinson@mozilla.com",
    bound_to: {
      type: "cookie",
      cookie_name: "SID"
    }
  }];


  document.addEventListener("login", function(event) {
    // redirect to login page
    document.location.href = ...;

    // ... or ...

    // call BrowserID!
    navigator.id.getVerifiedEmail(function(assertion) {
      // send to server to verify, get email address
    });
  }, false);

  document.addEventListener("logout", function(event) {
    // redirect to logout page
    document.location.href = ...;
  }, false);
}

Plugin

We have a plugin! I have been hard at work getting a Sessions API plugin ready for Firefox and an experimental version is ready! The plugin supports Firefox 4+ through to the current Aurora. The screen shots above were all taken with Nightly. A pre-built XPI can be found on GitHub which can be installed directly.

Bugs (Yes, they do exist)

The most important bug right now is when first opening Firefox with multiple instances, only the last instance opened will have Sessions support. This will be addressed very soon.

Secondly, there are some visual inconsistencies with the Windows version. I did not realize until too late that the styling of the favicon button was different in Windows than OSX.

Seeing The API and Addon in Action

You can see this in action either on this site (registration required) or on http://myfavoritebeer.org. myfavoritebeer.org has the added bonus of being integrated with BrowserID!

Future Directions

Shortly in the future we want to support the ability to switch between multiple sessions - ala Google. We are also looking at having the notion of "passive" and "active" sessions, but this has not yet been completely defined. The addon is going to integrate directly with BrowserID providing the user with a seamless login experience. Session and identity management are working their way into the browser more and more, and we think this is a good thing from both ease of use and security perspectives. Soon, instead of logging into individual sites, you are going to be logging into your browser which will then take care of 90% of the rest of the work.

Conclusion

So things are pretty awesome, slowly we are bringing the user's identity into the browser and providing a single place for the user to go for all of their session management tools. We are providing a mechanism for the user to instantly know who they are currently signed in as. We are working towards providing support for multiple sessions and being able to easily switch between two logins. We integrate well with BrowserID.

Sure there are some rough spots, this is still an experimental product. That is where you come in. We need feedback. We need people to install the addon and tell us what they think. We need sites to implement the API and tell us where their pain points are and what can be changed, improved, added and removed. If you are so inclined, you can even fork the repo and submit pull requests. I have rarely been submitted a pull request that I didn't like (who is going to turn down free work?). This should be community driven.

Interested?

Get the addon from GitHub or AMO.
Reach me directly at stomlinson@mozilla.com.
Subscribe to the identity mailing list.
Join us on IRC at #identity @irc.mozilla.org.