Authentication

Both the REST client library and the Realtime client library use common authentication mechanisms. The two schemes supported by Ably are Basic Authentication, which uses your private API key, and Token Authentication, which relies on a token you request from Ably, or a “token request” that you sign and issue from your servers to your clients. Token Authentication, in most cases, is the recommended strategy client-side as it provides robust access control and stringent security measures.

Quick intro to Ably Authentication

Using Basic Authentication
Every application has one or more private API keys that you set up and configure via the dashboard. It is recommended that private API keys are never shared with untrusted parties, and as such, should only be used by your trusted secure servers when authenticating with Ably. In the example below, a server is publishing a message using the Ably REST library over HTTP, which in turn is using an API key for Basic Authentication.


Server-side basic auth diagram

Using Token Authentication
Client-side devices should generally be considered untrusted, and as such, it is important that you minimize the impact of any credentials being compromised on those devices. The recommended strategy therefore for authenticating client-side devices is to issue them with time-limited tokens with only the privileges (capabilities) that are needed for that device. It is therefore the responsibility of the developer to identify each device before issuing a token. In the example below, we show you a a typical authentication workflow for client-side devices.


Client-side basic auth diagram

Tutorials & Examples

If you would like to just dive in and see an example of Ably authentication in code, then take a look at our client-server authentication tutorials.

Selecting an authentication mechanism

The following guidance aims to help you choose which system to use in any given situation. When deciding, it is recommended to bear in mind the principle of least privilege: a client should ideally only possess the credentials and rights that it needs to accomplish what it wants; this way, if the credentials are compromised, the rights that can be abused by an attacker are minimized.

Basic authentication is appropriate where:

  • the script, program or system holding the key is not exposed; for example, typically on one of your own servers. A key should not be embedded in a script in a public-facing web page
  • a secure, unmediated connection exists between the client and the Ably service. Keys should only really be sent over a TLS connection (that’s either an HTTPS connection, or an encrypted realtime connection). A key should not be used over a proxied connection unless the proxy is trusted
  • access needs to be granted selectively to groups of clients to specific channels or channel namespaces, but only a small number of such access control groups need to be established
  • clients are trusted to assume any client ID in the operations they are permitted to perform
  • you don’t wish to run your own server to control access

Token authentication is appropriate when:

  • there is a risk of exposure of the client’s credentials, either directly or over an insecure, or insecurely proxied, connection
  • a client is only intended to have use of the service for a limited period of time
  • a client needs to have the ability to identify itself (authenticate its specific identity with a clientId), but cannot be trusted sufficiently not to masquerade as a different client identity
  • fine-grained access needs to be given on a per-client basis to specific channels and/or capabilities
  • there may be a requirement to be able to revoke the rights of a client individually

Note that many applications will most naturally use a mixed strategy: one or more trusted application servers will use basic authentication to access the service and issue tokens over HTTPS, whereas remote browsers and devices will use individually issued tokens.

Both types of authentication rely on a series of API keys to be set up for each application. Each key is configured via the dashboard and exposed as a single string such as xVLyHw.jTeUnA:CD9xtVBJSuqJDvW6 which contains information that identifies the key as well as containing the “secret” key value.

Basic Authentication explained

Basic authentication is the simplest way to authenticate with Ably using an API key string when instancing the client library. It is as simple as:

var ably = new Ably.Realtime({ key: 'xVLyHw.jTeUnA:CD9xtVBJSuqJDvW6' });

Process used by client libraries connecting with basic auth:


Basic authentication process diagram

Whilst Basic Authentication is simple, we recommend it is only used server-side as it suffers from a number of problems:

  • the secret is passed directly by the client to Ably, so it is not permitted for connections that are not over TLS (HTTPS or non-encrypted realtime connections) to prevent the key secret being intercepted
  • all of the configured capabilities of the key are implicitly possible in any request, and clients that legitimately obtain this key may then abuse the rights for that key
  • clients are permitted to use any client ID in all operations with Ably. As such, a client ID in messages and presence cannot be trusted as any client using Basic Authentication can masquerade with any client ID

