How to implement a BrowserID Primary

While working on my browserid-provider gem I had to figure out a few basic things to get this working, so I thought I’d share the essence here.

UPDATE: The primary source now is found at MDN.

The requirements for a BrowserID Primary Identity Provider are as follows.

  • A Primary authorize and certify users of it’s own domain by email address
  • The provider must respond to the following requests:
    1. GET /.well-known/browserid
    2. GET /provision
    3. GET /whoami
    4. GET /sign_in
    5. POST /certify

But wait, the only strict requirement is the first of those, since the rest can be configured to any other path of your preference.

GET /.well-known/browserid

You can observe the well-known/browserid at eyedee.me is is a JSON file with three keys:

  • public-key: An RSA public key
  • authentication: where to GET /sign_in
  • provisioning: where to GET /provision

I found some understanding of the RSA key here and the JWS here. The wiki page about the Primary Protocol gives more details about the requirements. Here’s how you can make one:

GET /provision

Next, your browser will send a request to your provisioning path for the HTML content that goes into the iframe mentioned on the Primary wiki page. I modified the static page from eyedee.me to my own dynamic page.

GET /whoami

This is a simple “application/json” document showing the username part of an email address:

{"user":null} or {"user":"mormor"}

GET /sign_in

This is where you present your authentication form, it will be loaded in the BrowserID window. You have to use the BrowserID authentication_api.js here, but watch out for getting this from the same site as the client gets the include.js and the /provision gets the provisioning_api.js. See more at the developer tips.

POST /certify

Last, The provisioning generates a key pair for the current user session on the client side, and sends a POST to the certify path. Have a look at the navigator.id.genKeyPair function in the provision document. The JSON data posted should look like this:

What we would like to do is:

  1. Verify that the browser session is active (a user is logged in)
  2. Sign the client certificate with our Primary certificate

BrowserID expects a JSON document like this one in response:

{"cert":"c429d3345a8d17eb3bf4a06a349d392e00d329744a5179380344e .... "}

Which I generate like this:

If you’re a Rubyist, go use my gem. Otherwise, go make a Primary.

By the way: I couldn’t have done this without the json-jwt.