fix(auth): isolate session races and financial caches
TD-001 and TD-002: serialize cookie writes, ignore stale responses and reset financial queries on identity changes. Backend cookie lifecycle regression: ec5780d.
This commit is contained in:
@@ -8,6 +8,7 @@ const HTTP_UNAUTHORIZED = 401;
|
||||
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:3000";
|
||||
|
||||
type AuthHandlers = {
|
||||
generation?: () => number;
|
||||
accessToken: () => string | null;
|
||||
refresh: () => Promise<boolean>;
|
||||
clear: () => void;
|
||||
@@ -41,7 +42,10 @@ async function authenticatedFetch(request: Request): Promise<Response> {
|
||||
const isAuthRequest = new URL(request.url).pathname.startsWith(
|
||||
"/api/v1/auth/",
|
||||
);
|
||||
const token = auth?.accessToken();
|
||||
const session = auth;
|
||||
const generation = session?.generation?.();
|
||||
const isCurrent = () => generation === auth?.generation?.();
|
||||
const token = session?.accessToken();
|
||||
const withToken = (source: Request, value: string) => {
|
||||
const headers = new Headers(source.headers);
|
||||
headers.set("Authorization", `Bearer ${value}`);
|
||||
@@ -51,6 +55,8 @@ async function authenticatedFetch(request: Request): Promise<Response> {
|
||||
token && !isAuthRequest ? withToken(request, token) : request;
|
||||
const retryRequest = authorized.clone();
|
||||
const response = await fetch(authorized);
|
||||
if (!isAuthRequest && !isCurrent())
|
||||
throw new DOMException("Session changed", "AbortError");
|
||||
if (
|
||||
response.status !== HTTP_UNAUTHORIZED ||
|
||||
isAuthRequest ||
|
||||
@@ -63,14 +69,17 @@ async function authenticatedFetch(request: Request): Promise<Response> {
|
||||
)?.code !== "UNAUTHORIZED"
|
||||
)
|
||||
return response;
|
||||
if (!isCurrent()) throw new DOMException("Session changed", "AbortError");
|
||||
if (!(await auth.refresh())) {
|
||||
auth.clear();
|
||||
if (isCurrent()) auth.clear();
|
||||
return response;
|
||||
}
|
||||
if (!isCurrent()) throw new DOMException("Session changed", "AbortError");
|
||||
const refreshedToken = auth.accessToken();
|
||||
return refreshedToken
|
||||
? fetch(withToken(retryRequest, refreshedToken))
|
||||
: response;
|
||||
if (!refreshedToken) return response;
|
||||
const retried = await fetch(withToken(retryRequest, refreshedToken));
|
||||
if (!isCurrent()) throw new DOMException("Session changed", "AbortError");
|
||||
return retried;
|
||||
}
|
||||
|
||||
const apiClient = createClient<paths>({
|
||||
|
||||
Reference in New Issue
Block a user