M54: harden operations and demo resilience
This commit is contained in:
@@ -29,10 +29,16 @@ function readCachedUser(): CurrentUser | null {
|
||||
}
|
||||
|
||||
function cacheUser(user: CurrentUser | null) {
|
||||
if (user) {
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(user));
|
||||
} else {
|
||||
sessionStorage.removeItem(STORAGE_KEY);
|
||||
try {
|
||||
if (user) {
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(user));
|
||||
} else {
|
||||
sessionStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
} catch {
|
||||
// Session storage is only a paint optimisation. Browser privacy settings or a full
|
||||
// storage quota must never turn a successful server-side login/logout into a client
|
||||
// failure; the HttpOnly session cookie remains authoritative.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,32 +53,39 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [oidcProviderName, setOidcProviderName] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
api.get<SystemStatus>("/api/v1/system/status")
|
||||
const controller = new AbortController();
|
||||
|
||||
// System capabilities and session identity are independent reads. In particular, a
|
||||
// slow public status endpoint must not hold an already valid session (or the login
|
||||
// controls) behind its timeout.
|
||||
void api.get<SystemStatus>("/api/v1/system/status", { signal: controller.signal })
|
||||
.then((system) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setDemoMode(system.demo_mode);
|
||||
setOidcEnabled(system.oidc_enabled);
|
||||
setOidcProviderName(system.oidc_provider_name);
|
||||
})
|
||||
.catch(() => setDemoMode(true))
|
||||
.finally(() => api
|
||||
.get<CurrentUser>("/api/v1/auth/session")
|
||||
.catch(() => {
|
||||
if (!controller.signal.aborted) setDemoMode(true);
|
||||
});
|
||||
|
||||
void api
|
||||
.get<CurrentUser>("/api/v1/auth/session", { signal: controller.signal })
|
||||
.then((confirmed) => {
|
||||
if (cancelled) return;
|
||||
if (controller.signal.aborted) return;
|
||||
setUser(confirmed);
|
||||
cacheUser(confirmed);
|
||||
})
|
||||
.catch(() => {
|
||||
if (cancelled) return;
|
||||
if (controller.signal.aborted) return;
|
||||
setUser(null);
|
||||
cacheUser(null);
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false);
|
||||
}));
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
if (!controller.signal.aborted) setLoading(false);
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
|
||||
Reference in New Issue
Block a user