Harden auth and improve user management

This commit is contained in:
2026-03-12 20:28:06 +01:00
parent 1a8e37bd36
commit eb699666d9
26 changed files with 1105 additions and 283 deletions

View File

@@ -6,17 +6,20 @@ import {
useState,
type PropsWithChildren,
} from "react";
import { USER_STORAGE_KEY } from "./storage";
import type { UserOption } from "./types";
import { apiGet } from "./api";
import { AUTH_TOKEN_STORAGE_KEY, USER_STORAGE_KEY } from "./storage";
import type { SessionResponse, UserOption } from "./types";
interface SessionContextValue {
user: UserOption | null;
setUser: (user: UserOption | null) => void;
ready: boolean;
setSession: (session: SessionResponse | null) => void;
}
const SessionContext = createContext<SessionContextValue>({
user: null,
setUser: () => undefined,
ready: false,
setSession: () => undefined,
});
function loadStoredUser(): UserOption | null {
@@ -33,6 +36,39 @@ function loadStoredUser(): UserOption | null {
export function SessionProvider({ children }: PropsWithChildren) {
const [user, setUserState] = useState<UserOption | null>(() => loadStoredUser());
const [ready, setReady] = useState(false);
useEffect(() => {
const token = window.localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
if (!token) {
setReady(true);
return;
}
let cancelled = false;
void apiGet<UserOption>("/session/me")
.then((currentUser) => {
if (!cancelled) {
setUserState(currentUser);
}
})
.catch(() => {
if (!cancelled) {
window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
window.localStorage.removeItem(USER_STORAGE_KEY);
setUserState(null);
}
})
.finally(() => {
if (!cancelled) {
setReady(true);
}
});
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
if (user) {
@@ -42,12 +78,26 @@ export function SessionProvider({ children }: PropsWithChildren) {
window.localStorage.removeItem(USER_STORAGE_KEY);
}, [user]);
function setSession(session: SessionResponse | null) {
if (session) {
window.localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, session.token);
setUserState(session.user);
setReady(true);
return;
}
window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
window.localStorage.removeItem(USER_STORAGE_KEY);
setUserState(null);
setReady(true);
}
const value = useMemo(
() => ({
user,
setUser: setUserState,
ready,
setSession,
}),
[user],
[ready, user],
);
return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;