Skip to content

Commit 135bb4b

Browse files
ggazzoclaude
andcommitted
fix(sdk): reset Accounts._lastLoginTokenWhenPolled on SDK disconnect
Belt-and-suspenders for the EE force-logout path. The existing recovery mechanisms (useForceLogout via stream message; _reconnectStopper via fire('reset') calling makeClientLoggedOut on auth failure) BOTH clear _lastLoginTokenWhenPolled when they run. But in microservices the notify-user/<uid>/force_logout stream traverses rocketchat-main → broker → ddp-streamer → WS while the close fires directly on ddp-streamer — so the stream message can be lost mid-flight, leaving useForceLogout out of the picture. _reconnectStopper still covers that case by retrying with the stale token and falling into makeClientLoggedOut, but only if a previous successful login registered the stopper. Wire a direct sdk.connection.on('disconnected') listener that nulls _lastLoginTokenWhenPolled. The next _pollStoredLoginToken call (from the 3s timer or a test's loginByUserState) now always compares against null and fires a login if a token is stored, regardless of whether the two earlier paths ran. In normal blip-and-recover network drops the SDK auto-relogin succeeds and the next poll re-sends a (no-op for the server) login resume — extra roundtrip but no user-visible effect. Verified locally with e2ee-passphrase-management :76/:87 and e2ee-key-reset (force-logout) all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7742877 commit 135bb4b

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

apps/meteor/client/meteor/overrides/stubMeteorStream.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Accounts } from 'meteor/accounts-base';
12
import { DDPCommon } from 'meteor/ddp-common';
23
import { Meteor } from 'meteor/meteor';
34
import { Tracker } from 'meteor/tracker';
@@ -247,3 +248,21 @@ sdk.connection.on('connected', () => {
247248
console.warn('[stubMeteorStream] reset on SDK reconnect failed', err);
248249
}
249250
});
251+
252+
// Belt-and-suspenders: when the underlying SDK socket disconnects, also reset
253+
// `Accounts._lastLoginTokenWhenPolled` so the next `_pollStoredLoginToken`
254+
// (whether triggered by the 3s polling timer or an external poke like a test's
255+
// `loginByUserState`) is forced to compare against `null` and fire a fresh
256+
// login if the stored token still exists. This covers the gap where neither
257+
// `useForceLogout` (stream message lost in the broker race) nor
258+
// `_reconnectStopper`'s `makeClientLoggedOut` ran — without this, a stored
259+
// token equal to the cached `_lastLoginTokenWhenPolled` short-circuits the
260+
// poller and the user sits with stale credentials until the next genuine
261+
// token rotation.
262+
sdk.connection.on('disconnected', () => {
263+
try {
264+
(Accounts as unknown as { _lastLoginTokenWhenPolled?: string | null })._lastLoginTokenWhenPolled = null;
265+
} catch {
266+
// ignore — we just want the poller to wake up next time
267+
}
268+
});

0 commit comments

Comments
 (0)