1. Overview

Bookshare has a variety of resources available through the API. Some of them are available publicly with no authentication needed, but for many resources, such as copyrighted material or user account data, the Bookshare API v2 requires that a user be authenticated and that their access to a particular resource be authorized. The process of authenticating and authorizing revolves around the concept of a token that is first obtained and then presented to the API on each call. This process and the token follow the OAuth 2 standard, so if you are familiar with that from other contexts or APIs, you should notice many similarities in Bookshare’s API.

2. OAuth 2 tokens

An OAuth 2 access token is a string that is opaque to an API client, but carries meaning for the API server. It represents an authenticated user and the authorization they gave for a specific client to access resources on their behalf. Being opaque means that it doesn’t reveal any identifying information about the user to the client, and because the process of obtaining that token is done outside the view of the client, as we will explain below, it doesn’t reveal any user credentials to the client either. That said, the access token should still be kept private to the client to prevent others from using it. If it were leaked, someone could use it to impersonate that user and that client. It is for this reason that access tokens have a limited lifespan, and can also be revoked on demand, something also explained more below.

For example, a token string of 'aX93nd02k…​' might represent the authorization that User A gave for Client X to act on their behalf, after User A was verified by offering their credentials to an authorization server.

To use an access token, you present it on each call that is made on a user’s behalf as a "Bearer" token in an "Authorization" request header.

Using curl as an example, a call to get a list of a user’s reading lists would look like this:

curl -H "Authorization: Bearer <token string>" https://api.bookshare.org/v2/mylists?api_key=<your API key>

This example highlights another authorization feature, which is that an access token is different than an API key. An access token identifies a user and an API key identifies a client. An API key can be used to serve many different users, and a user can obtain an access token for many different API keys. Both of them are important to ways that the Bookshare API secures access to protected resources but they are different concepts and managed differently. The information on this page is focused solely on access tokens.

While the OAuth2 Bearer Token Usage guidelines say that an access token can be presented in request parameters or form parameters, those uses are less secure and are discouraged. The authorization header should always be used as the Bookshare API may remove support for request parameter and form parameter use of access tokens in the future.

3. Obtaining an OAuth 2 token

The OAuth 2 spec describes different ways, called "grant types", to get a token, with the most common one proceeding like this:

  1. Your app: Post a request to the Bookshare Auth site, requesting a token for a certain user, for your app.

  2. User: Is presented with a Bookshare page to authorize or deny your app to perform certain operations on their behalf (download books, update account information, etc).

    1. The Bookshare Auth site may first challenge the user for their username and password if it can’t authenticate them from the initial request.

  3. Your app: If the user approved the request, the Bookshare Auth site will redirect to a URI that you provided for your app, with a code included on the response. Your app calls the Bookshare Auth site again to exchange this code for an access token to be used for calls on behalf of that user.

This means a couple of important things:

  • Your app never sees a user’s Bookshare username and password. All you need is the user’s token, which identifies them to Bookshare.

  • Your app needs to be able to be addressed by a URI so that the Bookshare Auth site can deliver the token to you. This URI could be a custom URI scheme registered with a mobile OS, or an HTTP URL handled by your app or site. See the Mobile Development section for more information if you’re developing a mobile application.

The Bookshare API v1 uses a different, custom authentication scheme. To help smooth the migration to the v2 API, OAuth 2 tokens can be used for access to both v1 and v2 resources.

4. OAuth 2 Grant Types

The Bookshare API supports the "authorization code" grant type.[1] [2] This is the only grant type defined for applications that are operating on behalf of end users. The flow is as described above, and a more concrete example follows.

The authorization code flow also provides a refresh token in its response, which allows you to request a new access token on the user’s behalf when the access token expires, without having to involve the user in reauthorizing your app.

5. Sample OAuth 2 authorization code grant flow

Let’s suppose that the application Flubber wants to get an OAuth 2 token for the person using their application, and wants to use the authorization code grant type.

To be able to handle the callback that will notify them that a user has approved them, the Flubber developers have registered a URI scheme of "flubber" with their mobile app platform, and have implemented the ability to recognize calls to the URI "flubber://authorize".

They have also registered their application with Bookshare, and were given an API key of "abcdefg", and a client secret of "xyz123".[3] In the OAuth 2 flows, these will be used for the client_id and client_secret parameter whenever these are called for.

They start by offering the app user a choice to "Login with Bookshare", which results in a GET to the Bookshare authorization server with a collection of request data. They also choose to send along a state parameter of "something", which they can compare against the state value that comes back on the redirect. The request would look like this:

GET /oauth/authorize HTTP/1.1
Host: auth.bookshare.org
Content-Type: application/x-www-form-urlencoded

response_type=code&client_id=abcdefg&redirect_uri=flubber://authorize&scope=basic&state=something

The result of this will be a redirect to a Bookshare web page, where the user will enter their credentials, and then be asked to approve or deny Flubber’s access to Bookshare on their behalf. The Flubber app isn’t involved in any of this interaction, so it is irrelevant to them how it happens.

Once the user has authenticated themselves and approved access, the Bookshare authorization server will send a redirect to the Flubber user-agent, giving it the redirect URI that was passed in, and attaching the authorization code.

