mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-25 23:22:06 -05:00
feat: consistent authentication flow
fix: missing suspense on login feat: re-prompt MFA if fail on login
This commit is contained in:
parent
0261fec676
commit
1fcb3cedc1
5 changed files with 57 additions and 37 deletions
|
@ -41,10 +41,9 @@ export default function Context({ children }: { children: Children }) {
|
|||
<UIProvider value={uiContext}>
|
||||
<Locale>
|
||||
<Intermediate>
|
||||
<Binder>
|
||||
{children}
|
||||
{<SyncManager />}
|
||||
</Binder>
|
||||
{children}
|
||||
<SyncManager />
|
||||
<Binder />
|
||||
</Intermediate>
|
||||
<ModalRenderer />
|
||||
</Locale>
|
||||
|
|
|
@ -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 <Redirect to="/" />;
|
||||
}
|
||||
|
||||
// Block render if client is getting ready to work.
|
||||
if (
|
||||
props.auth &&
|
||||
clientController.isLoggedIn() &&
|
||||
!clientController.isReady()
|
||||
) {
|
||||
return <Preloader type="spinner" />;
|
||||
}
|
||||
|
||||
return <>{props.children}</>;
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <Preloader type="spinner" />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
return null;
|
||||
};
|
||||
|
||||
export default observer(Binder);
|
||||
|
|
|
@ -49,7 +49,9 @@ export function App() {
|
|||
</Route>
|
||||
<Route path="/login">
|
||||
<CheckAuth>
|
||||
<Login />
|
||||
<LoadSuspense>
|
||||
<Login />
|
||||
</LoadSuspense>
|
||||
</CheckAuth>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
|
|
Loading…
Reference in a new issue