Bug
_updateStore() in internal-session.js only includes the authenticator key in persisted data when this.authenticator is non-empty:
_updateStore() {
let data = this.content;
if (!isEmpty(this.authenticator)) {
set(data, 'authenticated', Object.assign(
{ authenticator: this.authenticator },
data.authenticated || {}
));
}
return this.store.persist(data);
}
If this.authenticator is temporarily null due to a race condition (e.g., _clear() runs concurrently with a token refresh that triggers _setup() → _updateStore()), the session is persisted without the authenticator key:
{"authenticated": {"access_token": "...", "refresh_token": "..."}}
On the next page load, restore() reads this data, finds no authenticator key, and immediately clears the session — silently logging the user out with zero network requests.
Reproduction
- Have an authenticated session with a valid refresh token
- Trigger a race condition where
_clear() nulls this.authenticator while _updateStore() is about to persist (e.g., cross-tab logout during a scheduled token refresh)
- Close the tab
- Reopen the app — the user is immediately logged out without any token refresh attempt
Root cause
restore() treats missing authenticator key as "no session":
let { authenticator: authenticatorFactory } = restoredContent.authenticated || {};
if (authenticatorFactory) {
// ... restore session
} else {
// clear session ← THIS CLEARS VALID TOKENS
}
Suggested fix
Either:
-
Prevent _updateStore() from persisting when isAuthenticated is true but authenticator is null — refuse to write corrupt state:
_updateStore() {
if (this.isAuthenticated && isEmpty(this.authenticator)) {
return Promise.resolve(); // skip persist
}
// ... existing logic
}
-
Or preserve the last-known authenticator name so it's always available for persist, even if this.authenticator is transiently null.
Versions
- Verified the bug exists in v1.9.2 and the current master (v8.3.0) — the
_updateStore() logic is identical.
Bug
_updateStore()ininternal-session.jsonly includes theauthenticatorkey in persisted data whenthis.authenticatoris non-empty:If
this.authenticatoris temporarilynulldue to a race condition (e.g.,_clear()runs concurrently with a token refresh that triggers_setup()→_updateStore()), the session is persisted without theauthenticatorkey:{"authenticated": {"access_token": "...", "refresh_token": "..."}}On the next page load,
restore()reads this data, finds noauthenticatorkey, and immediately clears the session — silently logging the user out with zero network requests.Reproduction
_clear()nullsthis.authenticatorwhile_updateStore()is about to persist (e.g., cross-tab logout during a scheduled token refresh)Root cause
restore()treats missingauthenticatorkey as "no session":Suggested fix
Either:
Prevent
_updateStore()from persisting whenisAuthenticatedis true butauthenticatoris null — refuse to write corrupt state:Or preserve the last-known authenticator name so it's always available for persist, even if
this.authenticatoris transiently null.Versions
_updateStore()logic is identical.