flubber://authorize?code=4hMt0A&state=something

That authorization code is not the access token, but it can then be exchanged for an access token by calling the API again.

POST /oauth/token HTTP/1.1
Host: auth.bookshare.org
Authorization: Basic abcdefghijklmnop (1)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=4hMt0A&redirect_uri=flubber://authorize&scope=basic (2)
1 The basic auth credentials are the client ID and client secret.
2 The original redirect URI is needed as well, to ensure that the request is coming from the same client who requested the code.

This will send a JSON response that will include the access token, expiration time in seconds, and a refresh token.

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 169

{
    "access_token":"1234567",
    "token_type":"bearer",
    "refresh_token":"9876543",
    "expires_in":1209599,
    "scope":"basic"
}

The Flubber app should securely store this access token value and use it in subsequent calls for this user. The app could also choose to securely store the refresh token to use when the access token expires, as shown below.

In curl, a call to get the user’s reading lists would now look like this:

curl -H "Authorization: Bearer 1234567" https://api.bookshare.org/v2/mylists?api_key=abcdefg

Finally, if the user instead had denied the access request, the Flubber app would still get a response at the redirect URI, but with an error code and reason in it.

flubber://authorize?error=access_denied&error_description=User denied access

Regardless of the reason, without an access token, the Flubber app would now be limited to making requests for unsecured resources on behalf of this user.

6. Token lifespan

A token will be valid until it either expires or is revoked by the user. The expiration time is given, in seconds, in the callback URI or token response. In practice, you will also be told when it expires by an error response if an outdated token is used.

{"error":"unauthorized","error_description":"Access token expired: 1234567"}

If you have a refresh token, you can simply make a call without involving the user, and receive a new access token.

POST /oauth/token HTTP/1.1
Host: auth.bookshare.org
Authorization: Basic abcdefghijklmnop
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=9876544&scope=basic

Revoking a token can be done by a user or a client. A user would revoke it through an interface that might show them all the authorizations they have given (something not yet built by Bookshare), or through an interface provided by a client to let them 'Sign out', for example. At the moment, Bookshare supports revoking a single token at a time.

7. Mobile Development

If you are using the Bookshare API and authorization server for developing mobile applications, you may need to claim a URL pattern or register a custom URL scheme. This will allow the operating system to launch your app upon authorization redirect instead of the system’s browser. The URL can then be parsed and the token retrieved from the redirect URL in order to interact with the Bookshare API.

The process of claiming a URL pattern or registering a custom URL scheme will be dependent on the app’s operating system. Here are some resources on how to deal with OAuth redirect URLs in mobile development:

8. Endpoints

8.1. OAuth 2

