more auth ree
Some checks failed
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Has been cancelled
CI/CD Pipeline / Deploy to Kubernetes (push) Has been cancelled
CI/CD Pipeline / Build and Push App Docker Image (push) Has been cancelled

This commit is contained in:
yohlo
2026-03-02 22:38:09 -06:00
parent 2551ff8bb3
commit d2e1e5d4f0
5 changed files with 35 additions and 58 deletions

View File

@@ -4,7 +4,6 @@ import {
Outlet, Outlet,
Scripts, Scripts,
createRootRouteWithContext, createRootRouteWithContext,
redirect,
} from "@tanstack/react-router"; } from "@tanstack/react-router";
import * as React from "react"; import * as React from "react";
import { DefaultCatchBoundary } from "@/components/DefaultCatchBoundary"; import { DefaultCatchBoundary } from "@/components/DefaultCatchBoundary";
@@ -133,15 +132,6 @@ export const Route = createRootRouteWithContext<{
throw error; throw error;
} }
if (error instanceof Response && error.status === 440) {
console.log('__root beforeLoad: Session needs refresh, redirecting');
const from = encodeURIComponent(location.pathname + location.search);
throw redirect({
to: "/refresh-session",
search: { redirect: from }
});
}
console.error('__root beforeLoad error:', error); console.error('__root beforeLoad error:', error);
return {}; return {};
} }
@@ -152,6 +142,32 @@ export const Route = createRootRouteWithContext<{
function RootComponent() { function RootComponent() {
React.useEffect(() => { React.useEffect(() => {
ensureSuperTokensFrontend(); ensureSuperTokensFrontend();
if (typeof window !== 'undefined') {
const cookies = document.cookie.split(';');
const accessTokenCookies = cookies.filter(c => c.trim().startsWith('sAccessToken='));
if (accessTokenCookies.length > 1) {
console.warn(`[Root] Found ${accessTokenCookies.length} duplicate sAccessToken cookies - clearing all SuperTokens cookies`);
const cookieNames = ['sAccessToken', 'sRefreshToken', 'sIdRefreshToken', 'sFrontToken'];
const cookieDomain = (window as any).__COOKIE_DOMAIN__ || undefined;
cookieNames.forEach(name => {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
if (cookieDomain) {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${cookieDomain}`;
}
});
console.log('[Root] Cleared duplicate cookies - page will reload to establish fresh session');
setTimeout(() => {
window.location.reload();
}, 100);
}
}
}, []); }, []);
return ( return (

View File

@@ -56,21 +56,8 @@ export const fetchMe = createServerFn()
} }
if (error?.message === "SESSION_REFRESH_REQUIRED") { if (error?.message === "SESSION_REFRESH_REQUIRED") {
logger.info("FetchMe: Session refresh required (server function)"); logger.info("FetchMe: Session refresh required - returning empty auth (client will handle cleanup)");
throw new Response( return { user: undefined, roles: [], metadata: {}, phone: undefined };
JSON.stringify({
error: "SESSION_REFRESH_REQUIRED",
message: "Session needs to be refreshed",
shouldRetry: true
}),
{
status: 440,
headers: {
"Content-Type": "application/json",
"X-Session-Expired": "true"
}
}
);
} }
if (error?.message === "Unauthenticated") { if (error?.message === "Unauthenticated") {

View File

@@ -10,32 +10,14 @@ export async function getSessionForStart(request: Request, options?: { sessionRe
if (cookieHeader) { if (cookieHeader) {
const tokens = cookieHeader.match(/sAccessToken=([^;]+)/g); const tokens = cookieHeader.match(/sAccessToken=([^;]+)/g);
if (tokens && tokens.length > 1) { if (tokens && tokens.length > 1) {
logger.warn(`Detected ${tokens.length} duplicate sAccessToken cookies, cleaning up`); logger.warn(`Detected ${tokens.length} duplicate sAccessToken cookies - session is broken, forcing cleanup`);
const parsedTokens = tokens.map(tokenStr => { return {
const token = tokenStr.replace('sAccessToken=', ''); hasToken: false,
try { needsRefresh: true,
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); error: 'DUPLICATE_COOKIES_DETECTED',
return { token, exp: payload.exp, iat: payload.iat }; duplicateCount: tokens.length
} catch (e) { };
logger.error('Failed to parse token', e);
return { token, exp: 0, iat: 0 };
}
});
parsedTokens.sort((a, b) => b.exp - a.exp);
const freshestToken = parsedTokens[0];
logger.info(`Using freshest token: exp=${freshestToken.exp}, iat=${freshestToken.iat}`);
const cleanedCookie = cookieHeader
.split(';')
.filter(c => !c.trim().startsWith('sAccessToken='))
.join(';') + `; sAccessToken=${freshestToken.token}`;
const cleanedHeaders = new Headers(request.headers);
cleanedHeaders.set('cookie', cleanedCookie);
request = new Request(request, { headers: cleanedHeaders });
} }
} }

View File

@@ -23,9 +23,6 @@ export async function ensureServerQueryData<TData>(
if (error?.options?.to && error?.options?.statusCode) { if (error?.options?.to && error?.options?.statusCode) {
throw error; throw error;
} }
if (error instanceof Response && error.status === 440) {
throw error;
}
throw error; throw error;
} }
} }

View File

@@ -34,11 +34,6 @@ export const toServerResult = async <T>(
} }
} }
if (error instanceof Response && error.status === 440) {
logger.info('toServerResult: Re-throwing 440 Response for session refresh');
throw error;
}
const duration = Date.now() - startTime; const duration = Date.now() - startTime;
logger.error('Server Fn Error', error); logger.error('Server Fn Error', error);