Skip to main content

WebSocket Authentication Guide

The stream socket is public until you authenticate it. Authenticating joins the connection to its user room and (for overseers) the overseer room, which is what delivers BALANCE_UPDATED, API_KEY_*, USER_PROFILE_UPDATED, DOWNLOAD_READY, OVERSEER_* and the moderation events, and what lets the connection send chat messages, reactions and typing events and join the overseers' events firehose.

There is no token in the handshake: a static handshake token would be presented, expired, on every reconnect. The service sends an authenticate message instead, with the token current at that moment.

With a LaunchpadSDK​

// `wallet` as here, or an app `session` (see below)
const sdk = createLaunchpadSDK({ env: 'stage', wallet: process.env.PRIVATE_KEY! });
const freshJwt = process.env.APP_JWT ?? '';

await sdk.authenticateStreamWebSocket(); // the session's current token
await sdk.authenticateStreamWebSocket(freshJwt); // right after the app refreshed its JWT
sdk.deauthenticateStreamWebSocket(); // when the wallet disconnects
  • With an app session the token is session.accessToken(); otherwise it is the SDK's own session token (ensureValidToken(), refreshed when near expiry).
  • authenticateStreamWebSocket() opens the connection if it is not open and resolves a boolean: true once the server confirms; false when the server rejects the token (exception or stream_chat_auth_error), does not answer within 10 seconds, the connection drops meanwhile, there is no token, or a newer authenticateStreamWebSocket / deauthenticateStreamWebSocket call replaced it.
  • The same token on the same connection is not sent twice. A different token replaces a pending one (that call resolves false) and is sent after the server answered the one in flight, so the server's final identity is always the newest token.
  • Changing the SDK's wallet (setWallet, setWalletProvider) deauthenticates the socket.

Reconnects​

socket.io reconnects by itself. After every reconnect of a socket that was authenticated, the service asks for the token current at that moment (the session, never a remembered one), authenticates first, then re-joins every room that still has a subscriber, then calls the onReconnect listeners (refetch there what you missed). A socket that was never authenticated, or was deauthenticated, is not re-authenticated.

const stop = sdk.realtime.onReconnect(() => {
console.log('Reconnected: refetch what the screen shows');
});

deauthenticate() closes and reopens the connection​

The server has no message that drops a connection's identity (its user room and the overseer room stay joined until the connection closes). So deauthenticate() on an authenticated socket closes it and opens it again: subscriptions stay registered, the public rooms are joined again on the new connection, and onReconnect listeners fire.

With a bare StreamWebSocketService​

import { StreamWebSocketService, getEnvironmentConfig } from '@gala-chain/launchpad-sdk';

const service = new StreamWebSocketService({
url: getEnvironmentConfig('prod').launchpadBaseUrl,
tokenProvider: async () => process.env.APP_JWT ?? null, // asked at every authentication and every reconnect
});

const ok = await service.authenticate(); // the provider's token
const ok2 = await service.authenticate(process.env.APP_JWT); // an explicit token

The overseers' firehose needs an authenticated overseer socket​

subscribeEventsFirehose(listener, filters?) registers the subscriber at once, but the server only admits an authenticated overseer to the room: the service joins it after authenticate() resolves true (and again after every re-authentication), once for all subscribers. The server applies no filters, so each subscriber's filters (eventTypes, tokenName, userAddress) are applied client-side.

Security​

  • Keep tokens out of logs and out of persistent browser storage where you can; prefer an httpOnly cookie session (session in LaunchpadSDKConfig) so the SDK never holds the token.
  • Use wss://.
  • Call deauthenticateStreamWebSocket() on logout and on wallet disconnect.