ФУНКЦИИ - NODEDC LAUNCHER: fix global logout handoff

This commit is contained in:
DCCONSTRUCTIONS
2026-05-04 22:22:08 +03:00
parent 8e6d2cea39
commit ab1e0856d6
2 changed files with 71 additions and 6 deletions
+38 -5
View File
@@ -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 `<!doctype html>
<html lang="ru">
@@ -941,13 +946,20 @@ function renderGlobalLogoutPage(frontchannelLogoutUrls, finalRedirectUrl) {
<script>
const logoutUrls = ${logoutUrlsJson};
const finalRedirectUrl = ${redirectUrlJson};
const identityLogoutUrl = ${identityLogoutUrlJson};
for (const logoutUrl of logoutUrls) {
fetch(logoutUrl, { mode: "no-cors", credentials: "include", keepalive: true }).catch(() => undefined);
const image = new Image();
image.referrerPolicy = "no-referrer";
image.src = logoutUrl;
}
window.setTimeout(() => window.location.replace(finalRedirectUrl), 700);
if (identityLogoutUrl) {
fetch(identityLogoutUrl, { mode: "no-cors", credentials: "include", keepalive: true }).catch(() => undefined);
const identityImage = new Image();
identityImage.referrerPolicy = "no-referrer";
identityImage.src = identityLogoutUrl;
}
window.setTimeout(() => window.location.replace(finalRedirectUrl), 900);
</script>
</body>
</html>`;
@@ -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");
}