Skip to content

Commit

Permalink
wip: auto logout from iframe notice
Browse files Browse the repository at this point in the history
  • Loading branch information
tabarra committed Nov 5, 2023
1 parent 72454b9 commit 859fec2
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 9 deletions.
33 changes: 28 additions & 5 deletions core/components/WebServer/middlewares/authMws.ts
Expand Up @@ -5,6 +5,33 @@ import { ApiAuthErrorResp, ApiToastResp, GenericApiErrorResp } from "@shared/gen
import { InitializedCtx } from '../ctxTypes';
const console = consoleFactory(modulename);

const webLogoutPage = `<style>
.notice {
font-family: sans-serif;
font-size: 1.5em;
text-align: center;
background-color: #1F1F1F;
color: #fff;
padding: 2em;
border-radius: 0.25em;
}
.notice a {
color: #42428A;
}
</style>
<p class="notice">
User logged out. <br>
Redirecting to <a href="/login#expired" target="_parent">login page</a>...
</p>
<script>
// Notify parent window that auth failed
window.parent.postMessage({ type: 'logoutNotice' });
// If parent redirect didn't work, redirect here
setTimeout(function() {
window.location.href = '/login#expired';
}, 2000);
</script>`;


/**
* Intercom auth middleware
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions panel/package.json
Expand Up @@ -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": {
Expand Down
9 changes: 9 additions & 0 deletions 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<HTMLAnchorElement> & { href: string }) {
Expand Down Expand Up @@ -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 (
<div className="h-full min-h-screen">
<Header />
Expand Down
3 changes: 3 additions & 0 deletions 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<LogoutNoticeMessage>
}
12 changes: 11 additions & 1 deletion 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';


/**
Expand All @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion panel/src/pages/auth/Login.tsx
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion web/public/js/txadmin/base.js
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion web/public/js/txadmin/main.js
Expand Up @@ -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;
Expand Down

0 comments on commit 859fec2

Please sign in to comment.