ФУНКЦИИ - NODEDC LAUNCHER: fix global logout handoff
This commit is contained in:
parent
8e6d2cea39
commit
ab1e0856d6
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
/>
|
||||
|
||||
<main className="launcher-main">
|
||||
|
|
|
|||
Loading…
Reference in New Issue