Please be aware that you are viewing our bleeding edge unstable documentation. Unless you wanted to view the bleeding edge (and possibly unstable) documentation, we recommend you use our stable docs.

Go to Ably's stable canonical documentation »

I know what I'm doing, let me see the bleeding edge docs »

You are viewing our bleeding edge unstable documentation. We recommend you use our stable documentation »
Fork me on GitHub

REST API Token Request Spec

The Ably REST and Realtime client libraries aim to make things as simple as possible so it is not necessary to understand all of the details of token requests to interact with the service and issue tokens for clients. If you wish to issue tokens or token requests, we recommend you start with the client library authentication documentation.

However, if you are using the REST API token endpoint directly, or if you are creating token requests without the use of our client libraries, then the following specification will give you an in-depth understanding of how token requests work.

API key format

API keys are issued and managed from within your account dashboard. The API key string available in your dashboard is structured as a triple <app ID>:<key ID>:<key value>, where:

app ID
(public) identifier for the application
key ID
(public) identifier for the key in question: this uniquely identifies the key and is a system-assigned, URL-safe, identifier
key value
(private) key “secret” string, system-generated, uniquely associated with this key

Token request format

A token request is made against the requestToken endpoint, with a JSON token request in the request body. The token request comprises:

A signed token request also contains:

Signed token requests can be used to request a token from Ably without an authenticated connection. The signature generated with the key secret confirms the authenticity of the token and can thus be “trusted” by Ably. As signed token requests can be issued without a request to Ably, a server with a valid API key can issue token requests directly to clients, and clients can in turn generate a token by sending the token request to Ably.

The receiving Ably server verifies the signature if present, the timestamp (which must be within 2 minutes of the current time), verifies that the nonce/timestamp combination has not been used previously, verifies that the requested validity period is permitted, and verifies that the requested capabilities are permitted for that token based on the key capabilities.

The server may choose to subset the capabilities based on the capabilities of the key.

The server replies with an access token, which is essentially a signed version of the resolved set of capabilities, plus other metadata associated with the token (such as expiry time).

This access token can then be used for subsequent REST requests or Realtime connections. If a clientId was included in the request, then the token is associated with that clientId, and may be used to identify that client in operations that require identification (e.g. joining a channel that requires identification, or publishing a message with a verified clientId).

Parameter canonicalisation

The parameters of the token request are normalized/canonicalized as follows:

keyName
no action required
ttl
the decimal integer representation, without leading zeros, of the requested life of the token in milliseconds, if none is specified a default of 1 hour is used
capability
this is a canonicalized representation of the channel paths and associated operations in the capability. It is a JSON stringified value of a Javascript object of the form:
{
  "channel1": ["operation1a", "operation1b", "operation1c", ...],
  "channel2": ["operation2a", "operation2b", "operation2c", ...],
  ...
}

with the following constraints:

  • all white-space is removed;

  • channels are listed in forward lexicographic order;

  • operations are listed in forward lexicographic order;

  • there is no trailing comma on any list of array or object elements;

  • all strings are quoted and escaped as per the JSON standard;

  • for channel paths, the wildcard character * has special meaning. When the channel path is exactly "*", then all channels are matched. If however, a channel path ends with *, then the path before the * is used to match all channels in that namespace. For example, the channel path "user:*" matches all channels in the user namespace such as "user:john" and "user:matt". Find out more about channel namespaces;

  • for operations, the wildcard character * has special meaning. When the operation is exactly "*", then all operations are supported. Otherwise, a permitted operation string must be provided
clientId
the canonical form is the unquoted and un-escaped string. In the case that no clientId is included in the request, the empty string is used.
timestamp
the decimal integer representation, without leading zeros, of the time of the of the request in milliseconds since the epoch.
nonce
an unquoted, un-escaped random string of at least 16 characters.

Capability operations

The following capability operations are available for API keys and issued tokens.

subscribe
can subscribe to messages and presence state change messages on channels
publish
can publish messages to channels
presence
can register presence on a channel (enter, update and leave)
history
can retrieve message and presence state history on channels
stats
can retrieve current and historical usage statistics for an app
push-subscribe
can subscribe devices for push notifications
push-admin
can manage device registrations and push subscriptions for all devices in an app

See a working capabilities example, read understanding capabilities and token security above to get a more thorough overview of how capabilities can be used to secure your application along with working examples.

HMAC calculation

First the canonicalized request text, constructed as follows:

The resulting string must then the UTF8-encoded and then HMAC value is computed with hmac-sha-256 using the key secret value.

The HMAC value is then base-64 encoded.

Request body format

In the case of a signed token request, the request body is the JSON stringified representation of the object with the form:

{
  keyName: "<app ID>:<key ID>",
  ttl: <expiry in milliseconds>,
  capability: "<capability string>",
  clientId: "<client ID optional>",
  timestamp: <timestamp as ms since epoch>,
  nonce: "<random unique nonce>",
  mac: "<base 64-encoded HMAC value>"
}

An unsigned token request is identical except that the mac property is omitted. Note that Basic authentication must be used in order to request a token with an unsigned request.

Response body format

If successful, the authorization request returns the JSON stringified representation of an object containing the token:

{
  token: "<token value>",
  issued: <timestamp as ms since epoch>,
  expires: <timestamp as ms since epoch>,
  capability: "<canonical capability text>",
  clientId: "<client ID optional>"
}

Example token requests

Unsigned token request example

curl -X POST "https://rest.ably.io/keys/{{API_KEY_NAME}}/requestToken" \
 --user "{{API_KEY}}" \
 --header "Content-Type: application/json" \
 --data '{
   "keyName": "{{API_KEY_NAME}}",
   "ttl": "3600000",
   "capability":
     "{\"private\":[\"subscribe\",\"publish\",\"presence\"],\"*\":[\"subscribe\"]}",
   "clientId": "unique_identifier",
   "timestamp": {{MS_SINCE_EPOCH}},
   "nonce": "95e543b88299f6bae83df9b12fbd1ecd"
}'

Responds with JSON token:

{
  "token": "{{APP_ID}}.HHZNjgqmC-ACW....truncated",
  "keyName": "{{API_KEY_NAME}}",
  "issued": 1449745478956,
  "expires": 1449749078956,
  "capability":
    "{\"*\":[\"subscribe\"],\"private\":[\"presence\",\"publish\",\"subscribe\"]}",
  "clientId": "unique_identifier"
}

Signed token request example

curl -X POST "https://rest.ably.io/keys/{{API_KEY_NAME}}/requestToken" \
 -H "Content-Type: application/json" \
 --data '{{SIGNED_TOKEN_REQUEST_EXAMPLE}}'

Responds with JSON token:

{
  "token": "{{APP_ID}}.DTSukCRj1lis1sJltr...rhLRBcZgmXLf1FP8wKGrPYkkIs",
  "keyName": "{{API_KEY_NAME}}",
  "issued": 1449745797497,
  "expires": 1449749397497,
  "capability": "{\"*\":[\"*\"]}"
}

Back to top