There are only two endpoints needed to use OAuth 2: requesting an authorization (a Bookshare user's approval to act on their behalf), and request an access token (a representation of that approval). Which one, or ones, that you use will depend on the grant type flow.

8.1.1. Request client authorization for a user

GET /oauth/authorize
Description

The Bookshare API supports the authorization code grant type, where the end user is asked to authorize access after they have identified themselves correctly to Bookshare. The client ends up with an access token without needing to know the end users Bookshare credentials. See the OAuth 2 spec for more information.

Parameters
Form Parameters
Name Description Required Schema Default

response_type

OAuth 2 grant type, should be 'code' for authorization code flow

X

enum [code]

null

client_id

Your client identifier

X

String

null

redirect_uri

URI for redirect to your client after the user has been authorized

X

String

null

scope

The requested scope of the access request. Should be 'basic'

X

enum [basic]

null

state

An opaque value that will be passed back with the redirect URI to allow the client to ensure the integrity of the response

-

String

null

Responses
HTTP Code Message Datatype

200

OAuth 2 token response

Token

0

Unexpected error

Token Error

Example HTTP request

See the Sample OAuth 2 authorization code grant flow for examples of using this endpoint.

8.1.2. Revoke an OAuth2 access token

POST /oauth/revocations
Description

This endpoint allows you to revoke an OAuth2 access token.

Parameters
Form Parameters
Name Description Required Schema Default

token

The token to revoke.

X

String

null

grant_type

OAuth 2 grant type of the token to be revoked.

-

enum [authorization_code, refresh_token, password]

null

Responses
HTTP Code Message Datatype

200

Revocation success message

Status

0

Unexpected error

Token Error

Example HTTP request
POST /oauth/revocations HTTP/1.1
Host: auth.bookshare.org
Authorization: Basic abcdefghijklmnop
Content-Type: application/x-www-form-urlencoded

token=ad13cb0b-1f5d-44e3-95e4-a072c5200c4f
Example HTTP response
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 169

{
    "key": "SUCCESS",
    "messages": [
        "Token revoked."
    ]
}
Example Curl request
$ curl -u "abcdefg:" --data-urlencode "token=ad13cb0b-1f5d-44e3-95e4-a072c5200c4f" https://auth.bookshare.org/oauth/revocations -X POST

8.1.3. Request an OAuth 2 access token

POST /oauth/token
Description

This endpoint allows you to exchange either an authorization code or a refresh token for an access token.

Parameters
Form Parameters
Name Description Required Schema Default

grant_type

OAuth 2 grant type, should be 'authorization_code', 'refresh_token'. The 'password' type is limited to trusted clients only.

X

enum [authorization_code, refresh_token, password]

null

code

Authorization code from authorization request (only for 'authorization_code' type)

-

String

null

refresh_token

Refresh token from original token request (only for 'refresh_token' type)

-

String

null

username

Bookshare username (only for 'password' type)

-

String

null

password

Bookshare password (only for 'password' type). Since passwords may contain special characters, be sure to URL-encode the data that is submitted.

-

String

null

scope

The requested scope of the authorization (optional)

-

enum [basic]

[basic]

Responses
HTTP Code Message Datatype

200

OAuth 2 token response

Token

0

Unexpected error

Token Error

Example HTTP request
POST /oauth/token HTTP/1.1
Host: auth.bookshare.org
Authorization: Basic abcdefghijklmnop
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=12345&scope=basic
Example HTTP response
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 169

{
    "access_token":"ad13cb0b-1f5d-44e3-95e4-a072c5200c4f",
    "token_type":"bearer",
    "refresh_token":"3303fab2-2823-4560-a710-eb1f11e544e7",
    "expires_in":1209599,
    "scope":"basic"
}
Example Curl request
$ curl -u "abcdefg:" --data-urlencode "grant_type=authorization_code&code=12345&scope=basic" https://auth.bookshare.org/oauth/token

9. Models

9.1. Status

Field Name Description Type Format Nullable

key

String

messages

List of String

X

9.2. Token

Field Name Description Type Format Nullable

access_token

An OAuth 2 bearer token

String

token_type

Will always be 'bearer'

String

expires_in

Number of seconds that this token will be valid

String

refresh_token

Only for the 'code' grant type, this token can be redeemed for another access token without requiring the user to reauthorize the application.

String

scope

A name representing the set of capabilities for which this token is authorized

String

9.3. Token Error

Field Name Description Type Format Nullable

error

One of a set of standard error names

String

enum [invalid_request, unauthorized_client, access_denied, unsupported_response_type, invalid_scope, server_error, temporarily_unavailable, invalid_grant]

error_description

A human readable description of the error condition

String

Appendix A: OAuth2 Password Grant Type

For trusted clients, the Bookshare API supports the OAuth2 Password Grant Type, offering a simplified way of getting authorization tokens for users. We limit its use because it is a flow that allows the client to see a user’s password before it is transmitted to Bookshare. Before we allow a client to use the password grant type, we ask the following:

  1. To demonstrate that their system does not store the plaintext password, or forward to any third party.

  2. To describe how the authorization code or implicit grant types are not possible with their technology.

Our goal is to provide as strong of assurances as is reasonable to the Bookshare users that their credentials are being managed responsibly. The password grant type requires a significant amount of trust between Bookshare and a client, so the stronger the demonstration of your credentials management, the more likely we will be to consider allowing this grant type.

A.1. Password Grant Flow

The password grant type is similar to the HTTP Basic Auth flow, in that the client collects and transmits a user’s credentials, and gets an authorization answer directly. In this case, the client gets the OAuth2 token back after this first call, without the redirection required in the authorization code or implicit grant types.

For example, a client will collect the username and password of a user through a form or some other means, and then call the token endpoint. Along with this information, the client should pass a HTTP Basic Auth header, with the API key as the username and a blank string as the password. The way this data is formed and presented will vary by programming language and tool, but an example with curl would look like this:

curl -u "abcdefg:" -d "grant_type=password&username=john.smith@somewhere.org&password=mysecret" https://auth.bookshare.org/oauth/token

The HTTP request itself will look something like this:

POST /oauth/token HTTP/1.1
Host: auth.bookshare.org
Authorization: Basic eXR2czlwenNkNjJidjdyemFtd2RrdGhlOg==
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=john.smith@somewhere.org&password=abcdefg

The Bookshare server will authenticate the user, and if their credentials match, will return a token. This will be the same kind of access token you would get from the other grant types, with an expiration interval and a refresh token.

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 169

{
    "access_token":"1234567",
    "token_type":"bearer",
    "refresh_token":"9876543",
    "expires_in":1209599,
    "scope":"basic"
}

If the credentials don’t match what the server knows about the user, you will get a 400 status code and an error message.

HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
{
    "error":"invalid_grant",
    "error_description":"Bad credentials"
}

Once you have an access token, you can store it for this user, and pass it along with each subsequent request to identify the user to the system. In curl, a call to get the user’s reading lists, for example, would look like this:

curl -H "Authorization: Bearer 1234567" https://api.bookshare.org/v2/mylists?api_key=abcdefg

1. The password grant is supported, but since it involves the user presenting their Bookshare credentials to the client, this is available only to approved clients. Please contact partner-support@bookshare.org if you would like to request permission to use it. The password grant flow is described in the appendix.
2. The "implicit" grant type was defined in the OAuth 2.0 specification but has since been removed due to security concerns, and Bookshare no longer supports it.
3. Client secrets are created when you first register for an API key. If you were not given a client secret, you can use a blank string where the secret is called for.