Summary
authStore.signOutFromPotentiallyAuthenticatedInstances (src/features/auth/store/authStore.ts) is the per-entity sweep that Studio's Sign out runs before the central-manager /Logout/. Three defects in it were surfaced by the cross-model review of the #1672 fix and are pre-existing, so they were left out of that PR. None is introduced by the fix; all three are still present after it.
1. Instance logouts are serial, retry-armed, and ahead of the central-manager logout
The loop awaits onInstanceLogoutSubmit once per entity, in order, and only then does useLogout post /Logout/ to the central manager and run logoutOnSuccess (which is what clears localStorage). Every client it uses comes from getInstanceClient, which installs curryRetryGatewayErrors: on a 502/503/504 that interceptor sleeps 5s + 10s + 20s before giving up (src/integrations/api/retryGatewayErrors.ts), and a hung connection burns the client's 60s timeout.
Two signed-in instances, the first answering 503 on operation: 'logout': the second instance's session, the central-manager logout, and the storage wipe all wait ~35s. A user who clicks Sign out and walks away is still fully signed in for that window.
Suggested shape: run the local clears for every entity up front (they already precede each network call), then dispatch the instance logouts with Promise.allSettled and don't make the central-manager logout wait on them — or at least build the logout client without the gateway-retry interceptor.
2. A failed instance logout leaves stored basic-auth credentials on disk
flagForBasicAuth(id, null) and flagForFabricConnect(id, false) run only after a successful POST, inside onInstanceLogoutSubmit (src/integrations/api/instance/auth/onInstanceLogoutSubmit.ts). The sweep's own clears don't touch them — unlike signOutLocally, which does. Normally logoutOnSuccess → clearLocalStorage wipes them anyway, so this only bites when the instance logout fails and the central-manager /Logout/ rejects (offline, 401): then Studio:BasicAuth:<id> (base64 username/password) survives a user-requested sign-out until the next successful logout.
Suggested shape: clear the basic-auth entry and the Fabric Connect flag in the sweep's local-clear block, the way signOutLocally already does, so a failed POST cannot leave them behind.
3. An entity re-added during an await is skipped
The loop is a for…in over potentiallyAuthenticated with an await between iterations, and signOutAllLocally snapshots Object.keys first for exactly this reason. A loadUser in flight when the sweep starts captured its key before the clear, and its flagKeyAsSignedIn on resolve re-adds potentiallyAuthenticated[id]; keys added after a for…in starts are not visited, so the sweep never revisits it, and logoutOnSuccess's localStorage.clear() does not touch the in-memory entry. A same-tab re-login then inherits the prior user's connection — the leak signOutAllLocally was written to close. (Deleting the current key mid-loop is spec-safe; the re-add is the defect.)
Suggested shape: iterate a snapshot of the keys, and drop any entry re-added during the sweep at the end.
Notes
Summary
authStore.signOutFromPotentiallyAuthenticatedInstances(src/features/auth/store/authStore.ts) is the per-entity sweep that Studio's Sign out runs before the central-manager/Logout/. Three defects in it were surfaced by the cross-model review of the #1672 fix and are pre-existing, so they were left out of that PR. None is introduced by the fix; all three are still present after it.1. Instance logouts are serial, retry-armed, and ahead of the central-manager logout
The loop
awaitsonInstanceLogoutSubmitonce per entity, in order, and only then doesuseLogoutpost/Logout/to the central manager and runlogoutOnSuccess(which is what clears localStorage). Every client it uses comes fromgetInstanceClient, which installscurryRetryGatewayErrors: on a 502/503/504 that interceptor sleeps 5s + 10s + 20s before giving up (src/integrations/api/retryGatewayErrors.ts), and a hung connection burns the client's 60s timeout.Two signed-in instances, the first answering 503 on
operation: 'logout': the second instance's session, the central-manager logout, and the storage wipe all wait ~35s. A user who clicks Sign out and walks away is still fully signed in for that window.Suggested shape: run the local clears for every entity up front (they already precede each network call), then dispatch the instance logouts with
Promise.allSettledand don't make the central-manager logout wait on them — or at least build the logout client without the gateway-retry interceptor.2. A failed instance logout leaves stored basic-auth credentials on disk
flagForBasicAuth(id, null)andflagForFabricConnect(id, false)run only after a successful POST, insideonInstanceLogoutSubmit(src/integrations/api/instance/auth/onInstanceLogoutSubmit.ts). The sweep's own clears don't touch them — unlikesignOutLocally, which does. NormallylogoutOnSuccess→clearLocalStoragewipes them anyway, so this only bites when the instance logout fails and the central-manager/Logout/rejects (offline, 401): thenStudio:BasicAuth:<id>(base64 username/password) survives a user-requested sign-out until the next successful logout.Suggested shape: clear the basic-auth entry and the Fabric Connect flag in the sweep's local-clear block, the way
signOutLocallyalready does, so a failed POST cannot leave them behind.3. An entity re-added during an
awaitis skippedThe loop is a
for…inoverpotentiallyAuthenticatedwith anawaitbetween iterations, andsignOutAllLocallysnapshotsObject.keysfirst for exactly this reason. AloadUserin flight when the sweep starts captured its key before the clear, and itsflagKeyAsSignedInon resolve re-addspotentiallyAuthenticated[id]; keys added after afor…instarts are not visited, so the sweep never revisits it, andlogoutOnSuccess'slocalStorage.clear()does not touch the in-memory entry. A same-tab re-login then inherits the prior user's connection — the leaksignOutAllLocallywas written to close. (Deleting the current key mid-loop is spec-safe; the re-add is the defect.)Suggested shape: iterate a snapshot of the keys, and drop any entry re-added during the sweep at the end.
Notes