fix(auth): auth pages reload when a session appears in another tab

Fixes #69

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-12 20:28:42 -04:00
co-authored by Claude Opus 5
parent d0017e4a78
commit 086e4b3b03
2 changed files with 40 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
import { useEffect } from 'react';
import { getFallbackSession, subscribeSessionChanges } from '../state/sessions';
/**
* #69 — `useSessionSync` (the full cross-tab session sync, with in-place token
* rotation handling) is mounted only inside the authenticated `ClientRoot`
* shell, so a tab parked on the login/register/OIDC-callback pages never
* learns that another tab just signed in. Left alone, that tab keeps showing
* the login form and, if submitted, starts a second device/session for the
* same account.
*
* This is a deliberately lighter watcher for the unauthenticated routes: it
* only cares about a session APPEARING where there was none before (a fresh
* login completed in another tab) and reloads so this tab picks up the
* authenticated shell instead. It does not need `useSessionSync`'s
* rotation/relogin handling since there is no running client here to keep in
* sync — do not import/duplicate that logic, just reload.
*/
export function useSessionAppeared(): void {
useEffect(() => {
// If we already have a session, there's nothing to watch for here — the
// authenticated shell's own useSessionSync takes over once mounted.
if (getFallbackSession()) return undefined;
const unsubscribe = subscribeSessionChanges((session) => {
if (session) {
window.location.reload();
}
});
return unsubscribe;
}, []);
}
+7
View File
@@ -26,6 +26,7 @@ import { settingsAtom } from '../state/settings';
import { isWithinTimeWindow } from '../utils/timeWindow';
import { LotusToastContainer } from '../features/toast/LotusToastContainer';
import { useTauriNotificationBadge } from '../hooks/useTauriNotificationBadge';
import { useSessionAppeared } from '../hooks/useSessionAppeared';
import { useTauriWindowChrome } from '../hooks/useTauriWindowChrome';
import { isTauri } from '../hooks/useTauri';
import { TitleBar } from '../features/desktop/TitleBar';
@@ -165,6 +166,12 @@ const queryClient = new QueryClient();
function App() {
const screenSize = useScreenSize();
useCompositionEndTracking();
// #69 — cross-tab session sync (useSessionSync) only mounts inside the
// authenticated ClientRoot shell, so a tab still on the unauthenticated
// routes (login/register/OIDC callback) never learns that another tab just
// signed in. This lighter watcher covers that gap by reloading once a
// session appears where there was none.
useSessionAppeared();
const portalContainer = document.getElementById('portalContainer') ?? undefined;