@bounded-sh/core 0.0.34 → 0.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6555,7 +6555,30 @@ async function wsIntent(appId, roomRoutePath, intent, isServer) {
6555
6555
  if (!connection || !connection.ws || connection.ws.readyState !== WS_READY_STATE_OPEN || connection.isAuthenticating) {
6556
6556
  return false;
6557
6557
  }
6558
- if (!(await canSendAuthenticatedLiveIntent(connection, isServer))) {
6558
+ // Identity-aware send gate. The server attributes intents to the
6559
+ // CONNECTION's authenticated principal, so the only correct reasons to
6560
+ // refuse the socket are identity ones:
6561
+ // - the socket is anonymous while the user is now authenticated, or
6562
+ // - the current principal DIFFERS from the socket principal.
6563
+ // A mere token rotation for the SAME principal, or a transiently-null
6564
+ // token mid-refresh, must NOT reroute a 30Hz command stream to
6565
+ // per-command HTTP — that collapses delivery rate and unbounds the game's
6566
+ // ack lag (field-debugged: server 500+ commands behind).
6567
+ const currentToken = await getIdToken(isServer).catch(() => null);
6568
+ if (!currentToken) {
6569
+ // Mid-refresh: the socket principal still governs server-side.
6570
+ if (!connection.authTokenFingerprint && !connection.authPrincipal)
6571
+ return false;
6572
+ }
6573
+ else if (!connection.authTokenFingerprint && !connection.authPrincipal) {
6574
+ // Anonymous socket + authenticated user: do not attribute this user's
6575
+ // input to the anon principal. Reauth the socket; caller may fall back.
6576
+ void maybeReauthRoomConnection(connection);
6577
+ return false;
6578
+ }
6579
+ else if (!connectionMatchesAuthToken(connection, currentToken)) {
6580
+ // Different principal (identity switch): never send as the old user.
6581
+ void maybeReauthRoomConnection(connection);
6559
6582
  return false;
6560
6583
  }
6561
6584
  try {
@@ -6566,6 +6589,26 @@ async function wsIntent(appId, roomRoutePath, intent, isServer) {
6566
6589
  return false;
6567
6590
  }
6568
6591
  }
6592
+ // Debounced background reauth: when the client's current token no longer
6593
+ // matches the connection's auth fingerprint (token rotation), refresh the
6594
+ // socket auth ONCE rather than degrading every subsequent send.
6595
+ const reauthInFlight = new WeakSet();
6596
+ async function maybeReauthRoomConnection(connection, isServer) {
6597
+ try {
6598
+ if (reauthInFlight.has(connection))
6599
+ return;
6600
+ reauthInFlight.add(connection);
6601
+ try {
6602
+ await reconnectWithNewAuthV2();
6603
+ }
6604
+ finally {
6605
+ reauthInFlight.delete(connection);
6606
+ }
6607
+ }
6608
+ catch (_a) {
6609
+ // Best-effort: the next send retries; the server still enforces auth.
6610
+ }
6611
+ }
6569
6612
  /**
6570
6613
  * Send a live-room intent over the EXISTING per-room socket and AWAIT the server
6571
6614
  * ack — so a policy/auth denial (401/403/404/503) REJECTS instead of being