Token Authentication explained

Token authentication is the recommended authentication scheme for client devices as tokens are short-lived and may more readily be distributed to clients where there is a risk of compromise. Tokens may also be issued with a particular scope – such as a limited set of access rights or capabilities or be limited to a specific identity. Tokens are most commonly issued server-side and may be signed using a private API key which guarantees to Ably that the token request is authentic and issued by a trusted issuer.

Token authentication is the default authentication scheme when a client library is instanced with any of the following options:

Token authentication is typically done in one of two ways:

Signed token request is created by your servers and passed to clients

Using our REST or Realtime client libraries, a signed token request is generated from your servers and handed to the client-side client library. The client-side client library then uses that signed token request to request a token from Ably and subsequently authenticate using that token. This is the recommended approach for authentication as: a signed token request can be generated securely by your servers without communicating with Ably; your secret API key is never shared with Ably or your clients; signed token requests cannot be tampered with, must be used soon after creation and can only be used once. This process is depicted in the following diagram:


Signed token request auth process diagram

An example of creating a token request can be seen below:

var ably = new Ably.Rest({ key: 'xVLyHw.jTeUnA:CD9xtVBJSuqJDvW6' });
ably.createTokenRequest({ clientId: 'client@example.com' }, null, function(err, tokenRequest) {
  /* tokenRequest => {
       "capability": "{\"*\":[\"*\"]}",
       "clientId": "client@example.com",
       "keyName": "xVLyHw.jTeUnA",
       "nonce": "5576521221082658",
       "timestamp": _VAR_MS_SINCE_EPOCH_VAR_,
       "mac": "GZRgXssZDCegRV....EXAMPLE"
     } */
});

Token is issued by your servers and passed to clients

Using our REST or Realtime client libraries, a token is requested by your servers from Ably and then handed to the client-side client library. The client-side client library then uses that token to authenticate with Ably. This is an alternative approach for authentication that allows you to issue tokens directly as opposed to providing signed token requests from your servers. The advantage for clients is it saves one round trip request as they do not need to request a token themselves. The disadvantage is that your servers must communicate with Ably each time a token is required. This process is depicted in the following diagram:


Token auth process diagram

An example of issuing a token can be seen below:

var ably = new Ably.Rest({ key: 'xVLyHw.jTeUnA:CD9xtVBJSuqJDvW6' });
ably.requestToken({ clientId: 'client@example.com' }, function(err, token) {
  /* token => {
       "token": "xVLyHw.Dtxd9tuz....EXAMPLE",
       "capability": "{\"*\":[\"*\"]}"
       "clientId": "client@example.com",
       "expires": 1449745287315,
       "keyName": "xVLyHw.jTeUnA",
       "issued": 1449741687315,
     } */
});

Capabilities and Token Security explained

API keys, like tokens, have a set of capabilities assigned to them that specify which operations (such as subscribe or publish) can be performed on which channels. However, unlike tokens, API keys are long-lived, secret and typically not shared with un-trusted clients.

API keys and their capabilities are configured using the dashboard, they cannot be added or removed programmatically. Tokens on the other hand are designed to be shared with un-trusted clients, are short-lived, and significantly, they are configured and issued programmatically using the Ably client libraries or directly from the Ably REST API. See selecting an authentication scheme to understand why token authentication, in most cases, is the preferred authentication scheme.

Tokens are issued from an existing API key, and their capabilities can, at most, match the capabilities of the issuing API key. If an API key must be shared with a third party, then it is recommended that the principle of least privilege is considered assigning only the capabilities needed by that third party. Thus, any Ably requests authenticated using that API key or tokens issued from that API key, will be restricted to the capabilities assigned to the key.

Capabilities for tokens are determined as follows:

  • If no capability is specified in the token request, then the token will be given the full set of capabilities assigned to the issuing key, see example;
  • If a set of capabilities are requested, then the token will be assigned the intersection of the requested capability and the capability of the issuing key, see example;
  • If a set of capabilities are requested, and the intersection between those and the API key’s capabilities is empty (i.e. they are entirely incompatible), then the token request will result in an error, see example.

