LCP License Server API - readium/readium-lcp-server GitHub Wiki

In brief

This server is never exposed on the wild Web: it is safely setup in a trusted zone, and the use of its API requires proper authentication.

The server stores, for each license: its ID, date of issue, provider's name, the (hashed) user key processed from the user passphrase, the associated hint, usage rights relative to the protected content and a reference to the encrypted content data.

The License server exposes several private REST API methods:

  • Store an encrypted publication
  • Fetch an encrypted publication
  • Generate and fetch a license
  • Generate and fetch a protected publication
  • Update the rights associated with a license
  • List licenses
  • Update a license

API

Store en encrypted publication

This method stores data generated by e.g. lcpencrypt. See the corresponding lcpencrypt spec.

PUT <LCPbaseURL>/contents/<content_id>

Payload: it is a json payload structured like:

{
  "content-id" : "<value>",
  "storage-mode" : 0,
  "content-encryption-key" : "<value>",
  "protected-content-location" : "<value>",
  "protected-content-length" : "<value>",
  "protected-content-sha256" : "<value>",
  "protected-content-disposition" : "<value>"
} 

The method can load the encrypted file using a local storage (file:) or http(s).

The protected-content-location field contains the location where the lcpserver will search for the file.

The protected-content-disposition in the json struct is used as target filename. When retrieving the encrypted file, the user therefore gets this filename in the Content-Disposition http header. The content_id in the URL equals the content-id in the json payload and will be the effective content_id for the lcpserver.

Return:

Fetch an encrypted publication

This method fetches the encrypted publication previously stored by the License Server.

Warning: It does not return anything if the encryption tools manages the storage of the encrypted publication.

Note that an encrypted publication is not associated with any LCP license; this is the result of the initial encryption process applied by e.g. the LCP encryption tool. Do not confuse it with a protected publication.

GET <LCPbaseURL>/contents/<content_id>

Return:

Generate a license

This method generates a license from the data resulting from a user transaction on the content management system.

The caller passes the following arguments:

  • The provider identifier (a URL),
  • User information the provider wants to embed in the license (id, name, email; optional),
  • The (hashed) passphrase and corresponding user hint
  • The rights the user gets on the content.

POST <LCPbaseURL>/contents/<content_id>/license

Payload: {partial license} (json)

The payload structure is a partial LCP license document, which will be completed by the License Server.

The payload MUST contain the following json properties:

  • provider: a URI that identifies the provider in an unambiguous way;
  • user: the user information the provider wants in the license, either in clear or encrypted; check the LCP specification for more details. id is mandatory, all other properties are optional; allowed properties are:
    1. id: an identifier for the user.
    2. email: the user email.
    3. name: the user name.
    4. encrypted: the array of properties the provider wants encrypted.
  • encryption/user_key: an object with 3 properties:
    1. text_hint: the hint proposed by the provider to the user for selecting its passphrase.
    2. hex_value or value: the hashed value of the user passphrase. Its format is specified below. Warning: it is not part of the LCP specification.
    3. algorithm: the URI of the hashing algorithm used for calculating the above value. The algorithm used in the context of the basic and 1.0 profiles is http://www.w3.org/2001/04/xmlenc#sha256. As this is the default value used by the server, setting this property is optional.
  • rights: the set of rights associated with the license. Unused properties can be skipped. Allowed sub-properties are:
    1. start: date and time when the license begins.
    2. end: date and time when the license ends.
    3. print: maximum number of pages that can be printed over the lifetime of the license.
    4. copy: maximum number of characters that can be copied to the clipboard over the lifetime of the license.

Extensibility: The payload MAY contain other license properties, which will be included in the final license if they are not set by the License Server.

Sample partial license:

{
  "provider": "http://www.imaginaryebookretailer.com",
  "user": {
    "id": "d9f298a7-7f34-49e7-8aae-4378ecb1d597",
    "email": "[email protected]",
    "encrypted": ["email"]
  },
  "encryption": {
   "user_key": {
    "text_hint": "The title of the first book you ever read",
    "hex_value": "4981AA0A50D563040519E9032B5D74367B1D129E239A1BA82667A57333866494",
    }
  },
  "rights": {
    "print": 10,
    "copy": 2048,
    "start": "2019-11-04T01:08:15+01:00",
    "end": "2019-11-25T01:08:15+01:00"
  }
}

Calculating hex_value: A server should never directly store the user passphrases, which are personal info, but rather hashed values, as hex-encoded strings. For instance the passphrase "123 456" becomes "4981AA0A50D563040519E9032B5D74367B1D129E239A1BA82667A57333866494" when hashed using the sha256 algorithm (just try with this online tool). hex_value is simply the result of the hash calculation, as an hex-encoded string.

Calculating value: This property, alternative to hex_value, is only kept for backward compatibility with the initial version of the API. From the hashed value of the passphrase, expressed as an hex-encoded string, calculate a byte array (32-bytes / 256-bits binary buffer); for instance, "4981AA..." becomes [49, 81, 170, ...]. The expected value is the Base64 encoding of this byte array (a base64 conversion is usually implicitly applied to byte arrays when converted to json structures). More info can be found in this issue.

Storage: No personal data is stored in the License server database.

