# Token auth
Token authentication uses a trusted device with an [API key](https://ably.com/docs/auth.md#api-keys) to issue time-limited tokens to untrusted clients. Tokens have a limited set of access rights, known as [capabilities](https://ably.com/docs/auth/capabilities.md), and can have a specific [identity](https://ably.com/docs/auth/identified-clients.md) using a `clientId`.
Token authentication is the recommended authentication method to use 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.
* Tokens support functionality not available with Basic auth, such as user claims.
## Choosing a token mechanism
Ably supports two token formats: JWT, the primary and recommended format, and Ably tokens, a legacy format. Each format is associated with a different flow for issuing tokens to clients.
### JWT format (recommended)
[JWTs](https://ably.com/docs/auth/token/jwt.md) are the recommended approach for most applications:
- No Ably SDK required on your server. Any JWT library works.
- Supports [channel-scoped claims](https://ably.com/docs/auth/token/jwt.md#channel-claims) for trusted metadata
- Supports [per-connection rate limits](https://ably.com/docs/auth/token/jwt.md#rate-limits)
- Stateless and ideal for serverless environments
Use JWTs unless your capability list is very large (JWTs must fit within HTTP header limits, typically around 8 KB) or you need to keep capabilities confidential (clients can decode JWTs).
### Native Ably Token formats
[Ably tokens](https://ably.com/docs/auth/token/ably-tokens.md) are an alternative mechanism that predates Ably's JWT support. Unlike JWTs, Ably tokens are not generated by a client but are issued by the Ably service via a `/requestToken` endpoint. Integrating Ably tokens is more involved, and they do not support all functionality available with JWTs. Use native [Ably Tokens](https://ably.com/docs/auth/token/ably-tokens.md) when JWTs are not suitable:
- TokenRequest: Server creates a signed request locally, client exchanges it with Ably
- Ably Token (direct): Server requests token from Ably, passes it to client
- [Embedded Token JWT](#embedded): If you need a single app token format, you can wrap Ably credentials in an outer JWT used by your existing auth system
## How token authentication works
1. Your client calls `authUrl` or `authCallback` to request a token from your server.
2. Your server validates the client and returns a token (JWT, TokenRequest, or Ably Token).
3. The client uses this token to authenticate with Ably.
4. Tokens are short-lived and expire after a set period.
5. The client SDK automatically requests a new token before expiry, ensuring uninterrupted connectivity.
### Token refresh
One of the important benefits of using an Ably SDK is that automatic token refresh is handled for you.
When you provide either an `authUrl` or an `authCallback`, the SDK automatically:
1. Calls your auth endpoint when connecting
2. Requests a new token before the current token expires
3. Maintains the connection seamlessly during refresh
### Token TTL limits
Ably enforces maximum TTL (time-to-live) limits:
- Access tokens: Maximum TTL of 24 hours.
- Device tokens (for push notifications): Maximum TTL of 5 years.
- Revocable tokens: Maximum TTL of 1 hour. A token is revocable if [token revocation](https://ably.com/docs/auth/revocation.md) has been enabled for the API key used to issue it.
## Dynamic channel access control
Token authentication allows you to dynamically change a client's channel access permissions without disconnecting. Use the [`authorize()`](https://ably.com/docs/api/realtime-sdk/authentication.md#authorize) method to re-authenticate with updated [capabilities](https://ably.com/docs/auth/capabilities.md).
When you call `client.auth.authorize()`:
1. The client obtains a new token with different capabilities from your authentication server.
2. The new token is sent to Ably using the existing realtime connection.
3. The updated permissions are automatically applied without disconnecting.
### Use cases
You can trigger re-authentication in response to:
- Detect error code [40160](https://ably.com/docs/platform/errors/codes.md#40160) when a client attempts to attach to a channel.
- Instruct clients to re-authenticate via Ably channels or other communication methods.
The following example shows how to re-authenticate with updated capabilities:
#### Javascript
```
// Re-authenticate to get new capabilities
try {
const newTokenDetails = await client.auth.authorize({
clientId: 'user123',
// Your auth server will return a token with updated capabilities
});
console.log('Successfully updated permissions');
} catch (error) {
console.error('Failed to re-authenticate:', error);
}
```
#### Nodejs
```
// Re-authenticate to get new capabilities
try {
const newTokenDetails = await client.auth.authorize({
clientId: 'user123',
// Your auth server will return a token with updated capabilities
});
console.log('Successfully updated permissions');
} catch (error) {
console.error('Failed to re-authenticate:', error);
}
```
#### Python
```
# Re-authenticate to get new capabilities
# Your auth server will return a token with updated capabilities
new_token_details = await client.auth.authorize(
token_params={'client_id': 'user123'}
)
print('Successfully updated permissions')
```
#### Java
```
// Re-authenticate to get new capabilities
try {
Auth.TokenParams tokenParams = new Auth.TokenParams();
tokenParams.clientId = "user123";
// Your auth server will return a token with updated capabilities
Auth.TokenDetails newTokenDetails = client.auth.authorize(tokenParams, null);
System.out.println("Successfully updated permissions");
} catch (AblyException e) {
System.out.println("Failed to re-authenticate: " + e.getMessage());
}
```
#### Csharp
```
// Re-authenticate to get new capabilities
try {
TokenParams tokenParams = new TokenParams { ClientId = "user123" };
// Your auth server will return a token with updated capabilities
TokenDetails newTokenDetails = await client.Auth.AuthorizeAsync(tokenParams);
Console.WriteLine("Successfully updated permissions");
} catch (AblyException e) {
Console.WriteLine("Failed to re-authenticate: " + e.Message);
}
```
#### Ruby
```
# Re-authenticate to get new capabilities
# Your auth server will return a token with updated capabilities
client.auth.authorize(client_id: 'user123') do |new_token_details|
puts 'Successfully updated permissions'
end
```
#### Swift
```
// Re-authenticate to get new capabilities
let tokenParams = ARTTokenParams(clientId: "user123")
// Your auth server will return a token with updated capabilities
client.auth.authorize(tokenParams, options: nil) { newTokenDetails, error in
guard let newTokenDetails = newTokenDetails else {
print("Failed to re-authenticate: \(error!)")
return
}
print("Successfully updated permissions")
}
```
#### Objc
```
// Re-authenticate to get new capabilities
ARTTokenParams *tokenParams = [[ARTTokenParams alloc] initWithClientId:@"user123"];
// Your auth server will return a token with updated capabilities
[client.auth authorize:tokenParams options:nil callback:^(ARTTokenDetails *newTokenDetails, NSError *error) {
if (error) {
NSLog(@"Failed to re-authenticate: %@", error);
} else {
NSLog(@"Successfully updated permissions");
}
}];
```
#### Kotlin
```
// Re-authenticate to get new capabilities
val tokenParams = Auth.TokenParams()
tokenParams.clientId = "user123"
// Your auth server will return a token with updated capabilities
val newTokenDetails = client.auth.authorize(tokenParams, null)
println("Successfully updated permissions")
```
#### Go
```
// Re-authenticate to get new capabilities
// Your auth server will return a token with updated capabilities
client.Auth.Authorize(context.Background(), &ably.TokenParams{
ClientID: "user123",
})
```
#### Flutter
```
// Re-authenticate to get new capabilities
// Your auth server will return a token with updated capabilities
final newTokenDetails = await client.auth.authorize(
tokenParams: ably.TokenParams(clientId: 'user123'),
);
print('Successfully updated permissions');
```
For security purposes, handle non-compliant clients by:
- Set shorter token TTL expiry times to force frequent re-authentication (minimum 10 minutes recommended).
- Use the [token revocation API](https://ably.com/docs/auth/revocation.md) to immediately invalidate tokens.
## Server clock requirements
The machine that is used to sign JWTs (or sign TokenRequests, in the case of using Ably tokens) should have an accurate clock, as tokens and `TokenRequest` contain a timestamp. You can use an [NTP daemon](https://en.wikipedia.org/wiki/Ntpd), or if you are not able to control your server's clock, you can use the `queryTime` [auth option](https://ably.com/docs/api/rest-sdk/types.md#auth-options).
## Access restrictions
### Token-based client validation
Token authentication enables you to validate client characteristics (such as origin, IP address, cookies, or any other client features) in your authentication server before issuing tokens. This provides flexible access control as you can implement any validation logic in your auth server as part of the token issuance process.
### API key restrictions
For cases where token authentication is impractical, Ably can add origin or IP address restrictions directly to API keys. However, this approach has significant limitations:
- Available to enterprise support packages only.
- Changes require contacting [Ably support](https://ably.com/support); restrictions cannot be updated programmatically or self-served.
- Not a security boundary: origin headers can be easily spoofed, especially outside browser contexts.
- Permissive fallback: requests with no origin header are allowed when origin restrictions are set.
Token authentication with server-side validation is recommended over API key restrictions wherever possible.
## Embedded Token JWT
If you have an existing [JWT](https://jwt.io/) authentication system, you can embed an Ably Token as a claim within your JWT. This allows you to use your existing auth infrastructure while providing Ably authentication.

### Requirements
* The embedded token must be an Ably Token (not a JWT).
* The embedded token is included under the `x-ably-token` key in the [JOSE Header](https://tools.ietf.org/html/rfc7519), or if using JWS, in the payload claims.
* 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 its `exp` claim is later than the expiry of the enclosed token.
### When to use
- You already have a JWT-based authentication system
- You want to avoid changing your client authentication flow
- You need the capability hiding features of Ably Tokens but want to use your existing JWT infrastructure
### Server examples
#### Javascript
```
const ably = new Ably.Rest({ key: 'your-api-key' });
const tokenDetails = await ably.auth.requestToken({ clientId: 'client@example.com' });
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 */
```
#### Python
```
rest = AblyRest(key='your-api-key')
token_request_data = {
'clientId': 'client@example.com',
}
token_details = await rest.auth.request_token(token_params=token_request_data)
header = {
"typ": "JWT",
"alg": "HS256",
"x-ably-token": token_details.token
}
claims = {
"exp": int(time.time()) + 3600
}
base64_header = base64.urlsafe_b64encode(bytes(json.dumps(header), 'utf-8')).decode('utf-8')
base64_claims = base64.urlsafe_b64encode(bytes(json.dumps(claims), 'utf-8')).decode('utf-8')
signature = hashlib.sha256((base64_header + "." + base64_claims + "{{API_KEY_SECRET}}").encode('utf-8')).digest()
signature_base64 = base64.urlsafe_b64encode(signature).decode('utf-8')
jwt_token = base64_header + "." + base64_claims + "." + signature_base64
```
#### Java
```
ClientOptions options = new ClientOptions("your-api-key");
AblyRest rest = new AblyRest(options);
Auth.TokenParams tokenParams = new Auth.TokenParams();
tokenParams.clientId = "client@example.com";
Auth.TokenDetails tokenDetails = rest.auth.requestToken(tokenParams, null);
Map headerClaims = new HashMap<>();
headerClaims.put("typ", "JWT");
headerClaims.put("alg", "HS256");
headerClaims.put("x-ably-token", tokenDetails.token);
// Time of expiration in seconds (an hour)
long currentTimeInSeconds = System.currentTimeMillis() / 1000;
// Define the claims
Map claims = new HashMap<>();
claims.put("exp", currentTimeInSeconds + 3600);
// Create the JWT
Algorithm algorithm = Algorithm.HMAC256("{{API_KEY_SECRET}}");
String token = JWT.create()
.withHeader(headerClaims)
.withPayload(claims)
.sign(algorithm);
```
#### Php
```
$rest = new Ably\AblyRest(
['key' => 'your-api-key']
);
$tokenDetails = $rest->auth->requestToken(
['clientId' => 'client@example.com']
);
$header = [
'typ' => 'JWT',
'alg' => 'HS256',
'x-ably-token' => $tokenDetails->token
];
$currentTime = time();
$claims = [
'exp' => $currentTime + 3600 /* time of expiration in seconds (an hour) */
];
$base64Header = base64_encode(json_encode($header));
$base64Claims = base64_encode(json_encode($claims));
$secret = 'YOUR_SECRET';
$signature = hash_hmac('sha256', $base64Header . '.' . $base64Claims, $secret, true);
$base64Signature = base64_encode($signature);
$jwt = $base64Header . '.' . $base64Claims . '.' . $base64Signature;
```
#### Go
```
rest, err := ably.NewREST(ably.WithKey("your-api-key"))
if err != nil {
log.Fatalf("Failed to create Ably REST client: %v", err)
}
// Request a token
tokenParams := &ably.TokenParams{
ClientID: "client@example.com",
}
tokenDetails, err := rest.Auth.RequestToken(context.Background(), tokenParams)
if err != nil {
log.Fatalf("Failed to request token: %v", err)
}
// Get the current time in seconds
currentTime := time.Now().Unix()
// Create JWT header
header := map[string]interface{}{
"typ": "JWT",
"alg": "HS256",
"x-ably-token": tokenDetails.Token,
}
// Create JWT claims
claims := map[string]interface{}{
"exp": currentTime + 3600, // time of expiration in seconds
}
// Encode header to base64
headerJSON, err := json.Marshal(header)
if err != nil {
log.Fatalf("Failed to marshal header: %v", err)
}
base64Header := base64.RawURLEncoding.EncodeToString(headerJSON)
// Encode claims to base64
claimsJSON, err := json.Marshal(claims)
if err != nil {
log.Fatalf("Failed to marshal claims: %v", err)
}
base64Claims := base64.RawURLEncoding.EncodeToString(claimsJSON)
// Create the signature
dataToSign := base64Header + "." + base64Claims
h := hmac.New(sha256.New, []byte("SECRET"))
h.Write([]byte(dataToSign))
signature := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
// Combine the parts to form the final JWT
jwt := base64Header + "." + base64Claims + "." + signature
log.Println("JWT:", jwt)
// Send JWT to client (for demonstration, print it here)
```
#### Flutter
```
final clientOptions = ably.ClientOptions(
key: 'your-api-key',
);
final rest = ably.Rest(options: clientOptions);
const tokenParams = ably.TokenParams(
clientId: 'client@example.com',
);
final tokenDetails = await rest.auth.requestToken(
tokenParams: tokenParams
);
final header = {
"typ": "JWT",
"alg": "HS256",
"x-ably-token": tokenDetails.token
};
final currentTime = (DateTime.now().millisecondsSinceEpoch / 1000).round();
final claims = {
"iat": currentTime, /* current time in seconds */
"exp": currentTime + 3600, /* time of expiration in seconds */
"x-ably-capability": "{\"*\":[\"*\"]}"
};
final base64Header = base64UrlEncode(utf8.encode(json.encode(header)));
final base64Claims = base64UrlEncode(utf8.encode(json.encode(claims)));
final hmacSha256 = Hmac(sha256, utf8.encode("$base64Header.$base64Claims"));
final digest = hmacSha256.convert(utf8.encode("{{API_KEY_SECRET}}"));
final signature = base64UrlEncode(digest.bytes);
final ablyJwt = "$base64Header.$base64Claims.$signature";
```
## What triggers token authentication
Any of the following cause an SDK to use token authentication:
* An [`authUrl`](https://ably.com/docs/api/realtime-sdk/types.md#client-options) or [`authCallback`](https://ably.com/docs/api/realtime-sdk/types.md#client-options) is provided.
* [`useTokenAuth`](https://ably.com/docs/api/realtime-sdk/types.md#client-options) is true.
* A [`token`](https://ably.com/docs/api/realtime-sdk/types.md#client-options) or [`tokenDetails`](https://ably.com/docs/api/realtime-sdk/types.md#client-options) 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 as the current token expires.
## Next steps
- [JWTs](https://ably.com/docs/auth/token/jwt.md) - Recommended approach for most applications
- [Ably Tokens](https://ably.com/docs/auth/token/ably-tokens.md) - Alternative when JWTs aren't suitable
- [Capabilities](https://ably.com/docs/auth/capabilities.md) - Control what operations clients can perform
- [Identified clients](https://ably.com/docs/auth/identified-clients.md) - Assign trusted identities to clients
- [Token revocation](https://ably.com/docs/auth/revocation.md) - Immediately invalidate tokens
## Related Topics
- [JWTs](https://ably.com/docs/auth/token/jwt.md): JWT authentication is the recommended approach for authenticating clients with Ably. Create JWTs signed with your Ably API key.
- [Ably Tokens](https://ably.com/docs/auth/token/ably-tokens.md): Ably Tokens are an alternative to JWTs when you need to keep capabilities confidential or when your capability list is very large.
## Documentation Index
To discover additional Ably documentation:
1. Fetch [llms.txt](https://ably.com/llms.txt) for the canonical list of available pages.
2. Identify relevant URLs from that index.
3. Fetch target pages as needed.
Avoid using assumed or outdated documentation paths.