fix(auth): stop the re-login dialog hijacking the login screen
Nightly Build / build (push) Successful in 8m11s

On a cold load with no session, both shells mount every component before
their boot auth check resolves, so a dozen gated /api calls 401 in
parallel and the fetch watch raised the re-login dialog over the login
screen the boot check was about to show (.relogin-backdrop is z-10000,
.login-page z-9999). The user typed their password into the modal, which
only closes itself on success — revealing the login page still up with
the app hidden, so they were asked a second time and only a manual
reload got them in.

The dialog is for a session that dies under an open tab, so gate it on
one having ever been established. Recognising that is passive, in the
same fetch wrapper: mobile.html probes /api/auth/me from a classic
inline script that runs before this module exists, so an explicit marker
per shell would never fire there and the dialog would be dead on mobile.
Any 2xx from a gated endpoint proves a session; only the routes
guard.rs::is_public lets through unauthenticated are excluded.

Knock-on: with the report now a no-op on a cold load, the chat's
reconnect loop no longer stopped on it. Retry only in the native shell,
which authenticates on its own — everywhere else something is already
asking for a password.
This commit is contained in:
Daniele
2026-08-10 12:23:45 +01:00
parent 5980bdb5b9
commit 5fb5854ff2
2 changed files with 62 additions and 7 deletions
+10 -4
View File
@@ -2,7 +2,7 @@ import { html, nothing } from 'lit';
import { LightElement } from './base.js';
import { InboxCardsMixin } from './inbox-cards.js';
import { t } from './i18n.js';
import { isSessionExpired, notifySessionExpired, probeSession } from './session-expiry.js';
import { isSessionExpired, isNativeShell, notifySessionExpired, probeSession } from './session-expiry.js';
// Slash commands handled entirely server-side: they reply with a `Done` and never
// echo back as a `user_message`, so they are the only commands rendered
@@ -492,9 +492,15 @@ export class ChatSession extends InboxCardsMixin(LightElement) {
if (isSessionExpired()) return; // the dialog is already up
if ((await probeSession()) === 'expired') {
notifySessionExpired();
// A shell that handles auth itself (native mobile) ignores the report;
// there is no dialog coming, so keep retrying as before.
if (isSessionExpired()) return;
// Only the native shell keeps retrying: it authenticates in the background,
// so a refused upgrade there really can be transient. Everywhere else the
// server has just told us this browser is nobody, and something is already
// asking for a password — the re-login dialog if a session died under the
// tab, or the shell's boot check showing the login screen on a cold load
// (where `notifySessionExpired` is deliberately a no-op). Retrying past
// that is pure noise; the socket comes back on `auth-restored`, or the
// login page reloads the whole page.
if (!isNativeShell()) return;
}
setTimeout(() => this._connectWS(), 2000);
}