From ab1e0856d6a5f37e70506659dfc94c188a45acc0 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Mon, 4 May 2026 22:22:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=A3=D0=9D=D0=9A=D0=A6=D0=98=D0=98=20-?= =?UTF-8?q?=20NODEDC=20LAUNCHER:=20fix=20global=20logout=20handoff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/dev-server.mjs | 43 ++++++++++++++++++++++++++++++++++++----- src/app/LauncherApp.tsx | 34 +++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/server/dev-server.mjs b/server/dev-server.mjs index 0b8cb3c..ab19fba 100644 --- a/server/dev-server.mjs +++ b/server/dev-server.mjs @@ -148,27 +148,31 @@ app.get("/auth/logout", asyncRoute(async (req, res) => { res.clearCookie(sessionCookieName, clearCookieOptions()); if (!globalLogout || !config.oidcConfigured) { + setNoStore(res); res.redirect(returnTo); return; } const discovery = await getOidcDiscovery(); const endSessionEndpoint = discovery.end_session_endpoint; + const loginRedirectUrl = buildLoginRedirectUrl(returnTo, { forceLogin: true }); if (!endSessionEndpoint) { - res.type("html").send(renderGlobalLogoutPage(getFrontchannelLogoutUrls(), new URL(returnTo, config.appBaseUrl).toString())); + setNoStore(res); + res.type("html").send(renderGlobalLogoutPage(getFrontchannelLogoutUrls(), loginRedirectUrl)); return; } const logoutUrl = new URL(endSessionEndpoint); logoutUrl.searchParams.set("client_id", config.clientId); - logoutUrl.searchParams.set("post_logout_redirect_uri", new URL(returnTo, config.appBaseUrl).toString()); + logoutUrl.searchParams.set("post_logout_redirect_uri", loginRedirectUrl); if (session?.tokenSet.idToken) { logoutUrl.searchParams.set("id_token_hint", session.tokenSet.idToken); } - res.type("html").send(renderGlobalLogoutPage(getFrontchannelLogoutUrls(), logoutUrl.toString())); + setNoStore(res); + res.type("html").send(renderGlobalLogoutPage(getFrontchannelLogoutUrls(), loginRedirectUrl, logoutUrl.toString())); })); app.get("/api/me", (req, res) => { @@ -915,9 +919,10 @@ function normalizeLogoutUrl(value) { } } -function renderGlobalLogoutPage(frontchannelLogoutUrls, finalRedirectUrl) { +function renderGlobalLogoutPage(frontchannelLogoutUrls, finalRedirectUrl, identityLogoutUrl = null) { const logoutUrlsJson = JSON.stringify(frontchannelLogoutUrls); const redirectUrlJson = JSON.stringify(finalRedirectUrl); + const identityLogoutUrlJson = JSON.stringify(identityLogoutUrl); return ` @@ -941,13 +946,20 @@ function renderGlobalLogoutPage(frontchannelLogoutUrls, finalRedirectUrl) { `; @@ -1251,6 +1263,27 @@ function clearCookieOptions() { return options; } +function setNoStore(res) { + res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"); + res.setHeader("Pragma", "no-cache"); + res.setHeader("Expires", "0"); +} + +function buildLoginRedirectUrl(returnTo, { forceLogin = false } = {}) { + const loginUrl = new URL("/auth/login", config.appBaseUrl); + const cleanReturnTo = sanitizeReturnTo(returnTo); + + if (forceLogin) { + loginUrl.searchParams.set("prompt", "login"); + } + + if (cleanReturnTo !== "/") { + loginUrl.searchParams.set("returnTo", cleanReturnTo); + } + + return loginUrl.toString(); +} + function randomBase64Url(size) { return randomBytes(size).toString("base64url"); } diff --git a/src/app/LauncherApp.tsx b/src/app/LauncherApp.tsx index 680e963..c69429f 100644 --- a/src/app/LauncherApp.tsx +++ b/src/app/LauncherApp.tsx @@ -190,6 +190,38 @@ export function LauncherApp() { window.location.replace(buildLoginRedirectUrl(authSession.loginUrl)); }, [authSession]); + useEffect(() => { + let isMounted = true; + + const validateRestoredSession = (event: PageTransitionEvent) => { + if (!event.persisted) return; + + fetchAuthSession() + .then((session) => { + if (!isMounted) return; + + if (!session.authenticated) { + window.location.replace(buildLoginRedirectUrl(session.loginUrl)); + return; + } + + setAuthSession(session); + }) + .catch(() => { + if (isMounted) { + window.location.replace(buildLoginRedirectUrl("/auth/login")); + } + }); + }; + + window.addEventListener("pageshow", validateRestoredSession); + + return () => { + isMounted = false; + window.removeEventListener("pageshow", validateRestoredSession); + }; + }, []); + useEffect(() => { if (!authSession?.authenticated) return; @@ -493,7 +525,7 @@ export function LauncherApp() { onToggleAdmin={() => setAdminOpen((current) => !current)} onOpenShowcase={() => setAdminOpen(false)} onOpenProfileSettings={() => setProfileSettingsOpen(true)} - onLogout={() => window.location.assign(authSession.logoutUrl)} + onLogout={() => window.location.replace(authSession.logoutUrl)} />