# 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.