diff --git a/examples/auth-generate-jwt/react/src/config.ts b/examples/auth-generate-jwt/react/src/config.ts
new file mode 100644
index 0000000000..ce58979c72
--- /dev/null
+++ b/examples/auth-generate-jwt/react/src/config.ts
@@ -0,0 +1,3 @@
+export const config = {
+ AUTH_URL: import.meta.env.VITE_AUTH_JWT_URL as string,
+};
diff --git a/examples/auth-generate-jwt/react/src/env.d.ts b/examples/auth-generate-jwt/react/src/env.d.ts
index 7421e8e743..ada17315d3 100644
--- a/examples/auth-generate-jwt/react/src/env.d.ts
+++ b/examples/auth-generate-jwt/react/src/env.d.ts
@@ -2,6 +2,7 @@
interface ImportMetaEnv {
readonly VITE_ABLY_KEY: string;
+ readonly VITE_AUTH_JWT_URL: string;
}
interface ImportMeta {
diff --git a/examples/auth-generate-jwt/server/src/server.ts b/examples/auth-generate-jwt/server/src/server.ts
index c9ec3d265c..b40aade6ae 100644
--- a/examples/auth-generate-jwt/server/src/server.ts
+++ b/examples/auth-generate-jwt/server/src/server.ts
@@ -1,9 +1,11 @@
import express from 'express';
import path from 'path';
+import { fileURLToPath } from 'url';
import dotenv from 'dotenv';
import cors from 'cors';
import crypto from 'crypto';
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
dotenv.config({ path: path.resolve(__dirname, '../../../.env.local') });
const app = express();
@@ -21,8 +23,6 @@ function base64urlEncode(str: string) {
}
app.get('/generate-jwt', async (_req, res) => {
- console.log('1 - /generate-jwt endpoint called');
-
const ablyApiKey = process.env.VITE_ABLY_KEY || '';
const [apiKeyName, apiKeySecret] = ablyApiKey.split(':');
try {
@@ -51,8 +51,7 @@ app.get('/generate-jwt', async (_req, res) => {
const signature = base64urlEncode(hmac.digest('base64'));
const ablyJwt = base64Header + '.' + base64Claims + '.' + signature;
- console.log('2 - JWT generated: ', ablyJwt);
- res.json(ablyJwt);
+ res.type('application/jwt').send(ablyJwt);
} catch (error) {
res.status(500).json({ error: 'Failed to generate token' });
}
diff --git a/examples/auth-request-token/javascript/src/config.ts b/examples/auth-request-token/javascript/src/config.ts
new file mode 100644
index 0000000000..651f0a42de
--- /dev/null
+++ b/examples/auth-request-token/javascript/src/config.ts
@@ -0,0 +1,3 @@
+export const config = {
+ AUTH_URL: import.meta.env.VITE_AUTH_TOKEN_URL as string,
+};
diff --git a/examples/auth-request-token/javascript/src/script.ts b/examples/auth-request-token/javascript/src/script.ts
index c7adc27b32..a57e1f0b68 100644
--- a/examples/auth-request-token/javascript/src/script.ts
+++ b/examples/auth-request-token/javascript/src/script.ts
@@ -1,5 +1,6 @@
import * as Ably from 'ably';
import './styles.css';
+import { config } from './config';
const connectButton = document.querySelector('button') as HTMLButtonElement;
@@ -14,7 +15,15 @@ function handleConnect() {
messageOne.textContent = '✓';
const realtimeClient = new Ably.Realtime({
- authUrl: 'http://localhost:3001/request-token',
+ authCallback: async (_tokenParams, callback) => {
+ try {
+ const response = await fetch(config.AUTH_URL || 'http://localhost:3001/request-token');
+ const token = await response.text();
+ callback(null, token);
+ } catch (error) {
+ callback(error instanceof Error ? error.message : String(error), null);
+ }
+ },
});
const messageTwo = document.getElementById('message-2');
diff --git a/examples/auth-request-token/javascript/vite-env.d.ts b/examples/auth-request-token/javascript/vite-env.d.ts
index 449e61aa75..8affe3e165 100644
--- a/examples/auth-request-token/javascript/vite-env.d.ts
+++ b/examples/auth-request-token/javascript/vite-env.d.ts
@@ -1,5 +1,6 @@
interface ImportMetaEnv {
readonly VITE_ABLY_KEY: string;
+ readonly VITE_AUTH_TOKEN_URL: string;
// Add other environment variables here if needed
}
diff --git a/examples/auth-request-token/react/src/App.tsx b/examples/auth-request-token/react/src/App.tsx
index 9e7c99d768..fdb2b383eb 100644
--- a/examples/auth-request-token/react/src/App.tsx
+++ b/examples/auth-request-token/react/src/App.tsx
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import * as Ably from 'ably';
import { AblyProvider, useConnectionStateListener } from 'ably/react';
import './styles/styles.css';
+import { config } from './config';
interface StatusMessage {
id: number;
@@ -53,10 +54,36 @@ export default function App() {
]);
const handleConnect = async () => {
- // Navigate to authenticated page
- window.location.href = '/authenticated';
+ // Update first message
+ setMessages((prevMessages) => prevMessages.map((msg) => (msg.id === 1 ? { ...msg, success: true } : msg)));
+
+ // Initialize Ably client with token auth
+ const realtimeClient = new Ably.Realtime({
+ authCallback: async (_tokenParams, callback) => {
+ try {
+ const response = await fetch(config.AUTH_URL || 'http://localhost:3001/request-token');
+ const token = await response.text();
+ callback(null, token);
+ } catch (error) {
+ callback(error instanceof Error ? error.message : String(error), null);
+ }
+ },
+ });
+
+ // Update second message
+ setMessages((prevMessages) => prevMessages.map((msg) => (msg.id === 2 ? { ...msg, success: true } : msg)));
+
+ setClient(realtimeClient);
};
+ if (client) {
+ return (
+
+
+
+ );
+ }
+
return (
diff --git a/examples/auth-request-token/react/src/config.ts b/examples/auth-request-token/react/src/config.ts
new file mode 100644
index 0000000000..651f0a42de
--- /dev/null
+++ b/examples/auth-request-token/react/src/config.ts
@@ -0,0 +1,3 @@
+export const config = {
+ AUTH_URL: import.meta.env.VITE_AUTH_TOKEN_URL as string,
+};
diff --git a/examples/auth-request-token/react/src/env.d.ts b/examples/auth-request-token/react/src/env.d.ts
index 7421e8e743..1fc76da9eb 100644
--- a/examples/auth-request-token/react/src/env.d.ts
+++ b/examples/auth-request-token/react/src/env.d.ts
@@ -2,6 +2,7 @@
interface ImportMetaEnv {
readonly VITE_ABLY_KEY: string;
+ readonly VITE_AUTH_TOKEN_URL: string;
}
interface ImportMeta {
diff --git a/examples/auth-request-token/server/src/server.ts b/examples/auth-request-token/server/src/server.ts
index c065fbba41..304038f55c 100644
--- a/examples/auth-request-token/server/src/server.ts
+++ b/examples/auth-request-token/server/src/server.ts
@@ -1,9 +1,11 @@
import express from 'express';
import path from 'path';
+import { fileURLToPath } from 'url';
import dotenv from 'dotenv';
import cors from 'cors';
import Ably from 'ably';
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
dotenv.config({ path: path.resolve(__dirname, '../../../.env.local') });
const app = express();
@@ -19,10 +21,8 @@ app.use(
const ably = new Ably.Rest(process.env.VITE_ABLY_KEY || '');
app.get('/request-token', async (_req, res) => {
- console.log('1 - /request-token endpoint called');
try {
const tokenRequest = await ably.auth.requestToken({ clientId: 'example-client-id' });
- console.log('2 - Token generated:', tokenRequest);
res.json(tokenRequest);
} catch (error) {
res.status(500).json({ error: 'Failed to generate token' });
diff --git a/src/components/Examples/ExamplesRenderer.tsx b/src/components/Examples/ExamplesRenderer.tsx
index 5db3648007..59d680edec 100644
--- a/src/components/Examples/ExamplesRenderer.tsx
+++ b/src/components/Examples/ExamplesRenderer.tsx
@@ -73,6 +73,8 @@ const ExamplesRenderer = ({
Object.entries(files).forEach(([languageKey, languageFiles]) => {
result[languageKey as LanguageKey] = updateAblyConnectionKey(languageFiles, apiKey, {
NAME: getRandomChannelName(), // Use CHANNEL_NAME as env var key
+ AUTH_TOKEN_URL: process.env.VITE_AUTH_TOKEN_URL || '',
+ AUTH_JWT_URL: process.env.VITE_AUTH_JWT_URL || '',
});
});
return result;
diff --git a/src/data/examples/index.ts b/src/data/examples/index.ts
index b7d4ba13c7..c1fc99c47e 100644
--- a/src/data/examples/index.ts
+++ b/src/data/examples/index.ts
@@ -23,6 +23,26 @@ export const examples: Example[] = [
metaTitle: 'Build AI message-per-response streaming with Ably AI Transport',
metaDescription: `Stream AI-generated tokens by appending them to a single message using Ably AI Transport. Each response appears as one compacted message in channel history.`,
},
+ {
+ id: 'auth-generate-jwt',
+ name: 'Generate JWT',
+ description: 'Generate a JSON Web Token (JWT) for authenticating users.',
+ products: ['auth'],
+ layout: 'single-horizontal',
+ visibleFiles: ['src/script.ts', 'App.tsx', 'Chat.tsx', 'Home.tsx', 'index.tsx'],
+ metaTitle: `Authenticate with Ably using JWTs`,
+ metaDescription: `Learn how to generate and use JWTs for client authentication with Ably. Secure token-based auth for realtime applications.`,
+ },
+ {
+ id: 'auth-request-token',
+ name: 'Request Token',
+ description: 'Request an Ably Token for authenticating users.',
+ products: ['auth'],
+ layout: 'single-horizontal',
+ visibleFiles: ['src/script.ts', 'App.tsx', 'Chat.tsx', 'Home.tsx', 'index.tsx'],
+ metaTitle: `Authenticate with Ably Token`,
+ metaDescription: `Learn how to request and use Ably Tokens for client authentication. Secure token-based auth for realtime applications.`,
+ },
{
id: 'chat-presence',
name: 'Chat presence',