Session validation
Build and Push Lunchtime Images (Kaniko) / build-and-push (push) Successful in 1m16s
Build and Push Lunchtime Images (Kaniko) / build-and-push (push) Successful in 1m16s
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
|||||||
clearUserId,
|
clearUserId,
|
||||||
ensureUserId,
|
ensureUserId,
|
||||||
hasStoredUserId,
|
hasStoredUserId,
|
||||||
|
USER_SESSION_INVALIDATED_EVENT,
|
||||||
updateUserId,
|
updateUserId,
|
||||||
} from "./lib/userIdentity";
|
} from "./lib/userIdentity";
|
||||||
import { THEME_MODE_KEY } from "./lib/constants";
|
import { THEME_MODE_KEY } from "./lib/constants";
|
||||||
@@ -363,6 +364,28 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleSessionInvalidated = () => {
|
||||||
|
setUserId("");
|
||||||
|
setUserEmail("");
|
||||||
|
setIsOnboardingOpen(true);
|
||||||
|
navigateTo("/");
|
||||||
|
message.warning("Your session is no longer valid. Please sign in again.");
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
USER_SESSION_INVALIDATED_EVENT,
|
||||||
|
handleSessionInvalidated,
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener(
|
||||||
|
USER_SESSION_INVALIDATED_EVENT,
|
||||||
|
handleSessionInvalidated,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const themeConfig = useMemo(
|
const themeConfig = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
algorithm: themeMode === "dark" ? darkAlgorithm : defaultAlgorithm,
|
algorithm: themeMode === "dark" ? darkAlgorithm : defaultAlgorithm,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ensureUserId } from "./userIdentity";
|
import { ensureUserId, invalidateUserSession } from "./userIdentity";
|
||||||
import type { ApiError } from "./types";
|
import type { ApiError } from "./types";
|
||||||
|
|
||||||
const API_BASE = "/api";
|
const API_BASE = "/api";
|
||||||
@@ -17,6 +17,15 @@ export function isNotFoundError(error: unknown) {
|
|||||||
return !!(maybeError && (maybeError.isNotFound || maybeError.status === 404));
|
return !!(maybeError && (maybeError.isNotFound || maybeError.status === 404));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldClearStoredUserId(status: number, detail: unknown) {
|
||||||
|
if (status !== 401) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedDetail = String(detail || "").trim().toLowerCase();
|
||||||
|
return normalizedDetail === "invalid user id" || normalizedDetail === "missing user id";
|
||||||
|
}
|
||||||
|
|
||||||
export async function apiFetch<T = any>(url: string, options: RequestInit = {}): Promise<T> {
|
export async function apiFetch<T = any>(url: string, options: RequestInit = {}): Promise<T> {
|
||||||
const fullUrl = url.startsWith("/api") ? API_BASE + url.slice(4) : url;
|
const fullUrl = url.startsWith("/api") ? API_BASE + url.slice(4) : url;
|
||||||
const userId = ensureUserId();
|
const userId = ensureUserId();
|
||||||
@@ -45,6 +54,10 @@ export async function apiFetch<T = any>(url: string, options: RequestInit = {}):
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const payload = await response.json().catch(() => ({} as { detail?: string }));
|
const payload = await response.json().catch(() => ({} as { detail?: string }));
|
||||||
|
if (shouldClearStoredUserId(response.status, payload?.detail)) {
|
||||||
|
invalidateUserSession();
|
||||||
|
}
|
||||||
|
|
||||||
throw createApiError(payload.detail || `Request failed (${response.status})`, {
|
throw createApiError(payload.detail || `Request failed (${response.status})`, {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { USER_TOKEN_KEY } from "./constants";
|
|||||||
import { getStoredValue, removeStoredValue, setStoredValue } from "./storage";
|
import { getStoredValue, removeStoredValue, setStoredValue } from "./storage";
|
||||||
|
|
||||||
const emailByUserId = new Map<string, string>();
|
const emailByUserId = new Map<string, string>();
|
||||||
|
export const USER_SESSION_INVALIDATED_EVENT = "lunchtime:user-session-invalidated";
|
||||||
|
|
||||||
function generateGuid() {
|
function generateGuid() {
|
||||||
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
||||||
@@ -76,3 +77,11 @@ export function regenerateUserId() {
|
|||||||
export function clearUserId() {
|
export function clearUserId() {
|
||||||
removeStoredValue(USER_TOKEN_KEY);
|
removeStoredValue(USER_TOKEN_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function invalidateUserSession() {
|
||||||
|
clearUserId();
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
window.dispatchEvent(new Event(USER_SESSION_INVALIDATED_EVENT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user