Skip to content

JS SDK not refreshing JWT used in WebSocket #47

Description

@6bda68f0-ctrl

The issue is that once the token expires or the socket is killed, the connection cannot be re-established.

My setup: React SPA using https://www.npmjs.com/package/@ughuuu/gamend


WebSocket reconnect loops forever on expired JWT — no token-refresh hook in GameRealtime

Problem

GameRealtime bakes the access JWT into the Phoenix.Socket connect params at construction time and never updates it:

// dist/realtime.js (GameRealtime constructor)
this._token = token;                              // stored once, never updated
var params = { token: token };                   // static object, not a function
this._socket = new Phoenix.Socket(wsUrl, { params: params });

Phoenix JS replays these same params on every internal reconnect (via endPointURL()), but there is no way to update the token after construction. So when the 15-minute access JWT expires and the client reconnects (e.g. after Bandit's 5-minute idle timeout closes the transport on a backgrounded tab), Phoenix JS retries with the same expired JWT → server rejects with a 403 handshake rejection → WS onerror → reconnect loop.

Because the frontend has no gamend-level hook to refresh the token on reconnect, it must instead destroy and rebuild the entire GameRealtime instance whenever the token changes. This pushes complex recovery logic (stale-token detection, background-tab suppression, dedup/cooldown) into every consuming app's GamendRealtimeProvider.

How to fix

Phoenix JS already supports params as a function (it wraps the value in closure(), which calls it fresh on every transportConnect()). Expose this:

// In the GameRealtime constructor, accept an optional tokenProvider:
function GameRealtime(serverUrl, token, socketOpts = {}, tokenProvider) {
  this._token = token;
  const params = tokenProvider
    ? () => ({ token: tokenProvider() })   // ← called on every reconnect
    : { token: token };
  this._socket = new Phoenix.Socket(wsUrl, { params, ...socketOpts });
}

When tokenProvider is supplied, a reconnecting socket automatically picks up the current (refreshed) token — no instance rebuild needed. The consumer just registers a callback that returns the live access token, and Phoenix's built-in reconnect self-heals from a stale-JWT 403 without any app-level recovery heuristic.

Impact

This single capability would eliminate the need for every gamend consumer to implement tokenSig-driven socket teardown, attemptRecovery() heuristics, stale-socket generation guards, and the auth_token_accessor registration seam — all of which exist today solely because GameRealtime cannot update its token post-construction.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions