# Set up authentication
AI Transport authenticates through your existing auth: your server validates the user and signs an Ably token, and the browser's Ably client fetches it through `authCallback`and refreshes it before expiry.
This page is the practical setup. For the conceptual model (the three auth layers, capabilities, token lifecycle, cancel authorisation), see the [authentication concept](https://ably.com/docs/ai-transport/concepts/authentication.md).
## Sign tokens on the server
Create an endpoint that authenticates the user and returns a short-lived Ably JWT with the [capabilities](https://ably.com/docs/ai-transport/concepts/authentication.md#capabilities) AI Transport needs:
### Javascript
```
import jwt from 'jsonwebtoken';
const [keyName, keySecret] = process.env.ABLY_API_KEY.split(':');
export async function GET(req) {
const userId = await authenticateUser(req);
const ablyJwt = jwt.sign(
{
'x-ably-capability': JSON.stringify({
[`conversations:${userId}`]: ['publish', 'subscribe', 'history'],
}),
'x-ably-clientId': userId,
},
keySecret,
{ algorithm: 'HS256', keyid: keyName, expiresIn: '1h' },
);
return new Response(ablyJwt, { headers: { 'Content-Type': 'text/plain' } });
}
```
Scope the capability to the channel namespace the user is allowed to access, `conversations:${userId}` here. The `x-ably-clientId` claim binds the token to a specific user identity that the Ably service verifies on every publish.
## Fetch tokens from the client
Construct an Ably Realtime client with `authCallback`. The SDK calls it on first auth and again whenever a refresh is needed:
### Javascript
```
import * as Ably from 'ably';
const realtimeClient = new Ably.Realtime({
authCallback: async (tokenParams, callback) => {
try {
const response = await fetch('/api/auth/token', { credentials: 'include' });
if (!response.ok) throw new Error('Auth failed');
const jwt = await response.text();
callback(null, jwt);
} catch (error) {
callback(error, null);
}
},
});
```
## Wire it into the React provider stack
In a React app, hold the client in state, wrap the tree in `AblyProvider`, then in `ClientSessionProvider` for the channel:
### Javascript
```
'use client'
import { useEffect, useState } from 'react';
import * as Ably from 'ably';
import { AblyProvider } from 'ably/react';
import { ClientSessionProvider, useClientSession } from '@ably/ai-transport/react';
import { UIMessageCodec } from '@ably/ai-transport/vercel';
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/auth/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};
}
function App({ conversationId }) {
return (
);
}
function Chat() {
const { session, sessionError } = useClientSession();
// ...
}
```
## Authenticate the agent POST
The application's POST that wakes the agent is a separate HTTP request from the channel auth. Authenticate it however you normally do (session cookie, bearer token, signed header) when you call `fetch` on the core flow:
### Javascript
```
async function wakeAgent(run) {
await fetch('/api/chat', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${await getAccessToken()}`,
},
body: JSON.stringify(run.toInvocation().toJSON()),
});
}
```
For the Vercel flow, [`ChatTransportProvider`](https://ably.com/docs/ai-transport/api/react/vercel/chat-transport-provider.md) accepts a `credentials` prop and a `chatOptions.prepareSendMessagesRequest` hook that returns `{ body?, headers? }` per request. Use the hook to attach auth headers to every invocation POST it makes for you.
## Read next
- [Authentication concept](https://ably.com/docs/ai-transport/concepts/authentication.md): the three auth layers, capabilities, and token lifecycle.
- [Enable channel rules](https://ably.com/docs/ai-transport/getting-started/channel-rules.md): the one-time namespace configuration AI Transport requires.
- [Core SDK getting started](https://ably.com/docs/ai-transport/getting-started/core-sdk.md): build a chat app on the auth set up here.
- [Vercel AI SDK getting started](https://ably.com/docs/ai-transport/getting-started/vercel-ai-sdk.md): build a chat app using the Vercel wrapper.
## Related Topics
- [About AI Transport](https://ably.com/docs/ai-transport.md): AI Transport is durable session infrastructure for AI applications. Streams survive reconnects, sessions span devices, and any participant signals any other through the same session.
- [Enable channel rules](https://ably.com/docs/ai-transport/getting-started/channel-rules.md): Enable the Ably channel rule AI Transport needs: Message annotations, updates, deletes, and appends on the channel namespace your conversations live on.
## 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.