See capability operations below for the complete set of supported operations on a channel.

Resource names and wildcards

Capabilities are a map from resources to a list of operations. Each resource can match a single channel e.g. channel, or multiple channels using wildcards (*). Wildcards can only replace whole segments (segments are delimited by :) of the resource name. A wildcard at the end of the name can replace arbitrarily many segments. For example:

  • A resource of * will match any channel
  • A resource of namespace:* will match any channel in the namespace namespace, including namespace:channel, and namespace:channel:other
  • A resource of foo:*:baz will match foo:bar:baz, but not foo:bar:bam:baz
  • A resource of foo:* will match foo:bar, foo:bar:bam, foo:bar:bam:baz etc., as the wildcard as at the end
  • A resource of foo* (without a colon!) will only match the single channel literally called foo*, which probably isn’t what you want

A resource can also be a queue, in which case it will start with [queue], e.g. [queue]appid-queuename. (This is unambiguous as channel names may not begin with a [). Similar wildcard rules apply, e.g. [queue]* will match all queues.

You can also have a resource name of [*]*, which will match both all queues and all channels.

Wildcards are also supported for operations, by requesting an operations list of ['*'].

Capabilities example in code

If you want to see some live code examples of how capabilities work, take a look at our capabilities example.

Token request without capabilities example

Given an API key exists with the following capabilities:

{
  "chat": ["publish", "subscribe", "presence"],
  "status": ["subscribe"]
}

If token is requested without requiring any capabilities:

auth.requestToken(tokenCallback)

Then the token request is treated as requesting all capabilities, i.e. {"[*]*":["*"]}), and all capabilities of the API key are assigned to the token. The capabilities for the issued token would be as follows:

{
  "chat": ["publish", "subscribe", "presence"],
  "status": ["subscribe"]
}

Token request with intersection of capabilities example

Given an API key exists with the following capabilities:

{
  "chat:*": ["publish", "subscribe", "presence"],
  "status": ["subscribe", "history"],
  "alerts": ["subscribe"]
}

And a token is requested with the following explicit capabilities:

auth.requestToken({ capability: {
  "chat:bob": ["subscribe"], // only "subscribe" intersects
  "status": ["*"], // "*"" intersects with "subscribe"
  "secret": ["publish", "subscribe"] // key does not have access to "secret" channel
}}, tokenCallback)

Then Ably will intersect the API key’s capabilities and the requested capabilities i.e. Ably will satisfy the token request’s capabilities as far as possible based on the capability of the issuing API key. The capabilities for the issued token would be as follows:

{
  "chat:bob": ["subscribe"],
  "status": ["subscribe", "history"]
}

Token request with incompatible capabilities

Given an API key exists with the following capabilities:

{
  "chat": ["*"]
}

And a token is requested with the following explicit capabilities:

auth.requestToken({ capability: {
  "status": ["*"]
}}, tokenCallback)

Then Ably will be unable to issue a token because the intersection of the requested capabilities and the API key’s capabilities is empty – they are entirely incompatible. In the example above, requestToken will call the callback with an error.

Token request with wider channel scope than the key

Given an API key exists with the following capabilities:

{
  "chat:team:*": ["publish"]
}

And a token is requested with the following explicit capabilities:

auth.requestToken({ capability: {
  "chat:*": ["*"],
  "status": ["*"]
}}, tokenCallback)

Then Ably will intersect the API key’s capabilities and the requested capabilities i.e. Ably will satisfy the token request’s capabilities as far as possible based on the capability of the issuing API key. The capabilities for the issued token would be as follows:

{
  "chat:team:*": ["publish"]
}

See a working capabilities example.

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.

Understanding Identified clients

When a client is authenticated and connected to Ably, they are considered to be an authenticated client. However, whilst an authenticated client has a verifiable means to authenticate with Ably, they do not necessarily have an identity. When a client is assigned a trusted identity (i.e. a client ID), then they are considered to be an identified client and for all operations they perform with the Ably service, their client ID field will be automatically populated and can be trusted by other clients.

