# Chat authentication
Chat authentication is handled by the underlying Pub/Sub SDK. You authenticate an Ably Realtime client, then pass that authenticated client into `ChatClient`.
## Authentication flow
1. Your auth server authenticates the user.
2. Your auth server issues an Ably-compatible token (JWT format is recommended for most apps).
3. The client SDK fetches tokens with `authCallback` and refreshes them automatically before expiry.
4. The authenticated Pub/Sub client is passed into `ChatClient`.
## Server setup
Create an endpoint that validates user-provided credentials and returns JWTs with the appropriate Chat capabilities:
### Javascript
```
// Server-side JWT with Chat capabilities
import jwt from 'jsonwebtoken';
const [keyName, keySecret] = process.env.ABLY_API_KEY.split(':');
const ablyJwt = jwt.sign(
{
'x-ably-capability': JSON.stringify({
// Only allow access to a specific room
'your-room': ['publish', 'subscribe', 'presence', 'history'],
}),
'x-ably-clientId': userId,
},
keySecret,
{ algorithm: 'HS256', keyid: keyName, expiresIn: '1h' }
);
```
### Python
```
# Server-side JWT with Chat capabilities
import jwt
import json
import time
import os
key_name, key_secret = os.environ['ABLY_API_KEY'].split(':')
now = int(time.time())
ably_jwt = jwt.encode(
{
'iat': now,
'exp': now + 3600,
'x-ably-capability': json.dumps({
# Only allow access to a specific room
'your-room': ['publish', 'subscribe', 'presence', 'history'],
}),
'x-ably-clientId': user_id,
},
key_secret,
algorithm='HS256',
headers={'kid': key_name}
)
```
### Go
```
// Server-side JWT with Chat capabilities
header := map[string]string{
"typ": "JWT",
"alg": "HS256",
"kid": keyName,
}
currentTime := time.Now().Unix()
claims := map[string]interface{}{
"iat": currentTime,
"exp": currentTime + 3600,
"x-ably-capability": `{"your-room":["publish","subscribe","presence","history"]}`,
"x-ably-clientId": userId,
}
// Sign using HS256 with your API key secret
```
### Ruby
```
# Server-side JWT with Chat capabilities
require 'jwt'
key_name, key_secret = ENV['ABLY_API_KEY'].split(':')
now = Time.now.to_i
payload = {
'iat' => now,
'exp' => now + 3600,
'x-ably-capability' => '{"your-room":["publish","subscribe","presence","history"]}',
'x-ably-clientId' => user_id
}
ably_jwt = JWT.encode(payload, key_secret, 'HS256', { 'kid' => key_name })
```
### Php
```
// Server-side JWT with Chat capabilities
$header = [
'typ' => 'JWT',
'alg' => 'HS256',
'kid' => $keyName
];
$currentTime = time();
$claims = [
'iat' => $currentTime,
'exp' => $currentTime + 3600,
'x-ably-capability' => '{"your-room":["publish","subscribe","presence","history"]}',
'x-ably-clientId' => $userId
];
$base64Header = base64_encode(json_encode($header));
$base64Claims = base64_encode(json_encode($claims));
$signature = hash_hmac('sha256', $base64Header . '.' . $base64Claims, $keySecret, true);
$jwt = $base64Header . '.' . $base64Claims . '.' . base64_encode($signature);
```
### Java
```
Map headerClaims = new HashMap<>();
headerClaims.put("typ", "JWT");
headerClaims.put("alg", "HS256");
headerClaims.put("kid", keyName);
long currentTimeInSeconds = System.currentTimeMillis() / 1000;
Map claims = new HashMap<>();
claims.put("iat", currentTimeInSeconds);
claims.put("exp", currentTimeInSeconds + 3600);
claims.put("x-ably-capability", "{\"your-room\":[\"publish\",\"subscribe\",\"presence\",\"history\"]}");
claims.put("x-ably-clientId", userId);
Algorithm algorithm = Algorithm.HMAC256(keySecret);
String token = JWT.create()
.withHeader(headerClaims)
.withPayload(claims)
.sign(algorithm);
```
### Csharp
```
// Server-side JWT with Chat capabilities
var header = new Dictionary
{
{ "typ", "JWT" },
{ "alg", "HS256" },
{ "kid", keyName }
};
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var claims = new Dictionary
{
{ "iat", currentTime },
{ "exp", currentTime + 3600 },
{ "x-ably-capability", "{\"your-room\":[\"publish\",\"subscribe\",\"presence\",\"history\"]}" },
{ "x-ably-clientId", userId }
};
// Sign using HS256 with your API key secret
```
## Client setup
### Javascript
```
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';
const realtimeClient = new Ably.Realtime({
authCallback: async (tokenParams, callback) => {
try {
const response = await fetch('/api/ably-token', { credentials: 'include' });
if (!response.ok) throw new Error('Auth failed');
const jwt = await response.text();
callback(null, jwt);
} catch (error) {
callback(error, null);
}
},
});
const chatClient = new ChatClient(realtimeClient);
```
### React
```
import { useMemo } from 'react';
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';
import { ChatClientProvider } from '@ably/chat/react';
export function App() {
const realtimeClient = useMemo(
() =>
new Ably.Realtime({
authCallback: async (tokenParams, callback) => {
try {
const response = await fetch('/api/ably-token', { credentials: 'include' });
if (!response.ok) throw new Error('Auth failed');
callback(null, await response.text());
} catch (error) {
callback(error, null);
}
},
}),
[]
);
const chatClient = useMemo(() => new ChatClient(realtimeClient), [realtimeClient]);
return (
);
}
```
### Swift
```
import Ably
import AblyChat
let realtimeOptions = ARTClientOptions()
realtimeOptions.authCallback = { tokenParams, callback in
fetchAblyJwt { result in
switch result {
case .success(let jwt):
callback(jwt as ARTTokenDetailsCompatible, nil)
case .failure(let error):
callback(nil, error)
}
}
}
let realtime = ARTRealtime(options: realtimeOptions)
let chatClient = ChatClient(realtime: realtime)
```
### Kotlin
```
import com.ably.chat.ChatClient
import io.ably.lib.realtime.AblyRealtime
import io.ably.lib.rest.Auth
import io.ably.lib.types.ClientOptions
val realtimeClient = AblyRealtime(
ClientOptions().apply {
authCallback = Auth.TokenCallback { _ ->
// Return JWT string from your auth server
fetchAblyJwt()
}
}
)
val chatClient = ChatClient(realtimeClient)
```
In each example, the authenticated Pub/Sub client is passed into `ChatClient` and Chat uses that connection for authentication and token renewal.
## Chat capabilities
Capabilities are permissions that control what operations a client can perform. When you create a token for a Chat user, you specify which capabilities they have. Each Chat feature requires specific capabilities:
| Feature | Required Capabilities |
|---------|----------------------|
| Send messages | `publish` |
| Receive messages | `subscribe` |
| Update messages | `message-update-any` or `message-update-own` |
| Delete messages | `message-delete-any` or `message-delete-own` |
| Message history | `subscribe`, `history` |
| Message reactions | `annotation-publish`, optionally `annotation-subscribe` |
| Presence | `subscribe`, `presence` |
| Typing indicators | `publish`, `subscribe` |
| Room reactions | `publish`, `subscribe` |
| Occupancy | `subscribe`, `channel-metadata` |
| All Chat features | `publish`, `subscribe`, `presence`, `history`, `channel-metadata`, `annotation-publish`, `annotation-subscribe`, `message-update-own`, `message-delete-own` |
## Room-scoped capabilities
You can scope capabilities to specific rooms, a namespace of rooms, or all rooms:
* `my-chat-room` - a specific room
* `dms:*` - all rooms in the `dms:` namespace
* `*` - all chat rooms
## Token lifecycle and permission updates
- With `authCallback` or `authUrl`, token refresh is automatic and handled by the SDK.
- To change a user's capabilities during an active session, issue a new token from your auth server and re-authenticate:
### Javascript
```
// Re-authenticate to pick up updated capabilities
await realtimeClient.auth.authorize();
```
- To immediately remove access, [revoke issued tokens](https://ably.com/docs/auth/revocation.md).
- If your capability JSON is too large for JWT or must remain confidential, use native [Ably Tokens](https://ably.com/docs/auth/token/ably-tokens.md).
## Related Topics
- [SDK setup](https://ably.com/docs/chat/setup.md): Install, authenticate and instantiate the Chat SDK.
- [Connections](https://ably.com/docs/chat/connect.md): Manage the realtime connections to Ably.
- [Rooms](https://ably.com/docs/chat/rooms.md): Use rooms to organize your users and chat messages.
- [Integrations](https://ably.com/docs/chat/integrations.md): Ably Chat integrations with external services.
## 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.