# REST API Token Request Spec The [Ably REST and Realtime client libraries](https://ably.com/download/) aim to make things as simple as possible so it is not necessary to understand all of the details of how to interact with the service and issue tokens for clients. If you wish to issue Ably-compatible tokens or [Ably TokenRequests](https://ably.com/docs/api/realtime-sdk/types.md#token-request), we recommend you start with the [client library authentication documentation](https://ably.com/docs/auth.md). However, if you are using the [REST API token endpoint directly](https://ably.com/docs/api/rest-api.md#request-token), or if you are creating Ably `TokenRequests` without the use of our client libraries, then the following specification will give you an in-depth understanding of how getting Ably Tokens works. ### API key format API keys are issued and managed from [within your account dashboard](https://faqs.ably.com/setting-up-and-managing-api-keys). The API key string available in your dashboard is structured as a triple `.:`, where: | Component | Description | | --------- | ----------- | | 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 | ### Ably TokenRequest format A request for an Ably Token is made against the [`requestToken`](https://ably.com/docs/api/rest-api.md#request-token) endpoint, with a JSON Ably `TokenRequest` in the request body. The Ably `TokenRequest` comprises: * the `keyName` comprising of the app ID and key ID such as `{{API_KEY_NAME}}` * a capability (i.e. a set of channel names/namespaces and, for each, a set of [operations](#capability-operations)) which should be a subset of the set of capability associated with the key specified in `keyName` * optionally, a `clientId` thus identifying clients using this token and preventing them from identifying themselves with any other `clientId` * optionally, an expiry time or TTL, will default to 1 hour if not specified * a timestamp to ensure the Ably `TokenRequest` is still valid * a unique nonce string, randomly-generated by the client, to ensure the Ably `TokenRequest` cannot be reused A signed Ably `TokenRequest` also contains: * a signature, generated as an HMAC of each of the above components, using the key secret value. A signed Ably `TokenRequest` can be used to request an Ably 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 Ably Token Requests can be issued without a request to Ably, a server with a valid API key can issue Ably `TokenRequests` directly to clients, and clients can in turn generate an Ably Token by sending the Ably `TokenRequest` 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 Ably `TokenRequest` are normalized/canonicalized as follows: | Parameter | Description | Type | | --------- | ----------- | ---- | | keyName | No action required | String | | ttl | The decimal integer representation, without leading zeros, of the requested life of the Ably Token in milliseconds. If none is specified a default of 1 hour is used | Integer | | capability | This is a canonicalized representation of the channel paths and associated [operations](#capability-operations) in the capability. It is a JSON stringified value of a JavaScript object of the form:

`{"channel1": ["operation1a", "operation1b", ...], "channel2": ["operation2a", "operation2b", ...], ...}`

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 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](https://ably.com/docs/channels.md#channel-namespaces)
• for operations, the wildcard character `*` has special meaning. When the operation is exactly `*`, then [all operations](#capability-operations) are supported. Otherwise, a [permitted operation](#capability-operations) string must be provided | String | | 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 | String | | timestamp | The decimal integer representation, without leading zeros, of the time of the of the request in milliseconds since the epoch | Integer | | nonce | An unquoted, un-escaped random string of at least 16 characters | String | ### Capability operations The following capability operations are available for API keys and issued tokens: | Operation | Description | | --------- | ----------- | | subscribe | Can subscribe to messages, objects, and presence state change messages on channels, and get the presence set of a channel | | publish | Can publish messages to channels | | presence | Can register presence on a channel (enter, update and leave) | | object-subscribe | Can subscribe to updates to objects on a channel | | object-publish | Can update objects on a channel | | annotation-subscribe | Can subscribe to individual annotations on a channel | | annotation-publish | Can publish annotations to messages on a channel | | 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 | | channel-metadata | Can get metadata for a channel, and enumerate channels | | privileged-headers | Can set data in the privileged section of the [message extras](https://ably.com/docs/api/realtime-sdk/messages.md#extras) | Read the [capabilities docs](https://ably.com/docs/auth/capabilities.md) to get a more thorough overview of how capabilities can be used to secure your application. While most of these capabilities need to be enabled for the resource you're using them with, as described in [resource names and wildcards](https://ably.com/docs/auth/capabilities.md#wildcards), there are exceptions. The `stats` permission only does something when attached to the wildcard resource `'*'`, or a resource that contains that as a subset, such as `'[*]*'`, since stats are app-wide. The `channel-metadata` permission works both ways. When associated with a specific channel or set of channels it allows you to [query the metadata of a channel](https://ably.com/docs/metadata-stats/metadata/rest.md) to request its status. When associated with the wildcard resource `'*'` it takes on an additional meaning: as well as allowing channel status requests for all channels, it also allows you to [enumerate all active channels](https://ably.com/docs/metadata-stats/metadata/rest.md#enumerate). ### HMAC calculation First the canonicalized request text, constructed as follows: * start with the empty string * for each of the following fields in order: `keyName`, `ttl`, `capabilities`, `clientId`, `timestamp`, `nonce`, even when empty ** append the canonicalized string value for that field ** append a newline (0xa) character. Note that a newline character is added for each field, including any empty client ID string, and the last (nonce) field. The resulting string must then the UTF8-encoded and then HMAC value is computed with [hmac-sha-256](https://tools.ietf.org/html/rfc4868) using the key secret value. The HMAC value is then [base-64 encoded](https://tools.ietf.org/html/rfc4648). ### Request body format In the case of a signed Ably `TokenRequest`, the request body is the JSON stringified representation of the object with the form: ```json { keyName: ":", ttl: , capability: "", clientId: "", timestamp: , nonce: "", mac: "" } ``` An unsigned Ably `TokenRequest` is identical except that the mac property is omitted. Note that [Basic authentication](#basic-authentication) must be used in order to request an Ably Token with an unsigned Ably `TokenRequest`. ### Response body format If successful, the authorization request returns the JSON stringified representation of an object containing the Ably Token: ```json { token: "", issued: , expires: , capability: "", clientId: "" } ``` ### Example Ably TokenRequests ### Unsigned Ably TokenRequest example ```shell curl -X POST "https://main.realtime.ably.net/keys/{{API_KEY_NAME}}/requestToken" \ --user "your-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 TokenDetails: ```json { "token": "{{APP_ID}}.HHZNjgqmC-ACW....truncated", "keyName": "{{API_KEY_NAME}}", "issued": 1449745478956, "expires": 1449749078956, "capability": "{\"*\":[\"subscribe\"],\"private\":[\"presence\",\"publish\",\"subscribe\"]}", "clientId": "unique_identifier" } ``` ### Signed Ably TokenRequest example ```shell curl -X POST "https://main.realtime.ably.net/keys/{{API_KEY_NAME}}/requestToken" \ -H "Content-Type: application/json" \ --data '{{SIGNED_TOKEN_REQUEST_EXAMPLE}}' ``` Responds with TokenDetails: ```json { "token": "{{APP_ID}}.DTSukCRj1lis1sJltr...rhLRBcZgmXLf1FP8wKGrPYkkIs", "keyName": "{{API_KEY_NAME}}", "issued": 1449745797497, "expires": 1449749397497, "capability": "{\"*\":[\"*\"]}" } ```