From 1fcb3cedc12fd2a922605ab185124d3036589576 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Wed, 29 Jun 2022 16:27:57 +0100 Subject: [PATCH] feat: consistent authentication flow fix: missing suspense on login feat: re-prompt MFA if fail on login --- src/context/index.tsx | 7 ++- src/context/revoltjs/CheckAuth.tsx | 15 ++++++ src/controllers/client/ClientController.tsx | 55 +++++++++++++-------- src/controllers/client/jsx/Binder.tsx | 13 +---- src/pages/app.tsx | 4 +- 5 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/context/index.tsx b/src/context/index.tsx index cc3fcdd1..045e733e 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -41,10 +41,9 @@ export default function Context({ children }: { children: Children }) { - - {children} - {} - + {children} + + diff --git a/src/context/revoltjs/CheckAuth.tsx b/src/context/revoltjs/CheckAuth.tsx index 126ff925..eec8e8e5 100644 --- a/src/context/revoltjs/CheckAuth.tsx +++ b/src/context/revoltjs/CheckAuth.tsx @@ -1,6 +1,8 @@ import { observer } from "mobx-react-lite"; import { Redirect } from "react-router-dom"; +import { Preloader } from "@revoltchat/ui"; + import { clientController } from "../../controllers/client/ClientController"; interface Props { @@ -10,6 +12,10 @@ interface Props { children: Children; } +/** + * Check that we are logged in or out and redirect accordingly. + * Also prevent render until the client is ready to display. + */ export const CheckAuth = observer((props: Props) => { const loggedIn = clientController.isLoggedIn(); @@ -22,5 +28,14 @@ export const CheckAuth = observer((props: Props) => { return ; } + // Block render if client is getting ready to work. + if ( + props.auth && + clientController.isLoggedIn() && + !clientController.isReady() + ) { + return ; + } + return <>{props.children}; }); diff --git a/src/controllers/client/ClientController.tsx b/src/controllers/client/ClientController.tsx index 4666ae5d..fc51c4f6 100644 --- a/src/controllers/client/ClientController.tsx +++ b/src/controllers/client/ClientController.tsx @@ -7,6 +7,7 @@ import { injectController } from "../../lib/window"; import { state } from "../../mobx/State"; import Auth from "../../mobx/stores/Auth"; +import { resetMemberSidebarFetched } from "../../components/navigation/right/MemberSidebar"; import { modalController } from "../modals/ModalController"; import Session from "./Session"; @@ -205,29 +206,37 @@ class ClientController { // Prompt for MFA verificaiton if necessary if (session.result === "MFA") { const { allowed_methods } = session; - const mfa_response: API.MFAResponse | undefined = await new Promise( - (callback) => - modalController.push({ - type: "mfa_flow", - state: "unknown", - available_methods: allowed_methods, - callback, - }), - ); + while (session.result === "MFA") { + const mfa_response: API.MFAResponse | undefined = + await new Promise((callback) => + modalController.push({ + type: "mfa_flow", + state: "unknown", + available_methods: allowed_methods, + callback, + }), + ); - if (typeof mfa_response === "undefined") { - throw "Cancelled"; + if (typeof mfa_response === "undefined") { + break; + } + + try { + session = await this.apiClient.api.post( + "/auth/session/login", + { + mfa_response, + mfa_ticket: session.ticket, + friendly_name, + }, + ); + } catch (err) { + console.error("Failed login:", err); + } } - session = await this.apiClient.api.post("/auth/session/login", { - mfa_response, - mfa_ticket: session.ticket, - friendly_name, - }); - if (session.result === "MFA") { - // unreachable code - return; + throw "Cancelled"; } } @@ -247,12 +256,12 @@ class ClientController { @action logout(user_id: string) { const session = this.sessions.get(user_id); if (session) { - this.sessions.delete(user_id); if (user_id === this.current) { this.current = null; - this.pickNextSession(); } + this.sessions.delete(user_id); + this.pickNextSession(); session.destroy(); } } @@ -272,6 +281,10 @@ class ClientController { */ @action switchAccount(user_id: string) { this.current = user_id; + + // This will allow account switching to work more seamlessly, + // maybe it'll be properly / fully implemented at some point. + resetMemberSidebarFetched(); } } diff --git a/src/controllers/client/jsx/Binder.tsx b/src/controllers/client/jsx/Binder.tsx index 6dea98ac..cbec7403 100644 --- a/src/controllers/client/jsx/Binder.tsx +++ b/src/controllers/client/jsx/Binder.tsx @@ -2,26 +2,17 @@ import { observer } from "mobx-react-lite"; import { useEffect } from "preact/hooks"; -import { Preloader } from "@revoltchat/ui"; - import { state } from "../../../mobx/State"; import { clientController } from "../ClientController"; /** - * Prevent render until the client is ready to display. * Also binds listeners from state to the current client. */ -const Binder: React.FC = ({ children }) => { +const Binder: React.FC = () => { const client = clientController.getReadyClient(); useEffect(() => state.registerListeners(client!), [client]); - - // Block render if client is getting ready to work. - if (clientController.isLoggedIn() && !clientController.isReady()) { - return ; - } - - return <>{children}; + return null; }; export default observer(Binder); diff --git a/src/pages/app.tsx b/src/pages/app.tsx index e2853fad..2251e942 100644 --- a/src/pages/app.tsx +++ b/src/pages/app.tsx @@ -49,7 +49,9 @@ export function App() { - + + +