From 859fec237fd8c2a32efeffc1e232c5d13509624d Mon Sep 17 00:00:00 2001 From: tabarra Date: Sun, 5 Nov 2023 10:55:36 -0300 Subject: [PATCH] wip: auto logout from iframe notice --- .../WebServer/middlewares/authMws.ts | 33 ++++++++++++++++--- package-lock.json | 14 ++++++++ panel/package.json | 1 + panel/src/MockShell.tsx | 9 +++++ panel/src/global.d.ts | 3 ++ panel/src/hooks/auth.ts | 12 ++++++- panel/src/pages/auth/Login.tsx | 7 +++- web/public/js/txadmin/base.js | 2 +- web/public/js/txadmin/main.js | 2 +- 9 files changed, 74 insertions(+), 9 deletions(-) diff --git a/core/components/WebServer/middlewares/authMws.ts b/core/components/WebServer/middlewares/authMws.ts index 3a8353bc7..c816e2315 100644 --- a/core/components/WebServer/middlewares/authMws.ts +++ b/core/components/WebServer/middlewares/authMws.ts @@ -5,6 +5,33 @@ import { ApiAuthErrorResp, ApiToastResp, GenericApiErrorResp } from "@shared/gen import { InitializedCtx } from '../ctxTypes'; const console = consoleFactory(modulename); +const webLogoutPage = ` +

+ User logged out.
+ Redirecting to login page... +

+`; + /** * Intercom auth middleware @@ -39,11 +66,7 @@ export const webAuthMw = async (ctx: InitializedCtx, next: Function) => { if (authResult.rejectReason) { console.verbose.warn(`Invalid session auth: ${authResult.rejectReason}`); } - if (ctx.method === 'GET' && ctx.path !== '/') { - return ctx.response.redirect(`/auth?logout&r=${encodeURIComponent(ctx.path)}`); - } else { - return ctx.response.redirect(`/auth?logout`); - } + return ctx.send(webLogoutPage); } //Adding the admin to the context diff --git a/package-lock.json b/package-lock.json index a5591ae1a..177efbd78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10290,6 +10290,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.9.1.tgz", + "integrity": "sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==", + "engines": { + "node": ">=16.15.0", + "npm": ">=8" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -11279,6 +11292,7 @@ "react-markdown": "^9.0.0", "tailwind-merge": "^1.14.0", "tailwindcss-animate": "^1.0.7", + "usehooks-ts": "^2.9.1", "wouter": "^2.12.1" }, "devDependencies": { diff --git a/panel/package.json b/panel/package.json index f3c5c62a1..039ae52fd 100644 --- a/panel/package.json +++ b/panel/package.json @@ -31,6 +31,7 @@ "react-markdown": "^9.0.0", "tailwind-merge": "^1.14.0", "tailwindcss-animate": "^1.0.7", + "usehooks-ts": "^2.9.1", "wouter": "^2.12.1" }, "devDependencies": { diff --git a/panel/src/MockShell.tsx b/panel/src/MockShell.tsx index abffb482e..77d5fdebe 100644 --- a/panel/src/MockShell.tsx +++ b/panel/src/MockShell.tsx @@ -1,8 +1,10 @@ +import { useEventListener } from 'usehooks-ts'; import { Link, useRoute } from "wouter"; import { cn } from "./lib/utils"; import ShellRouter from "./ShellRouter"; import { useContentRefresh, pageErrorStatusAtom } from "./hooks/mainPageStatus"; import { useAtomValue } from "jotai"; +import { useExpireAuthData } from './hooks/auth'; function MenuLink(props: React.HTMLProps & { href: string }) { @@ -76,6 +78,13 @@ function ServerSidebar() { } export default function MockShell() { + const expireSession = useExpireAuthData(); + useEventListener('message', (e: MessageEventFromIframe) => { + if(e.data.type === 'logoutNotice'){ + expireSession('child iframe'); + } + }); + return (
diff --git a/panel/src/global.d.ts b/panel/src/global.d.ts index 401c1ca41..0ea70fe04 100644 --- a/panel/src/global.d.ts +++ b/panel/src/global.d.ts @@ -1,7 +1,10 @@ import { InjectedTxConsts } from '@shared/otherTypes'; +type LogoutNoticeMessage = { type: 'logoutNotice' } + export declare global { interface Window { txConsts: InjectedTxConsts; } + type MessageEventFromIframe = MessageEvent } diff --git a/panel/src/hooks/auth.ts b/panel/src/hooks/auth.ts index fce98add1..9a31f5a79 100644 --- a/panel/src/hooks/auth.ts +++ b/panel/src/hooks/auth.ts @@ -1,6 +1,6 @@ import { ApiLogoutResp, ReactAuthDataType } from '@shared/authApiTypes'; import { useMutation } from '@tanstack/react-query'; -import { atom, useAtom, useAtomValue } from 'jotai'; +import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'; /** @@ -18,6 +18,16 @@ export const useIsAuthenticated = () => { return useAtomValue(isAuthenticatedAtom); }; +//Wipes auth data from the atom, this is triggered when an api pr page call returns a logout notice +export const useExpireAuthData = () => { + const setAuthData = useSetAtom(authDataAtom); + return (src = 'unknown') => { + console.log(`Logout notice received from '${src}'. Wiping auth data.`); + setAuthData(false); + window.history.replaceState(null, '', '/login#expired'); + } +} + //Generic authentication hook, using it will cause your component to re-render on _any_ auth changes export const useAuth = () => { const [authData, setAuthData] = useAtom(authDataAtom); diff --git a/panel/src/pages/auth/Login.tsx b/panel/src/pages/auth/Login.tsx index df940ced8..f7f1f4bc4 100644 --- a/panel/src/pages/auth/Login.tsx +++ b/panel/src/pages/auth/Login.tsx @@ -75,7 +75,12 @@ export default function Login() { }); }; - const logoutMessage = window.location.hash === '#logout' ? 'Logged Out.' : undefined; + let logoutMessage; + if(window.location.hash === '#logout'){ + logoutMessage = 'Logged Out.'; + } else if(window.location.hash === '#expired'){ + logoutMessage = 'Session Expired.'; + } const displayMessage = errorMessage ?? logoutMessage; //Prefill username/password if dev pass enabled diff --git a/web/public/js/txadmin/base.js b/web/public/js/txadmin/base.js index 7c0c570fd..1e0c2cfed 100644 --- a/web/public/js/txadmin/base.js +++ b/web/public/js/txadmin/base.js @@ -83,7 +83,7 @@ for (let pfp of pfpList) { //================================================================ const checkApiLogoutRefresh = (data) => { if (data.logout === true) { - window.location = `/auth?logout&r=${encodeURIComponent(window.location.pathname)}`; + window.parent.postMessage({ type: 'logoutNotice' }); return true; } else if (data.refresh === true) { window.location.reload(true); diff --git a/web/public/js/txadmin/main.js b/web/public/js/txadmin/main.js index 154cf61d8..9f3bead65 100644 --- a/web/public/js/txadmin/main.js +++ b/web/public/js/txadmin/main.js @@ -219,7 +219,7 @@ const getSocket = (rooms) => { socket.on('logout', () => { console.log('Received logout command from websocket.'); - window.location = `/auth?logout&r=${encodeURIComponent(window.location.pathname)}`; + window.parent.postMessage({ type: 'logoutNotice' }); }); return socket;