# Authentication
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
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 provides channels to AI Transport via `useChannel` or `channels.get`.
## Server setup
Create an endpoint that validates user-provided credentials and returns JWTs with the appropriate AI Transport capabilities:
### Javascript
```
// 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
### Javascript
```
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');
```
### React
```
'use client'
import { useEffect, useState } from 'react';
import * as Ably from 'ably';
import { AblyProvider, useChannel } from 'ably/react';
import { useClientTransport } from '@ably/ai-transport/react';
// 1. Create an authenticated Ably client and wrap your app
export function Providers({ children }) {
const [client, setClient] = useState(null);
useEffect(() => {
const ably = new Ably.Realtime({
authCallback: async (_tokenParams, callback) => {
try {
const response = await fetch('/api/ably-token', { credentials: 'include' });
callback(null, await response.text());
} catch (err) {
callback(err instanceof Error ? err.message : String(err), null);
}
},
});
setClient(ably);
return () => ably.close();
}, []);
if (!client) return null;
return {children};
}
// 2. Inside the provider, get a channel and pass it to the transport
function Chat({ conversationId }) {
const { channel } = useChannel({ channelName: conversationId });
const transport = useClientTransport({ channel, codec: yourCodec });
// ...
}
```
For more details, see [token authentication](https://ably.com/docs/auth/token.md), [capabilities](https://ably.com/docs/auth/capabilities.md), and [identified clients](https://ably.com/docs/auth/identified-clients.md).
## 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](https://ably.com/accounts/any/apps/any/settings):
1. Click **Add new rule**.
2. Enter the channel name or namespace on which to enable message updates and deletes.
3. Check **Message annotations, updates, deletes, and appends**.
4. Click **Create rule** to save.
Create a rule with updates and deletes enabled using the Control API by sending a `POST` request to [`/apps/{app_id}/namespaces`](https://ably.com/docs/api/control-api.md):
### Shell
```
curl -X POST https://control.ably.net/v1/apps/{APP_ID}/namespaces \
-H "Authorization: Bearer {ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"id": "my-namespace",
"mutableMessages": true
}'
```
Use the [Ably CLI](https://ably.com/docs/platform/tools/cli.md) to create a rule with updates and deletes enabled:
### Shell
```
ably apps rules create \
--name "my-namespace" \
--mutable-messages
```
Run `ably apps rules create --help` for a full list of available options.
## 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 channel
* `conversations:*` - all channels in the `conversations:` namespace
* `*` - all channels
## 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).
## 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.
### Javascript
```
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.
### Javascript
```
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:
### Javascript
```
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](https://ably.com/docs/ai-transport/getting-started/vercel-ai-sdk.md): Set up authentication in a working app.
- [Cancellation](https://ably.com/docs/ai-transport/features/cancellation.md): How cancel signals and authorization work in detail.
## Related Topics
- [Sessions and turns](https://ably.com/docs/ai-transport/how-it-works/sessions-and-turns.md): Understand how Ably AI Transport models sessions as durable channels and structures conversations into turns with clear lifecycles.
- [Transport](https://ably.com/docs/ai-transport/how-it-works/transport.md): Understand the two-layer transport architecture in Ably AI Transport: a generic core transport and a pluggable codec that bridges your AI framework to Ably.
## 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.