Authentication

Open in

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

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:

  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.

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:

FeatureRequired capabilities
Send user messages to channelpublish
Receive streamed tokenssubscribe
Replay history on reconnectsubscribe, history
Cancel a turnpublish
All AI Transport featurespublish, 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

1

2

// Re-authenticate to pick up updated capabilities
await realtimeClient.auth.authorize();

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

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.

JavaScript

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:

JavaScript

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.