v3: fixed authorization bug with stale tokens, fixed private dump access bug, various small fixes across the app
Some checks failed
Build and Publish Docker Image / build-and-push (push) Failing after 20s

This commit is contained in:
khannurien
2026-06-27 10:40:07 +00:00
parent 73e0114bf7
commit 76de798faf
24 changed files with 236 additions and 194 deletions

View File

@@ -1,8 +1,9 @@
import { type ReactNode, useState } from "react";
import { type ReactNode, useEffect, useState } from "react";
import { AuthContext, type AuthContextValue } from "./AuthContext.ts";
import { type AuthResponse, deserializeAuthResponse } from "../model.ts";
import { API_URL } from "../config/api.ts";
function isTokenExpired(token: string): boolean {
try {
@@ -33,6 +34,43 @@ export function AuthProvider({ children }: { children: ReactNode }) {
return parsed;
});
// The `exp` pre-check above only catches expiry; it cannot detect a token the
// server rejects for another reason (e.g. a changed GERBEUR_JWT_SECRET, which
// leaves the signature invalid but the token unexpired). Ask the server — the
// only authority on validity — and clear the session if it returns 401, so the
// UI never shows a false "logged in" state. Re-check when the tab regains
// focus to catch tokens invalidated by a server restart while we were away.
const token = authResponse?.token;
useEffect(() => {
if (!token) return;
const validate = async () => {
try {
const res = await fetch(`${API_URL}/api/users/me`, {
headers: { Authorization: `Bearer ${token}` },
cache: "no-store",
});
if (res.status === 401) {
localStorage.removeItem("authResponse");
setAuthResponse(null);
}
} catch {
// Network error — don't log out an offline user; only an explicit 401 clears the session.
}
};
validate();
const onVisible = () => {
if (document.visibilityState === "visible") validate();
};
globalThis.addEventListener("focus", validate);
document.addEventListener("visibilitychange", onVisible);
return () => {
globalThis.removeEventListener("focus", validate);
document.removeEventListener("visibilitychange", onVisible);
};
}, [token]);
const value: AuthContextValue = { authResponse, setAuthResponse };
return (