Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/data/nav/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export default {
name: 'SDK setup',
link: '/docs/chat/setup',
},
{
name: 'Authentication',
link: '/docs/chat/authentication',
},
{
name: 'Connections',
link: '/docs/chat/connect',
Expand Down
8 changes: 8 additions & 0 deletions src/data/nav/pubsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export default {
link: '/docs/auth',
index: true,
},
{
name: 'Quick reference',
link: '/docs/auth/quick-reference',
},
{
name: 'Basic auth',
link: '/docs/auth/basic',
Expand All @@ -99,6 +103,10 @@ export default {
name: 'Token auth',
link: '/docs/auth/token',
},
{
name: 'JWT integration',
link: '/docs/auth/jwt-integration',
},
{
name: 'Token revocation',
link: '/docs/auth/revocation',
Expand Down
4 changes: 4 additions & 0 deletions src/data/nav/spaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export default {
name: 'SDK setup',
link: '/docs/spaces/setup',
},
{
name: 'Authentication',
link: '/docs/spaces/authentication',
},
{
name: 'React Hooks',
link: '/docs/spaces/react',
Expand Down
8 changes: 8 additions & 0 deletions src/pages/docs/auth/capabilities.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,10 @@ Capabilities are determined for [Ably JWTs](/docs/auth/token#jwt) in the followi

## Custom restrictions on channels <a id="custom-restrictions"/>

<Aside data-type='important'>
**JWT-only feature:** Channel-scoped user claims are only available when using [Ably JWT authentication](/docs/auth/token#choosing-jwt). They are not available with TokenRequest authentication. See [JWT-only features](/docs/auth/token#jwt-only-features) for a complete list of capabilities exclusive to JWT authentication.
</Aside>

It is possible for JWTs to contain authenticated claims for users that can be used to allow or disallow certain interactions in your channels.

Messages can be annotated with trusted metadata copied from the client's authentication token by Ably servers. Clients are unable to directly publish messages with user claim metadata, and claims contained within the authentication token are signed to prevent tampering. Claims can be scoped to individual channels or to namespaces of [channels](/docs/channels). The most specific user claim will be added to the message as part of the `extras` object. Note that this does not apply to presence or metadata messages.
Expand Down Expand Up @@ -649,6 +653,10 @@ bool fromModerator(Message message) {

## Using JWT for per connection publish rate limits <a id="jwt-limits"/>

<Aside data-type='important'>
**JWT-only feature:** Per-connection publish rate limits are only available when using [Ably JWT authentication](/docs/auth/token#choosing-jwt). They are not available with TokenRequest authentication. See [JWT-only features](/docs/auth/token#jwt-only-features) for a complete list of capabilities exclusive to JWT authentication.
</Aside>

JWTs may specify publish rate limits for a user on particular channels. These limits can be used to prevent any individual user from sending an excessive number of messages in a short period of time.

An example use case is in a large live chat where you may wish to limit users to posting messages no more than once every 10 seconds.
Expand Down
4 changes: 4 additions & 0 deletions src/pages/docs/auth/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ redirect_from:

Before a client or server can issue requests to Ably, such as subscribe to channels, or publish messages, it must authenticate with Ably. Authentication requires an Ably API key.

<Aside data-type='note'>
For a quick guide to choosing the right authentication method for your environment, see the [authentication quick reference](/docs/auth/quick-reference).
</Aside>

## Authentication terminology <a id="terminology"/>

The following terminology helps explain authentication, authorization, and identification in the context of the Ably service:
Expand Down
88 changes: 88 additions & 0 deletions src/pages/docs/auth/jwt-integration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: "JWT integration"
meta_description: "Integrate Ably with existing JWT authentication systems."
---

If your application already uses JWT for authentication, you can embed Ably tokens within your existing JWT structure. This approach lets you maintain a single token for both your application and Ably authentication.

For general JWT authentication patterns, see [token authentication](/docs/auth/token#scenarios).

## Embedded Ably Token <a id="embedded-token"/>

If you want a single JWT for everything, embed an Ably token in your existing JWT:

**Server:**

<Code>
```javascript
import Ably from 'ably';
import jwt from 'jsonwebtoken';

const ably = new Ably.Rest(process.env.ABLY_API_KEY);

app.post('/auth/login', async (req, res) => {
const user = await authenticateUser(req.body.email, req.body.password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}

// Get Ably token
const ablyToken = await ably.auth.requestToken({
clientId: user.id,
capability: getAblyCapabilities(user),
});

// Create your app JWT with embedded Ably token
const appJwt = jwt.sign(
{
userId: user.id,
email: user.email,
// Embed Ably token
'x-ably-token': ablyToken.token,
},
process.env.APP_JWT_SECRET,
{ expiresIn: '1h' } // Must not exceed Ably token expiry
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's teh case, why are you not specifying a TTL when issuing the token?

);

res.json({ token: appJwt });
});
```
</Code>

<Aside data-type='important'>
The outer JWT's expiry must not exceed the embedded Ably token's expiry.
</Aside>

## Token refresh strategy <a id="refresh"/>

When using Ably JWTs, the Ably SDK automatically handles token refresh. Simply implement an `authCallback` that fetches a new token from your server:

<Code>
```javascript
const realtime = new Ably.Realtime({
authCallback: async (tokenParams, callback) => {
try {
// This callback is automatically called when:
// 1. Initial connection
// 2. Token is about to expire
// 3. After a disconnection that requires re-auth
const response = await fetch('/api/ably-jwt', {
credentials: 'include', // Include cookies for session auth
});

if (!response.ok) {
// Handle auth errors - user may need to re-login
throw new Error('Authentication failed');
}

const ablyJwt = await response.text();
callback(null, ablyJwt);
} catch (error) {
callback(error, null);
}
},
});
```
</Code>

The Ably SDK automatically calls your `authCallback` before the current token expires, ensuring seamless token rotation.
197 changes: 197 additions & 0 deletions src/pages/docs/auth/quick-reference.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
---
title: Authentication quick reference
meta_description: "Choose the right authentication method for your use case with this quick reference guide."
---

Use this guide for copy-paste ready code examples for common authentication patterns.

## authUrl vs authCallback

Both options work on all platforms. Choose based on your needs:

- **`authUrl`**: Simpler setup. Provide a URL and the SDK handles the HTTP request. In browsers, cookies are sent automatically for same-origin requests. Use `authHeaders` to add custom headers like Bearer tokens.

- **`authCallback`**: Full control. You implement the token-fetching logic, which is useful when integrating with your app's existing HTTP client, native networking libraries, or platform-specific secure storage.

## Quick examples by platform

### Client-side (use token authentication)

<Code>
```javascript
// ALWAYS use token authentication in browsers
const realtime = new Ably.Realtime({
authCallback: async (tokenParams, callback) => {
try {
const response = await fetch('/api/ably-token');
const token = await response.text();
callback(null, token);
} catch (error) {
callback(error, null);
}
},
});
```

```swift
let options = ARTClientOptions()
options.authCallback = { params, callback in
fetchToken { result in
switch result {
case .success(let token): callback(token, nil)
case .failure(let error): callback(nil, error)
}
}
}
let realtime = ARTRealtime(options: options)
```

```kotlin
val options = ClientOptions().apply {
authCallback = { params -> fetchToken() }
}
val realtime = AblyRealtime(options)
```
</Code>

### Server-side (API key is safe)

<Code>
```javascript
// Server-side: Use REST client for token generation and API operations
import Ably from 'ably';
const ably = new Ably.Rest(process.env.ABLY_API_KEY);
```

```python
# Server-side: Use REST client for token generation and API operations
from ably import AblyRest
ably = AblyRest(os.environ['ABLY_API_KEY'])
```
</Code>

## Common mistakes to avoid

### Never do this in client-side code

<Code>
```javascript
// WRONG: Exposes your API key secret in browser
const realtime = new Ably.Realtime('app-id.key-id:secret');

// WRONG: API key in client-side environment variable
// (bundlers include these in the client bundle)
const realtime = new Ably.Realtime(import.meta.env.VITE_ABLY_KEY);

// WRONG: Hardcoded key
const realtime = new Ably.Realtime({
key: 'xxxxx.yyyyy:zzzzz',
clientId: 'user-123',
});
```
</Code>

### Do this instead

<Code>
```javascript
// CORRECT: Token authentication keeps your key safe
const realtime = new Ably.Realtime({
authCallback: async (tokenParams, callback) => {
try {
const response = await fetch('/api/ably-token');
const token = await response.text();
callback(null, token);
} catch (error) {
callback(error, null);
}
},
// clientId is assigned by your auth server, not here
});
```
</Code>

## Server auth endpoint template

Your server needs an endpoint that creates JWTs. No Ably SDK is required—use any JWT library:

<Code>
```javascript
// Server-side: /api/ably-token endpoint
import jwt from 'jsonwebtoken';

const [keyName, keySecret] = process.env.ABLY_API_KEY.split(':');

app.get('/api/ably-token', async (req, res) => {
// 1. Get authenticated user from your auth middleware
const userId = req.user?.id;
if (!userId) {
return res.status(401).json({ error: 'Not authenticated' });
}

// 2. Create JWT with user's identity
const ablyJwt = jwt.sign(
{
'x-ably-capability': JSON.stringify({
'*': ['publish', 'subscribe', 'presence', 'history'],
}),
'x-ably-clientId': userId,
},
keySecret,
{
algorithm: 'HS256',
keyid: keyName,
expiresIn: '1h',
}
);

// 3. Return to client
res.send(ablyJwt);
});
```

```python
# Server-side: /api/ably-token endpoint
from flask import Flask, g
import jwt
import os
import json
import time

app = Flask(__name__)
api_key = os.environ['ABLY_API_KEY']
key_name, key_secret = api_key.split(':')

@app.route('/api/ably-token')
def get_ably_token():
# 1. Get authenticated user from your auth middleware (e.g., g.user, current_user)
user_id = getattr(g, 'user', {}).get('id')
if not user_id:
return {'error': 'Not authenticated'}, 401

# 2. Create JWT with user's identity
now = int(time.time())
ably_jwt = jwt.encode(
{
'iat': now,
'exp': now + 3600,
'x-ably-capability': json.dumps({
'*': ['publish', 'subscribe', 'presence', 'history']
}),
'x-ably-clientId': user_id,
},
key_secret,
algorithm='HS256',
headers={'kid': key_name}
)

# 3. Return to client
return ably_jwt
```
</Code>

## See also

- [Token authentication](/docs/auth/token) - Full token auth documentation
- [Capabilities](/docs/auth/capabilities) - Fine-grained permission control
- [Identified clients](/docs/auth/identified-clients) - Assigning trusted client identities
Loading