import * as Ably from 'ably'; import * as jwt from 'jsonwebtoken'; import { ablyApiKey, isLocalEnvironment, testEnvironment } from './environment'; import { randomClientId } from './identifier'; export const baseRealtimeOptions = (options?: Ably.ClientOptions): Ably.ClientOptions => { options = options ?? {}; options.clientId = options.clientId ?? randomClientId(); options.environment = options.environment ?? testEnvironment(); options.key = options.key ?? ablyApiKey(); options.useBinaryProtocol = options.useBinaryProtocol ?? false; options.logHandler = options.logHandler ?? ((msg) => { console.error(msg); }); options.logLevel = options.logLevel ?? 1; // error if (isLocalEnvironment()) { options.port = 8081; options.tls = false; } return options; }; // Create a realtime client with the given options, or Sandbox defaults if not specified, // and return it. export const ablyRealtimeClient = (options?: Ably.ClientOptions): Ably.Realtime => { // TODO: need to replace with pub/sub UTS RpcRealtime returned from RpcChatClient? return new Ably.Realtime(baseRealtimeOptions(options)); }; // At the moment, chat doesn't support keys for authentication, so create a client that uses tokens export const ablyRealtimeClientOptionsWithToken = (options?: Ably.ClientOptions): Ably.ClientOptions => { options = baseRealtimeOptions(options); if (!options.key) { throw new Error('key must be provided when using tokens'); } const [keyId, keySecret] = options.key.split(':'); if (!keyId || !keySecret) { throw new Error('key must be in the format "keyId:key'); } options.useTokenAuth = true; // Generate the token // It's valid for 1 hour and has access to all channels and chat rooms const header = { typ: 'JWT', alg: 'HS256', kid: keyId, }; const currentTime = Math.round(Date.now() / 1000); const claims = { iat: currentTime, exp: currentTime + 3600, 'x-ably-capability': '{"*":["*"], "[chat]*":["*"]}', // eslint-disable-next-line @typescript-eslint/naming-convention 'x-ably-clientId': options.clientId, }; const token = jwt.sign(claims, keySecret, { header: header }); options.token = token; return options; }; export const ablyRealtimeClientWithToken = (options?: Ably.ClientOptions): Ably.Realtime => { return ablyRealtimeClient(ablyRealtimeClientOptionsWithToken(options)); };