Token auth
Token authentication uses a trusted device with an API key to issue time-limited tokens to untrusted clients. Tokens have a limited set of access rights, known as capabilities, and can have a specific identity using a clientId
.
Token authentication is the recommended authentication method to use client-side as it provides more fine-grained access control and limits the risk of credentials being exposed.
Any of the following cause an SDK to use token authentication:
- An
authUrl
orauthCallback
is provided that returns an Ably-compatible token or an AblyTokenRequest
useTokenAuth
is true- A
token
ortokenDetails
property is provided
Providing a literal token
or tokenDetails
is typically used for testing: since tokens are short-lived, in production you typically want to use an authentication method that allows the client library to renew the token automatically before the current token expires.
Authentication using tokens can be achieved requesting and issuing Ably Tokens or passing a JSON Web Tokens (JWT) to the Ably service.
Token refresh
One of the important benefits of using an Ably SDK is that the automatic refresh of tokens will be handled for you.
To use automatic refresh of tokens, provide either an authUrl
or an authCallback
. When the token is near to expiry the authUrl
or authCallback
is invoked and a new token is automatically requested.
An authURL
is recommended for use with web-based clients as they can easily utilize cookies and other web-only features. For non-web clients, authCallback
is the recommended strategy.
authUrl
You can specify an authUrl
when you create the Ably client. For example:
const realtime = new Ably.Realtime({ authUrl: '/auth' });
CopyCopied!
The client will obtain a token, JWT, or tokenRequest from the URL and use it to authenticate requests to Ably. Before token expiry, a request for a new token will be made automatically by the client to the authUrl
.
authCallback
You can specify an authentication callback function when you create the Ably client. Inside authCallback
, you can make a network request to your servers to generate the tokenRequest
. For example:
const ablyClient = new Realtime({
authCallback: async (tokenParams, callback) => {
let tokenRequest;
try {
tokenRequest = await obtainTokenRequest(); // Make a network request to your server
} catch (err) {
callback(err, null);
return;
}
callback(null, tokenRequest);
}
});
CopyCopied!
The tokenParams
argument in authCallback
is available for convenience, allowing you to see the capabilities, clientId
, and other details requested by the client. However, tokenParams
should not be trusted or used to generate the tokenRequest
on the server side. Instead it is recommended that your createTokenRequest
API authenticates clients separately, for example based on cookies, headers, or HTTP body.
AuthOptions
Use properties set with AuthOptions
to override the default authentication values set when instantiating a client. You can also embed AuthOptions
into your ClientOptions
while instantiating.
There are several AuthOptions
you can specify along with authUrl
and authCallback
:
authMethod
:- whenauthUrl
is called, the defaultGET
method will be used, unlessPOST
is specified.authHeaders
:- allows you to pass additional headers as required, depending on your use case.authParams
:- allows you to pass additional query parameters, depending on your use case.
The following is an example of passing AuthOptions
:
const realtime = new Ably.Realtime({
authUrl: "/auth",
authMethod: "POST",
authParams: { p1: param1, b: param2},
authHeaders: {h1: header1, h2: header2}
});
CopyCopied!
Ably Tokens
Ably Tokens can be used to authenticate with Ably in the following ways:
- Ably TokenRequest is created by your servers and passed to clients.
- An Ably Token is issued by your servers and passed to clients.
Note that the machine on which you are running your auth server should have an accurate clock, as tokens and TokenRequest
contain a timestamp. You can use an NTP daemon, or if you are not able to control your server’s clock, you can wish to use the queryTime
auth option.
Ably TokenRequest
Using an Ably SDK, a TokenRequest
is generated from your server and returned to the client-side SDK instance. The client-side SDK instance then uses the TokenRequest
to request an Ably Token from Ably, and subsequently authenticates using that Ably Token.
This is the recommended approach for client-side authentication, for the following reasons:
- An Ably
TokenRequest
can be generated securely by your servers without communicating with Ably. - Your secret API key is never shared with Ably or your clients.
- An Ably
TokenRequest
cannot be tampered with due to being signed, must be used soon after creation, and can only be used once.
The process used by Ably SDKs to authenticate with Ably using a TokenRequest
is illustrated in the following diagram:
The following is an example of creating an Ably TokenRequest
:
const ably = new Ably.Rest({ key: '<loading API key, please wait>' });
const tokenRequest = await ably.auth.createTokenRequest({ clientId: '[email protected]' });
Demo OnlyCopyCopied!
Clients can pass this server-side generated TokenRequest
to Ably to authenticate with Ably automatically.
Ably Token
Using an Ably SDK, an Ably Token is requested by your servers from Ably and then passed to the client-side SDK instance. The client-side SDK instance then uses that Ably Token to authenticate with Ably. This is an alternative approach for authentication that enables you to issue“Ably Tokens directly as opposed to providing Ably TokenRequests
from your servers.
The advantage for clients is that it saves one round trip request as they do not need to request an Ably Token themselves. The disadvantage is that your servers must communicate with Ably each time an Ably Token is required.
The process used by Ably SDKs to authenticate with Ably using an Ably Token is illustrated in the following diagram:
The following is an example of issuing an Ably Token from a server:
const ably = new Ably.Rest({ key: '<loading API key, please wait>' });
const tokenDetails = await ably.auth.requestToken({ clientId: '[email protected]' });
Demo OnlyCopyCopied!
JSON Web Tokens (JWT)
JSON Web Tokens (JWT) can be used to authenticate with Ably in the following ways:
- Ably JWT is created by your servers and passed to clients.
- Ably Token is embedded in a JWT from your server and passed to clients.
Note that the machine on which you are running your auth server should have an accurate clock, as tokens contain a timestamp. You can use an NTP daemon, or if you are not able to control your server’s clock, you can wish to use the queryTime
auth option.
JWT using your API key
It is possible to use a JWT as a form of token for authentication with Ably, so long as it is structured appropriately, in what will be referred to as an Ably JWT. It is possible for an Ably JWT to contain claims indicating its clientId
, capabilities and expiry – in an analogous way to an Ably Token – and it is signed with the applicable Ably API key’s secret part.
This is similar to signing an Ably TokenRequest
, but the client does not need to request an Ably Token, instead being able to use the Ably JWT as a token in itself. Any compliant third-party JWT library may be used to create the JWT without requiring the token to be issued by Ably. This can be useful for situations where an Ably client library is not available, such as an embedded device connecting to Ably via MQTT.
The process used by Ably SDKs to authenticate with Ably using JWTs is illustrated in the following diagram:
The following is an example of creating an Ably JWT:
var header = {
"typ":"JWT",
"alg":"HS256",
"kid": "{{API_KEY_NAME}}"
}
var currentTime = Math.round(Date.now()/1000);
var claims = {
"iat": currentTime, /* current time in seconds */
"exp": currentTime + 3600, /* time of expiration in seconds */
"x-ably-capability": "{\"*\":[\"*\"]}"
}
var base64Header = btoa(header);
var base64Claims = btoa(claims);
/* Apply the hash specified in the header */
var signature = hash((base64Header + "." + base64Claims), "{{API_KEY_SECRET}}");
var ablyJwt = base64Header + "." + base64Claims + "." + signature;
CopyCopied!
At present Ably does not support asymmetric signatures based on a key pair belonging to a third party.
Embed an Ably Token in a JWT
If a system has an existing JWT scheme, it’s possible to embed an Ably Token as a claim within it.
The JWT and embedded token need to meet the following requirements:
- The embedded token is an Ably Token.
- The embedded token is included under the
x-ably-token
key in the JOSE Header, or if using JWS, the embedded token is included using thex-ably-token
claim in the payload. - The expiry time of the embedded token must not be earlier than the outer JWT‘s expiry time (
exp
claim). Ably will reject any JWT if it is unencrypted and itsexp
claim is later than the expiry of the enclosed token. This helps to ensure that tokens are renewed prior to expiry.
The process used by Ably SDKs to authenticate with Ably using an Ably Token embedded in a JWT is illustrated in the following diagram:
The following is an example of issuing an Ably Token inside the of header of a JWT. Note that the authenticity of the JWT will not be checked, due to Ably not having access to your SECRET
key.
const ably = new Ably.Rest({ key: '<loading API key, please wait>' });
const tokenDetails = await ably.auth.requestToken({ clientId: '[email protected]' });
const header = {
"typ":"JWT",
"alg":"HS256",
"x-ably-token": tokenDetails.token
}
const claims = {
"exp": currentTime + 3600
}
const base64Header = btoa(header);
const base64Claims = btoa(claims);
/* Apply the hash specified in the header */
const signature = hash((base64Header + "." + base64Claims), SECRET);
const jwt = base64Header + "." + base64Claims + "." + signature;
/* Send jwt to client */
Demo OnlyCopyCopied!
When to use token auth
Ably recommends that token authentication is used client-side for the following reasons:
- Tokens ensure that an Ably API key isn’t exposed in client applications.
- Tokens are short-lived so there is only a short period of time during which a compromised token can be used.
- Tokens provide more fine-grained access control, which also limits the area of exposure a compromised token can access.