The only data stored into the License server database are:

  • The provider identifier
  • The user identifier
  • The content identifier
  • The rights associated with the license (print, copy, start, end)
  • The date of issue of the license, added after the license has been generated.

Links: The links that will be included in the complete license are specified in the license section of the configuration file (as templated links), not in the partial license.

Return:

Processing of the partial license

The following data will be deleted from the structure before the license is returned to the sender:

  • The custom user key value property

The following data will be added to the returned license:

  • The license identifier
  • The date of issue of the license
  • Optionally, the date of update of the license
  • The encryption profile
  • The content key information (encrypted value and algorithm)
  • The link to the publication
  • The link to the status document
  • A signature

Note:

  • Licenses are generated to be served as .lcpl file, i.e. outside of their associated publication; this is why the link to the publication is provided.
  • The use of License Status Documents is mandatory for license providers.

Fetch a fresh license

This method returns an already existing license, using its identifier as key.

The caller must pass the following user information:

  • passphrase hash,
  • passphrase hint,
  • any user information the provider wants embedded in the license (an exception is the user id, which has been passed to the License Server when the license was generated and does not need to be repeated here).

POST <LCPbaseURL>/licenses/<license_id>

Payload: {partial license} (json)

The payload structure is a partial LCP license document, which will be completed by the License Server. Its structure is similar to the structure used for the generation of a new license (see above), but the provider property and the rights section should be omitted.

Sample partial license:

{
  "user": {
    "email": "[email protected]",
    "encrypted": ["email"]
  },
  "encryption": {
   "user_key": {
    "text_hint": "The title of the first book you ever read",
    "hex_value": "4981AA0A50D563040519E9032B5D74367B1D129E239A1BA82667A57333866494",
    }
  }
}

Return:

List licenses

This method returns a sequence of partial licenses, in ante-chronological order (on date of issue).

It may be used as a simple way for an external processor to extract periodically the newly created licenses e.g. for indexation in an analytics system or search engine.

The pagination mechanism is based on the Link headers RFC (https://tools.ietf.org/html/rfc5988 ) and the Github pagination syntax (https://developer.github.com/v3/#pagination).

Note that page numbering is 1-based and that omitting the ?page parameter will return the first page. Requests that return multiple items will be paginated to 30 items by default.

GET <LCPbaseURL>/licenses{page?, per_page?}

Return:

Example: http://lcpserver.my.org/licenses?page=2&per_page=50

Sample partial license returned by the list request:

{
  "id": "e698a7-7f34-49e7-8aae-4378ecb1d5ff",
  "issued": "2016-11-04T01:08:15+01:00",
  "updated": "2016-11-04T01:09:15+01:00",
  "provider": "http://www.imaginaryebookretailer.com",
  "user": {
    "id": "d9f298a7-7f34-49e7-8aae-4378ecb1d597",
  },
  "rights": {
    "print": 10,
    "copy": 2048,
    "start": "2016-11-04T01:08:15+01:00",
    "end": "2016-11-25T01:08:15+01:00"
  }
}

Get the licenses associated with a given publication

This method returns a sequence of partial licenses, in ante-chronological order (on date of issue).

GET <LCPbaseURL>/contents/<content_id>/licenses{page?, per_page?}

Return:

Update a license

This method can force the update of the information stored in the License Server database, I.e.

  • The license provider
  • The user identifier
  • the rights associated with a license

It is usually called from the License Status server (for lending return or renewal), and in this case only contains a rights object.

PATCH <LCPbaseURL>/licenses/<license_id>

Payload: {partial-license} (json). The licence structure is a partial LCP license document. All fields listed above are updated if present in the source partial license.

Return:

Sample partial license:

{
  "rights": {
    "end": "2016-11-20T11:30:00+01:00"
  }
}

Generate a protected publication

This method generates a license from the data resulting from a user transaction on the content management system, and embeds the license in the encrypted publication identified in the request.

Warning 1: It does not return anything if the encryption tools manages the storage of the encrypted publication.

Warning 2: The License Server provides such functionality mostly for testing purposes. Generating a protected publication is CPU and memory intensive, which goes against the philosophy of the License Server. Providers should not use it unless they have a very good reason to do so; an equivalent functionality can easily be created as part of the content management system.

The resulting stream should be returned to the caller. The CMS may return the stream directly to the user as a direct download, or save it as a file in a Web accessible location and present to the user a "download" button.

POST <LCPbaseURL>/contents/<content_id>/publication

Payload: {partial license} (json): the structure of the partial license is strictly identical to the one specified for generating licenses.

Return:

Fetch an existing protected publication

This method returns a protected publication, using its license identifier as key.

Warning 1: It does not return anything if the encryption tools manages the storage of the encrypted publication.

Warning 2: The License Server provides such functionality mostly for testing purposes. Generating a protected publication is CPU and memory intensive, which goes against the philosophy of the License Server. Providers should not use it unless they have a very good reason to do so; an equivalent functionality can easily be created as part of the provider's content management system.

The resulting stream is returned to the caller. The caller may return the stream directly to the user as a direct download, or save it as a file in a Web accessible location and present to the user a "download" button.

POST <LCPbaseURL>/licenses/<license_id>/publication

Payload: {partial license} (json): the structure of the partial license is strictly identical to the one specified for fetching licenses.

Return:

⚠️ **GitHub.com Fallback** ⚠️