fix: keep terminal stable across auth refresh

This commit is contained in:
2569718930@qq.com
2026-05-30 14:33:26 +08:00
parent ed8c898e0e
commit 1c250906ed
4 changed files with 37 additions and 2 deletions
@@ -119,6 +119,15 @@ export function runTests() {
middlewareSource.indexOf("await refreshMiddlewareSession(request)"),
"middleware must redirect no-cookie page requests without calling Supabase auth",
);
const terminalGateSource = middlewareSource.slice(
middlewareSource.indexOf("async function handleTerminalGate"),
middlewareSource.indexOf("async function handleSupabaseAuthGate"),
);
assert(
terminalGateSource.includes("return response;") &&
!terminalGateSource.includes("return redirectToLogin(request, pathname);\n}"),
"terminal middleware must not navigate long-lived dashboards away when a session cookie exists but claims refresh is transiently unavailable",
);
assert(
middlewareSource.includes("unauthorizedSupabaseSessionResponse()"),
"middleware must reject no-cookie protected API requests without calling Supabase auth",
@@ -954,6 +954,18 @@ function ScanTerminalScreen() {
data: { subscription },
} = supabase.auth.onAuthStateChange(async (event, session) => {
if (event === "SIGNED_OUT") {
try {
const {
data: { session: currentSession },
} = await supabase.auth.getSession();
const accessToken =
currentSession?.access_token || session?.access_token || null;
if (accessToken) {
const payload = await loadAuthProfile(accessToken);
setProAccess((prev) => mergeAccessStateWithAuthPayload(prev, payload));
return;
}
} catch {}
setProAccess(createEmptyAccess(false));
} else if (event === "TOKEN_REFRESHED" || event === "SIGNED_IN") {
try {
@@ -65,4 +65,15 @@ export function runTests() {
chartCanvasSource.includes('compact ? "min-h-[120px]" : "min-h-[220px]"'),
"compact grid charts must not force desktop minimum heights that get clipped inside 3x3 terminal slots",
);
const signedOutBlock = dashboardSource.slice(
dashboardSource.indexOf('if (event === "SIGNED_OUT")'),
dashboardSource.indexOf('} else if (event === "TOKEN_REFRESHED"'),
);
assert(
signedOutBlock.includes("await supabase.auth.getSession()") &&
signedOutBlock.includes("mergeAccessStateWithAuthPayload(prev, payload)") &&
signedOutBlock.indexOf("await supabase.auth.getSession()") <
signedOutBlock.indexOf("setProAccess(createEmptyAccess(false))"),
"terminal auth listener must re-check the current Supabase session before clearing access on SIGNED_OUT events",
);
}
+5 -2
View File
@@ -104,8 +104,11 @@ async function handleTerminalGate(request: NextRequest): Promise<NextResponse> {
return response;
}
// Layer 1: Not logged in → redirect to /auth/login?next=/terminal
return redirectToLogin(request, pathname);
// A session cookie exists, but the edge/server refresh can occasionally fail
// during a long-lived terminal tab. Do not navigate an active dashboard away
// on a transient claims failure; the client can still verify via bearer auth
// and render the in-product access gate if the session is truly gone.
return response;
}
async function handleSupabaseAuthGate(request: NextRequest) {