-
Notifications
You must be signed in to change notification settings - Fork 538
Authentication
NB: the specification described below isn't yet rolled out on AnnotateIt as of February 1, 2012. We're working on it!
The simplest way to explain the role of the authentication system is by example. Consider the following:
-
Alice builds a website with documents which need annotating, DocLand.
-
Alice registers DocLand with AnnotateIt, and receives a "consumer key/secret" pair.
-
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".
-
Bob's browser sends requests to AnnotateIt to save annotations, and these include the authentication token as part of the payload.
-
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).
This is the simple explanation, but if you're in need of more technical details, keep reading.
How do we authorise users' browsers to create annotations on a Consumer's behalf? There are three (and a half) entities involved:
- The Service Provider (SP; AnnotateIt in the above example)
- The Consumer (C; DocLand)
- 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.
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:
-
consumerKey
= the key issued to C by SP -
userId
= the unique identifier of U on C's website -
authTokenIssueTime
= the ISO8601 formatted time at which the token was issued -
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) -
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'});
Original planning documents at:
Rehashed in Feb 2012: