Skip to content
rgrp edited this page May 25, 2011 · 19 revisions

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

  1. The Service Provider (e.g. http://annotateit.org/ or something similar) - where annotations are stored
  2. The Consumer (e.g. OpenShakespeare) - the website where annotations happen
  3. The User (and the User Agent) - the person/browser doing the annotations

Initially, the Consumer must register with the Service Provider: "I would like you to store my users' annotations on my behalf".

The Consumer is provided with a Account Key and an Account Secret and then provides to the user agent:

  • A set of headers
  • An auth-token consisting of set of headers 'signed' with Account Secret (via SHA256 hash)

The following are the current required set of headers (which should be prefixed with 'x-annotator-':

  • auth-token
  • account-id
  • user-id

We also optionally support a TTL header:

  • auth-token-valid-until (iso8601 formatted)

The auth-token is computed as:

  • token = sha256(account-key + user-id + auth-token-valid-until).hexdigest()

Authentication Specification

Nomenclature

  • Annotations are stored on the annotation store Service Provider (SP) -- e.g. annotations.okfn.org
  • Texts to be annotated, and configuration of the annotator, is provided by the Consumer (C) -- e.g. openshakespeare.org
  • User (U) visits client site using a User Agent (UA)

To aid comprehension, parallels are drawn with OAuth standard terminology. However, for clarity, it should be noted that communication is not needed between SP and C at the time U comes into contact with C's website. Issue of a Consumer Key (accountId) has already occurred by this point.

Definitions

Unless otherwise specified, the meanings of the following are:

  • accountId = Key for Consumer as issued by SP

  • accountSecret = Secret for Consumer as issued by SP

    • NB: accountSecret must never be sent over the wire after initial issue to the Consumer. It must never be revealed by the Consumer to any User.
  • authTokenTTL = Authentication token lifetime (in seconds) as agreed by C and SP

  • authTokenValidUntil = ISO8601-formatted time at which authToken was issued

  • authToken = SHA256(accountSecret + userId + authTokenValidUntil)

    • NB: + indicates string concatenation
    • NB: authToken must be computed on the Consumer's servers, not in the UA.
  • annotation = JSON-serialized representation of an annotation

  • userId = unique user identifier as defined by the Consumer

NB: this does not need to be globally unique, but must be locally unique to each of the consumer's users. If users share their userId, they will be able to forge each other's requests to the SP.

Timeline

  1. U opens C's site.

  2. U logs in to site, and is authenticated to C as userId.

  3. UA sends a GET request to C to retrieve an authentication token and associated information. If U is logged in as userId, C responds with a JSON dictionary, the "authentication envelope", containing the following keys:

    { accountId
    , authToken
    , authTokenValidUntil
    , userId
    }
    
    • If U is not logged in, should respond with an HTTP 401 status code.
    • NB: Strictly, accountId, and userId need not be sent in every such response, but the protocol is simpler if we ignore this.
  4. C website javascript saves AuthToken object in page scope. Before every annotation request, it should call isValid() on the token. This should, at the very least, ensure that:

     time.now < authTokenValidUntil
    
  5. If the authToken is valid, then the annotation request can be made, including all the key-value pairs in the authentication envelope in the request. If not, we should return to step 3 and retrieve a new authentication token before attempting the request.

  6. The SP receives the request. It retrieves the accountSecret corresponding to the provided accountId from its database. It computes SHA256(getConsumerSecretForConsumer(request.accountId) + request.userId + request.authTokenValidUntil). If the result is not identical to authToken, the request can be discarded at this point.

  7. If this condition is fulfilled, the request is both authentic and valid, and should be dealt with appropriately.

  8. If step 6 fails, the request should return an HTTP 401 status code.

Colophon

Copied from original at:

Clone this wiki locally