Harden auth and improve user management
This commit is contained in:
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user