From 925a511ce35ba40e6e92b6a32fcee42cd671d1bb Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 11 May 2026 18:20:44 -0300 Subject: [PATCH] refactor(client): drop Accounts._unstoreLoginToken monkey-patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remaining client-side Accounts._unstoreLoginToken calls fell into two roles: a monkey-patch that piggybacked on Meteor's token-clear hook to run side effects on logout, and direct calls used to manually wipe the stored credentials. Both are replaceable now that #40442 landed sdk.account.onLogout and #40479 landed the SDK storage helper. - Delete apps/meteor/client/meteor/overrides/unstoreLoginToken.ts (and its barrel entry). CachedStoresManager.ts registers clearAllCachesOnLogout via getDdpSdk().account.onLogout at module init, which fires on the same uid-undefined transition Meteor's monkey-patch was capturing. - AuthenticationProvider's unstoreLoginToken context method collapses from a monkey-patch + restore pair into `(callback) => getDdpSdk().account.onLogout(callback)`. Same return shape (callback -> unsubscribe), so useUnstoreLoginToken consumers stay intact. - AuthenticationProvider.wipeLocalAuth and the auth-error branch in ddpSdk.ts call removeStoredItem for USER_ID / LOGIN_TOKEN / LOGIN_TOKEN_EXPIRES — the three keys Meteor's _unstoreLoginToken clears. Adds LOGIN_TOKEN_EXPIRES to STORAGE_KEYS so storage.ts owns the full set. No behaviour change: same localStorage keys cleared, same logout cleanup ordering. The Accounts import stays in AuthenticationProvider.tsx and ddpSdk.ts because callLoginMethod / loggingIn / getLoginToken / the onEmailVerificationLink + onPageLoadLogin bridges still use it. --- .../lib/cachedStores/CachedStoresManager.ts | 3 +++ apps/meteor/client/lib/sdk/ddpSdk.ts | 8 ++++--- apps/meteor/client/lib/sdk/storage.ts | 1 + apps/meteor/client/meteor/overrides/index.ts | 1 - .../meteor/overrides/unstoreLoginToken.ts | 9 -------- .../AuthenticationProvider.tsx | 21 ++++++------------- 6 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 apps/meteor/client/meteor/overrides/unstoreLoginToken.ts diff --git a/apps/meteor/client/lib/cachedStores/CachedStoresManager.ts b/apps/meteor/client/lib/cachedStores/CachedStoresManager.ts index b899dde4b33a6..8988b36e6c946 100644 --- a/apps/meteor/client/lib/cachedStores/CachedStoresManager.ts +++ b/apps/meteor/client/lib/cachedStores/CachedStoresManager.ts @@ -1,4 +1,5 @@ import type { IWithManageableCache } from './CachedStore'; +import { getDdpSdk } from '../sdk/ddpSdk'; class CachedStoresManager { private items = new Set(); @@ -16,6 +17,8 @@ class CachedStoresManager { const instance = new CachedStoresManager(); +getDdpSdk().account.onLogout(() => instance.clearAllCachesOnLogout()); + export { /** @deprecated */ instance as CachedStoresManager, diff --git a/apps/meteor/client/lib/sdk/ddpSdk.ts b/apps/meteor/client/lib/sdk/ddpSdk.ts index f9cb3dbc2b953..580aa4dcf1471 100644 --- a/apps/meteor/client/lib/sdk/ddpSdk.ts +++ b/apps/meteor/client/lib/sdk/ddpSdk.ts @@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor'; import { createMeteorBackedSdk } from './meteorBackedSdk'; import { isSdkTransportEnabled } from './sdkTransportEnabled'; -import { STORAGE_KEYS, getStoredItem } from './storage'; +import { STORAGE_KEYS, getStoredItem, removeStoredItem } from './storage'; import { userIdStore } from '../user'; const sdkTransportEnabled = isSdkTransportEnabled(); @@ -142,8 +142,10 @@ export const ensureConnectedAndAuthenticated = async (): Promise => { // latter dispatches a `logout` method which itself races against // parallel re-auth flows in CI's parallel-shard environment and // kicked otherwise-healthy tests out. - Accounts._unstoreLoginToken(); - Meteor.connection.setUserId(null); + removeStoredItem(STORAGE_KEYS.USER_ID); + removeStoredItem(STORAGE_KEYS.LOGIN_TOKEN); + removeStoredItem(STORAGE_KEYS.LOGIN_TOKEN_EXPIRES); + (Meteor.connection as unknown as { setUserId: (uid: string | null) => void }).setUserId(null); return; } console.warn('[ddpSdk] loginWithToken failed', error); diff --git a/apps/meteor/client/lib/sdk/storage.ts b/apps/meteor/client/lib/sdk/storage.ts index ba71bf1ab1459..03e437d16959f 100644 --- a/apps/meteor/client/lib/sdk/storage.ts +++ b/apps/meteor/client/lib/sdk/storage.ts @@ -6,6 +6,7 @@ export const STORAGE_KEYS = { USER_ID: 'Meteor.userId', LOGIN_TOKEN: 'Meteor.loginToken', + LOGIN_TOKEN_EXPIRES: 'Meteor.loginTokenExpires', E2EE_PUBLIC_KEY: 'public_key', E2EE_PRIVATE_KEY: 'private_key', E2EE_RANDOM_PASSWORD: 'e2e.randomPassword', diff --git a/apps/meteor/client/meteor/overrides/index.ts b/apps/meteor/client/meteor/overrides/index.ts index b039f3861af3a..776f1fd6a8807 100644 --- a/apps/meteor/client/meteor/overrides/index.ts +++ b/apps/meteor/client/meteor/overrides/index.ts @@ -7,5 +7,4 @@ import './desktopInjection'; import './oauthRedirectUri'; import './settings'; import './totpOnCall'; -import './unstoreLoginToken'; import './userAndUsers'; diff --git a/apps/meteor/client/meteor/overrides/unstoreLoginToken.ts b/apps/meteor/client/meteor/overrides/unstoreLoginToken.ts deleted file mode 100644 index 8adbf1d912e9a..0000000000000 --- a/apps/meteor/client/meteor/overrides/unstoreLoginToken.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Accounts } from 'meteor/accounts-base'; - -import { CachedStoresManager } from '../../lib/cachedStores/CachedStoresManager'; - -const { _unstoreLoginToken } = Accounts; -Accounts._unstoreLoginToken = (...args) => { - _unstoreLoginToken.apply(Accounts, args); - CachedStoresManager.clearAllCachesOnLogout(); -}; diff --git a/apps/meteor/client/providers/AuthenticationProvider/AuthenticationProvider.tsx b/apps/meteor/client/providers/AuthenticationProvider/AuthenticationProvider.tsx index 0bf8b1c30bc8f..8e88ff194d8c4 100644 --- a/apps/meteor/client/providers/AuthenticationProvider/AuthenticationProvider.tsx +++ b/apps/meteor/client/providers/AuthenticationProvider/AuthenticationProvider.tsx @@ -10,6 +10,8 @@ import { useLDAPAndCrowdCollisionWarning } from './hooks/useLDAPAndCrowdCollisio import { capitalize as capitalizeService } from '../../../lib/utils/stringUtils'; import { useReactiveValue } from '../../hooks/useReactiveValue'; import { loginServices } from '../../lib/loginServices'; +import { getDdpSdk } from '../../lib/sdk/ddpSdk'; +import { STORAGE_KEYS, removeStoredItem } from '../../lib/sdk/storage'; export type LoginMethods = keyof typeof Meteor extends infer T ? (T extends `loginWith${string}` ? T : never) : never; @@ -125,27 +127,16 @@ const AuthenticationProvider = ({ children }: AuthenticationProviderProps): Reac }), getLoginToken: () => Accounts.storageLocation.getItem(Accounts.LOGIN_TOKEN_KEY) ?? null, wipeLocalAuth: () => { - try { - Accounts._unstoreLoginToken(); - } catch { - // ignore - } + removeStoredItem(STORAGE_KEYS.USER_ID); + removeStoredItem(STORAGE_KEYS.LOGIN_TOKEN); + removeStoredItem(STORAGE_KEYS.LOGIN_TOKEN_EXPIRES); try { Meteor.connection.setUserId(null); } catch { // ignore } }, - unstoreLoginToken: (callback) => { - const { _unstoreLoginToken } = Accounts; - Accounts._unstoreLoginToken = function (...args) { - callback(); - _unstoreLoginToken.apply(Accounts, args); - }; - return () => { - Accounts._unstoreLoginToken = _unstoreLoginToken; - }; - }, + unstoreLoginToken: (callback) => getDdpSdk().account.onLogout(callback), queryLoginServices: { getCurrentValue: () => loginServices.getLoginServiceButtons(), subscribe: (onStoreChange: () => void) => loginServices.on('changed', onStoreChange),