Skip to content
nickstenning edited this page Feb 1, 2012 · 19 revisions

What's the authentication system for?

The simplest way to explain the role of the authentication system is by example. Consider the following:

  1. Alice builds a website with documents which need annotating, DocLand.

  2. Alice registers DocLand with AnnotateIt, and receives a "consumer key/secret" pair.

  3. Alice's users (Bob is one of them) login to her DocLand, and receive an authentication token, which is a cryptographic hash of (among other things) their unique user ID at DocLand, and DocLand's "consumer secret".

  4. Bob's browser sends requests to AnnotateIt to save annotations, and these include the authentication token as part of the payload.

  5. AnnotateIt can verify the Bob is a real user from DocLand, and thus stores his annotation.

So why go to all this trouble? Well, the point is really to save you trouble. By implementing this authentication system (which shares key ideas with the industry standard OAuth) you can provide your users with the ability to annotate documents on your website without needing to worry about implementing your own Annotator backend. You can use AnnotateIt to provide the backend: all you have to do is implement a token generator on your website (described below).

(NB: the specification described isn't yet rolled out on AnnotateIt as of February 1, 2012. We're working on it!)

This is the simple explanation, but if you're in need of more technical details, keep reading.

Technical overview

How do we authorise users' browsers to create annotations on a Consumer's behalf? There are three (and a half) entities involved:

  1. The Service Provider (SP; AnnotateIt in the above example)
  2. The Consumer (C; DocLand)
  3. The User (U; Bob), and the User Agent (UA; Bob's browser)

Annotations are stored by the SP, which provides an API that the Annotator's "Store" plugin understands.

Text to be annotated, and configuration of the clientside Annotator, is provided by the Consumer.

Users will typically register with the Consumer -- we make no assumptions about your user registration/authentication process other than that it exists -- and the UA will, when visiting appropriate sections of C's site, request an authToken from C. Typically, an authToken will only be provided if U is currently logged into C's site.

Technical specification

It's unlikely you'll need to understand all of the following to get up and running using AnnotateIt, but it might come in handy if you want to write your own Service Provider.

A typical authToken comprises five parts:

  1. consumerKey = the key issued to C by SP
  2. userId = the unique identifier of U on C's website
  3. authTokenIssueTime = the ISO8601 formatted time at which the token was issued
  4. authTokenTTL = the length of time (in seconds) for which the token is valid (agreed when C obtained key/secret from SP, and usually 86400 seconds, or 1 day)
  5. authToken = the auth token itself, defined below

and might look something like this:

{
  "authToken": "2a83829a12758b9ebe37ad8facbdb1e740e0436d8915363c6f9f2a8868f51cf1", 
  "consumerKey": "annotateit", 
  "authTokenTTL": 86400, 
  "userId": "joebloggs", 
  "authTokenIssueTime": "2012-02-01T17:47:56.917271+00:00"
}

The authToken itself is computed as sha256(consumerSecret + userId + authTokenIssueTime).

For reference, here's a Python implementation of a token generator, suitable for dropping straight into your Flask or Django project:

import datetime
import hashlib

# Replace these with your details
CONSUMER_KEY = 'yourconsumerkey'
CONSUMER_SECRET = 'yourconsumersecret'

# Only change this if you're sure you know what you're doing
CONSUMER_TTL = 86400

ZERO = datetime.timedelta(0)
class Utc(datetime.tzinfo):
    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO
UTC = Utc()

def generate_token(user_id):
    issue_time = datetime.datetime.now(UTC).isoformat()
    token = hashlib.sha256(CONSUMER_SECRET + user_id + issue_time).hexdigest()

    return dict(
        consumerKey=CONSUMER_KEY,
        authToken=token,
        authTokenIssueTime=issue_time,
        authTokenTTL=CONSUMER_TTL,
        userId=user_id
    )

Now all you need to do is expose an endpoint in your web application that returns a JSON copy of the token dict to logged-in users (say, 'http://example.com/api/token'), and you can set up the Annotator like so:

$(body).annotator()
       .annotator('setupPlugins', {tokenUrl: 'http://example.com/api/token'});

Colophon

Original planning documents at:

Rehashed in Feb 2012:

Clone this wiki locally