Fix auth session latch: prevent redirects during token refresh
Root cause: AuthSessionStore.isAuthenticated is a computed signal that
returns false during token refresh ('loading' status). Since all routes
use canMatch guards that read isAuthenticated, a token refresh causes
ALL routes to fail guard evaluation simultaneously, redirecting the user
to random pages.
Fix: Add wasEverAuthenticated latch that stays true once set. During
transient 'loading' states, isAuthenticated returns true if the user
was previously authenticated — the session is being refreshed, not lost.
This eliminates the "phantom redirect" bug that made every page in the
app unstable (pages would load then silently navigate away after 1-5
seconds). Verified stable on /setup/identity-access and /evidence/audit-log
with 12-second wait after navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,9 +38,27 @@ export class AuthSessionStore {
|
||||
() => this.sessionSignal()?.tokens.expiresAtEpochMs ?? null
|
||||
);
|
||||
|
||||
readonly isAuthenticated = computed(
|
||||
() => this.sessionSignal() !== null && this.statusSignal() !== 'loading'
|
||||
);
|
||||
/**
|
||||
* True when the user has an active session and is not in a transient
|
||||
* 'loading' state. During token refresh the status briefly becomes
|
||||
* 'loading', which would make this computed return false. To prevent
|
||||
* route guards from redirecting away during that transient window, we
|
||||
* latch a `wasEverAuthenticated` flag that stays true for the entire
|
||||
* browser session once the user has logged in at least once. Guards
|
||||
* reading `isAuthenticated` will see true throughout the refresh cycle.
|
||||
*/
|
||||
private wasEverAuthenticated = false;
|
||||
readonly isAuthenticated = computed(() => {
|
||||
const hasSession = this.sessionSignal() !== null;
|
||||
const notLoading = this.statusSignal() !== 'loading';
|
||||
const authenticated = hasSession && notLoading;
|
||||
if (authenticated) {
|
||||
this.wasEverAuthenticated = true;
|
||||
}
|
||||
// During transient 'loading' states, return true if the user was
|
||||
// previously authenticated — the session is being refreshed, not lost.
|
||||
return authenticated || (this.wasEverAuthenticated && this.statusSignal() === 'loading');
|
||||
});
|
||||
|
||||
readonly tenantId = computed(
|
||||
() =>
|
||||
|
||||
Reference in New Issue
Block a user