Integrate BrowserID in a Tornado web app

22 November 2011   2 comments   Tornado, Mozilla

Mind That Age!

This blog post is 6 years old! Most likely, its content is outdated. Especially if it's technical.

Powered by Fusion×

Integrate BrowserID in a Tornado web app BrowserID is a new single sign-on initiative lead by Mozilla that takes a very refreshing approach to single sign-on. It's basically like OpenID except better and similar to the OAuth solutions from Google, Twitter, Facebook, etc but without being tied to those closed third-parties.

At the moment, BrowserID is ready for production (I have it on Kwissle) but the getting started docs is still something that is under active development (I'm actually contributing to this).

Anyway, I thought I'd share how to integrate it with Tornado

First, you need to do the client-side of things. I use jQuery but that's not a requirement to be able to use BrowserID. Also, there are different "patterns" to do login. Either you have a header that either says "Sign in"/"Hi Your Username". Or you can have a dedicated page (e.g. Let's, for simplicity sake, pretend we build a dedicated page to log in. First, add the necessary HTML:

<a href="#" id="browserid" title="Sign-in with BrowserID">
  <img src="/images/sign_in_blue.png" alt="Sign in">
<script src="" async></script>

Next you need the Javascript in place so that clicking on the link above will open the BrowserID pop-up:

function loggedIn(response) {
  location.href = response.next_url;
  /* alternatively you could do something like this instead:
       $('#header .loggedin').show().text('Hi ' + response.first_name);
    ...or something like that */

function gotVerifiedEmail(assertion) {
 // got an assertion, now send it up to the server for verification
 if (assertion !== null) {
     type: 'POST',
     url: '/auth/login/browserid/',
     data: { assertion: assertion },
     success: function(res, status, xhr) {
       if (res === null) {}//loggedOut();
       else loggedIn(res);
     error: function(res, status, xhr) {
       alert("login failure" + res);
 else {

$(function() {
  $('#browserid').click(function() {;
    return false;

Next up is the server-side part of BrowserID. Your job is to take the assertion that is given to you by the AJAX POST and trade that with for an email address:

import urllib
import tornado.web
import tornado.escape 
import tornado.httpclient

@route('/auth/login/browserid/')  # ...or whatever you use
class BrowserIDAuthLoginHandler(tornado.web.RequestHandler):

   def check_xsrf_cookie(self):  # more about this later

   def post(self):
       assertion = self.get_argument('assertion')
       http_client = tornado.httpclient.AsyncHTTPClient()
       domain = ''  # MAKE SURE YOU CHANGE THIS
       url = ''
       data = {
         'assertion': assertion,
         'audience': domain,
       response = http_client.fetch(

   def _on_response(self, response):
       struct = tornado.escape.json_decode(response.body)
       if struct['status'] != 'okay':
           raise tornado.web.HTTPError(400, "Failed assertion test")
       email = struct['email']
       self.set_secure_cookie('user', email,
       self.set_header("Content-Type", "application/json; charset=UTF-8")
       response = {'next_url': '/'}

Now that should get you up and running. There's of couse a tonne of things that can be improved. Number one thing to improve is to use XSRF on the AJAX POST. The simplest way to do that would be to somehow dump the XSRF token generated into your page and include it in the AJAX POST. Perhaps something like this:

var _xsrf = '{{ xsrf_token }}';
function gotVerifiedEmail(assertion) {
 // got an assertion, now send it up to the server for verification
 if (assertion !== null) {  
     type: 'POST',
     url: '/auth/login/browserid/',
     data: { assertion: assertion, _xsrf: _xsrf },

Another thing that could obviously do with a re-write is the way users are handled server-side. In the example above I just set the asserted user's email address in a secure cookie. More realistically, you'll have a database of users who you match by email address but instead store their database ID in a cookie or something like that.

What's so neat about solutions such as OpenID, BrowserID, etc. is that you can combine two things in one process: Sign-in and Registration. In your app, all you need to do is a simple if statement in the code like this:

user = self.db.User.find_by_email(email) 
if not user:
    user = self.db.User() = email
self.set_secure_cookie('user', str(

Hopefully that'll encourage a couple of more Tornadonauts to give BrowserID a try.


Sam Penrose
Hey, this is great stuff! Thanks for being an early supporter. We have since changed a couple of domains, so: => =>

Also, getVerifiedEmail() is deprecated. Finally, I am no Tornado hacker, but would the "validate_cert" kwarg be available in when you call http_client.fetch()? If so, using it would be a Good Thing.

Thanks again for a great blog post!
-- Sam
Peter Bengtsson
Thank you so much!
The site that I implemented it on I no longer support but your comment will hopefully help in guiding poor people who end up here.
Thank you for posting a comment

Your email will never ever be published

Related posts

Trivial but powerful tips for nosetests 19 November 2011
Python file with closing automatically 03 December 2011
Related by Keyword:
Common names amongst my Facebook friends 26 June 2014
Registration and sign-in by email verification 29 April 2013
Beach volleyball bums 02 August 2012
Too Cool For Me? 25 September 2011
A taste of the Django on inside Mozilla, Sheriffs Duty 22 July 2011
Related by Text:
My tricks for using AsyncHTTPClient in Tornado 13 October 2010
To then() or to success() in AngularJS 27 November 2014
How to unit test the innards of a Django view function 15 November 2008
AngularJS $q notify and resolve as a local GET proxy 18 April 2015
Using in Django 13 December 2016