Authentication in AI Transport operates at three levels: Ably token authentication for channel access, HTTP headers for server endpoint authorization, and cancel authorization for controlling who can cancel turns.
Authentication flow
- Your auth server authenticates the user.
- Your auth server issues an Ably-compatible token (JWT format is recommended for most apps).
- The client SDK fetches tokens with
authCallbackand refreshes them automatically before expiry. - The authenticated Pub/Sub client provides channels to AI Transport via
useChannelorchannels.get.
Server setup
Create an endpoint that validates user-provided credentials and returns JWTs with the appropriate AI Transport capabilities:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Server-side JWT with AI Transport 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 conversation channel
'your-conversation': ['publish', 'subscribe', 'history'],
}),
'x-ably-clientId': userId,
},
keySecret,
{ algorithm: 'HS256', keyid: keyName, expiresIn: '1h' }
);Client setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import * as Ably from 'ably';
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 channel = realtimeClient.channels.get('your-conversation');For more details, see token authentication, capabilities, and identified clients.
Enable message updates and deletes
AI Transport requires the Message annotations, updates, deletes, and appends channel rule to be enabled on the channel namespace used for conversations. Without this rule, the transport cannot function correctly.
In your app settings:
- Click Add new rule.
- Enter the channel name or namespace on which to enable message updates and deletes.
- Check Message annotations, updates, deletes, and appends.
- Click Create rule to save.
AI Transport capabilities
Capabilities are permissions that control what operations a client can perform. When you create a token for an AI Transport user, specify which capabilities they have:
| Feature | Required capabilities |
|---|---|
| Send user messages to channel | publish |
| Receive streamed tokens | subscribe |
| Replay history on reconnect | subscribe, history |
| Cancel a turn | publish |
| All AI Transport features | publish, subscribe, history |
Channel-scoped capabilities
You can scope capabilities to specific conversation channels, a namespace of channels, or all channels:
my-conversation- a specific conversation channelconversations:*- all channels in theconversations:namespace*- all channels
Token lifecycle and permission updates
- With
authCallbackorauthUrl, 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:
1
2
// Re-authenticate to pick up updated capabilities
await realtimeClient.auth.authorize();- To immediately remove access, revoke issued tokens.
- If your capability JSON is too large for JWT or must remain confidential, use native Ably Tokens.
Server endpoint authentication
When the client sends user messages, it makes an HTTP POST to the server endpoint. The ClientTransportOptions provide two mechanisms for authenticating these requests:
headers: Static or dynamic HTTP headers sent with every POST.
1
2
3
4
5
6
7
const transport = useClientTransport({
channel,
codec: UIMessageCodec,
headers: () => ({
'Authorization': `Bearer ${getAuthToken()}`,
}),
})credentials: Controls whether cookies are sent with the POST request. This maps directly to the Fetch API credentials option. Set to 'include' if your server uses cookie-based session authentication (for example, NextAuth or express-session) and the endpoint is cross-origin. For same-origin requests, the browser sends cookies by default and this option is not needed.
1
2
3
4
5
const transport = useClientTransport({
channel,
codec: UIMessageCodec,
credentials: 'include',
})The server endpoint validates these credentials however it normally would: JWT verification, session cookies, or API keys.
Cancel authorization
When a client publishes a cancel signal, the server can authorize or reject it. The onCancel hook on NewTurnOptions receives a CancelRequest with the filter, matched turn IDs, and a map of turn owners:
1
2
3
4
5
6
7
8
9
const turn = transport.newTurn({
turnId,
clientId,
onCancel: async (request) => {
// Only allow the turn owner to cancel
const owner = request.turnOwners.get(request.filter.turnId)
return owner === request.message.clientId
},
})If onCancel returns false, the cancellation is rejected. If onCancel is not provided, all cancel requests are accepted by default.
What to read next
- Getting started: Set up authentication in a working app.
- Cancellation: How cancel signals and authorization work in detail.