For example, assuming you were building a chat application and wanted to allow clients to publish messages and be present on a channel. If each client is assigned a trusted identity by your server, such as a unique email address or UUID, then all other subscribed clients can trust any messages or presence events they receive in the channel as being from that client. No other clients are permitted to assume a client ID that they are not assigned in their token or token request, that is they are unable to masquerade as another client ID.

In Ably a client can be identified with a client ID in two ways:

  • if the client is authenticated with a token that is issued for that client ID;
  • if the client claims that client ID (as part of ClientOptions in the constructor) and is authenticated with a token that is issued for a wildcard client ID (a special token privilege that allows any client identity to be assumed)

We encourage customers to always issue tokens to clients so that they authenticate using the short-lived token and do not have access to a customer’s private API keys. Since the customer can then control the client ID that may be used by any of its clients, all other clients can rely on the validity of the client ID in published messages and of members present in presence channels.

The following Javascript example demonstrates how to issue a token with an explicit client ID that, when used by a client, will then be considered an identified client.

var realtime = new Ably.Rest({ key: 'xVLyHw.jTeUnA:CD9xtVBJSuqJDvW6' });
realtime.auth.createTokenRequest({ clientId: 'Bob' }, function(err, tokenRequest) {
  /* ... issue the TokenRequest to a client ... */
})

Tokens

In the documentation, references to tokens typically refer to both TokenDetails object that contain the token string or the token string itself. TokenDetails objects are obtained when requesting tokens from the Ably service and contain not only the token string in the token attribute, but also contain attributes describing the properties of the token.

TokenDetails type

TokenDetails is a type providing details of the token string and its associated metadata.

PropertiesMembersAttributes

tokenToken
The token itself. A typical token string may appear like xVLyHw.AvtQgVwOGRJYDmB8q1dP0Fui1JzjyWLdjo3dFpEtFExSILlVvI
Type: String
expiresExpires
The time (in milliseconds since the epoch)The time at which this token expires
Type: IntegerLong IntegerDateTimeOffsetTimeNSDate
issuedIssued
The time (in milliseconds since the epoch)The time at which this token was issued
Type: IntegerLong IntegerDateTimeOffsetTimeNSDate
capabilityCapability
The capability associated with this token. The capability is a a JSON stringified canonicalized representation of the resource paths and associated operations. Read more about authentication and capabilities
Type: StringCapability
clientIdclient_idClientId
The client ID, if any, bound to this token. If a client ID is included, then the token authenticates its bearer as that client ID, and the token may only be used to perform operations on behalf of that client ID. The client is then considered to be an identified client
Type: String

Methods

expired?
True when the token has expired
Type: Boolean

Methods

is_expired()
True when the token has expired
Type: Boolean

Methods

IsValidToken()
True if the token has not expired
Type: Boolean

TokenDetails constructors

TokenDetails.fromJsonTokenDetails.from_json

TokenDetails.fromJson(String json) → TokenDetailsTokenDetails.from_json(String json) → TokenDetails

TokenDetails.fromJson(Object json) → TokenDetailsTokenDetails.from_json(Object json) → TokenDetails

A static factory method to create a TokenDetails from a deserialized TokenDetails-like object or a JSON stringified TokenDetails. This method is provided to minimize bugs as a result of differing types by platform for fields such as timestamp or ttl. For example, in Ruby ttl in the TokenDetails object is exposed in seconds as that is idiomatic for the language, yet when serialized to JSON using to_json it is automatically converted to the Ably standard which is milliseconds. By using the fromJson method when constructing a TokenDetails, Ably ensures that all fields are consistently serialized and deserialized across platforms.

Parameters

json
a TokenDetails-like deserialized object or JSON stringified TokenDetails.
Type: Object, String

Returns

A TokenDetails object


Get started now with our free plan

It includes 100 peak connections, 3 million messages per month, and loads of features.

Create your free account