Enable / Disable 2FA
This commit is contained in:
@@ -13,11 +13,11 @@ import config from "../config";
|
|||||||
import NextUIFormikInput from "./NextUIFormikInput";
|
import NextUIFormikInput from "./NextUIFormikInput";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { ChevronLeftIcon } from "../icons";
|
import { ChevronLeftIcon } from "../icons";
|
||||||
import { popErrorToast, popToast } from "../utilities";
|
import { checkTwoFactorStatus, popErrorToast, popToast } from "../utilities";
|
||||||
import { retrieveUserInformation } from "../security/users";
|
import { retrieveUserInformation } from "../security/users";
|
||||||
import instance from "../security/http";
|
import instance from "../security/http";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useState } from "react";
|
||||||
import AuthCode, { AuthCodeRef } from "react-auth-code-input";
|
import TwoFactorAuthenticationModule from "./TwoFactorAuthenticationModule";
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
email: Yup.string()
|
email: Yup.string()
|
||||||
@@ -42,10 +42,6 @@ export default function SignInModule() {
|
|||||||
|
|
||||||
const [twoFactorModal, setTwoFactorModal] = useState(false);
|
const [twoFactorModal, setTwoFactorModal] = useState(false);
|
||||||
const [userLoginInformation, setUserLoginInformation] = useState<any>();
|
const [userLoginInformation, setUserLoginInformation] = useState<any>();
|
||||||
const [twoFactorToken, setTwoFactorToken] = useState("");
|
|
||||||
const [twoFactorVerifying, setTwoFactorVerifying] = useState(false);
|
|
||||||
|
|
||||||
const AuthInputRef = useRef<AuthCodeRef>(null);
|
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
email: "",
|
email: "",
|
||||||
@@ -85,10 +81,9 @@ export default function SignInModule() {
|
|||||||
|
|
||||||
const handleSubmit = (values: any): void => {
|
const handleSubmit = (values: any): void => {
|
||||||
setUserLoginInformation(values);
|
setUserLoginInformation(values);
|
||||||
instance
|
checkTwoFactorStatus(values.email)
|
||||||
.post("/users/has-2fa", { email: values.email })
|
|
||||||
.then((answer) => {
|
.then((answer) => {
|
||||||
if (answer.data.enabled) {
|
if (answer) {
|
||||||
setTwoFactorModal(true);
|
setTwoFactorModal(true);
|
||||||
} else {
|
} else {
|
||||||
proceedWithLogin(values);
|
proceedWithLogin(values);
|
||||||
@@ -99,32 +94,6 @@ export default function SignInModule() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!(twoFactorToken.length == 6 && !twoFactorVerifying)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTwoFactorVerifying(true);
|
|
||||||
instance
|
|
||||||
.post("/users/verify-2fa", {
|
|
||||||
email: userLoginInformation.email,
|
|
||||||
token: twoFactorToken,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
proceedWithLogin();
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
popErrorToast(error);
|
|
||||||
AuthInputRef.current?.clear();
|
|
||||||
setTwoFactorToken("");
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
AuthInputRef.current?.clear();
|
|
||||||
setTwoFactorToken("");
|
|
||||||
setTwoFactorVerifying(false);
|
|
||||||
});
|
|
||||||
}, [twoFactorToken]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-16">
|
<div className="flex flex-col gap-16">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
@@ -190,22 +159,12 @@ export default function SignInModule() {
|
|||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>Two-Factor Authentication</ModalHeader>
|
<ModalHeader>Two-Factor Authentication</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div className="text-center flex flex-col gap-4">
|
{userLoginInformation && (
|
||||||
<AuthCode
|
<TwoFactorAuthenticationModule
|
||||||
containerClassName="flex flex-row gap-4 w-full justify-center"
|
email={userLoginInformation.email}
|
||||||
inputClassName="w-16 h-16 text-4xl text-center rounded-lg my-2 bg-neutral-100 dark:bg-neutral-800 border-2 border-neutral-200 dark:border-neutral-700 "
|
onTwoFactorSuccess={proceedWithLogin}
|
||||||
length={6}
|
|
||||||
allowedCharacters="numeric"
|
|
||||||
ref={AuthInputRef}
|
|
||||||
disabled={twoFactorVerifying}
|
|
||||||
onChange={(value) => {
|
|
||||||
setTwoFactorToken(value);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<p className="text-md opacity-50">
|
)}
|
||||||
Please enter the 6 digits passcode from your authenticator app.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter></ModalFooter>
|
<ModalFooter></ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
59
client/src/components/TwoFactorAuthenticationModule.tsx
Normal file
59
client/src/components/TwoFactorAuthenticationModule.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import AuthCode, { AuthCodeRef } from "react-auth-code-input";
|
||||||
|
import instance from "../security/http";
|
||||||
|
import { popErrorToast } from "../utilities";
|
||||||
|
|
||||||
|
export default function TwoFactorsAuthenticationModule({
|
||||||
|
email,
|
||||||
|
onTwoFactorSuccess,
|
||||||
|
}: {
|
||||||
|
email: string;
|
||||||
|
onTwoFactorSuccess: () => void;
|
||||||
|
}) {
|
||||||
|
const AuthInputRef = useRef<AuthCodeRef>(null);
|
||||||
|
const [twoFactorToken, setTwoFactorToken] = useState("");
|
||||||
|
const [twoFactorVerifying, setTwoFactorVerifying] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!(twoFactorToken.length == 6 && !twoFactorVerifying)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTwoFactorVerifying(true);
|
||||||
|
instance
|
||||||
|
.post("/users/verify-2fa", {
|
||||||
|
email: email,
|
||||||
|
token: twoFactorToken,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
onTwoFactorSuccess();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
popErrorToast(error);
|
||||||
|
AuthInputRef.current?.clear();
|
||||||
|
setTwoFactorToken("");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
AuthInputRef.current?.clear();
|
||||||
|
setTwoFactorToken("");
|
||||||
|
setTwoFactorVerifying(false);
|
||||||
|
});
|
||||||
|
}, [twoFactorToken]);
|
||||||
|
return (
|
||||||
|
<div className="text-center flex flex-col gap-4">
|
||||||
|
<AuthCode
|
||||||
|
containerClassName="flex flex-row gap-4 w-full justify-center"
|
||||||
|
inputClassName="w-16 h-16 text-4xl text-center rounded-lg my-2 bg-neutral-100 dark:bg-neutral-800 border-2 border-neutral-200 dark:border-neutral-700 "
|
||||||
|
length={6}
|
||||||
|
allowedCharacters="numeric"
|
||||||
|
ref={AuthInputRef}
|
||||||
|
disabled={twoFactorVerifying}
|
||||||
|
onChange={(value) => {
|
||||||
|
setTwoFactorToken(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p className="text-md opacity-50">
|
||||||
|
Please enter the 6 digits passcode from your authenticator app.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
173
client/src/components/TwoFactorsAuthenticationSetupModule.tsx
Normal file
173
client/src/components/TwoFactorsAuthenticationSetupModule.tsx
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import { retrieveUserInformation } from "../security/users";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Button, Image, Input } from "@nextui-org/react";
|
||||||
|
import instance from "../security/http";
|
||||||
|
import { checkTwoFactorStatus } from "../utilities";
|
||||||
|
import TwoFactorAuthenticationModule from "./TwoFactorAuthenticationModule";
|
||||||
|
|
||||||
|
export default function TwoFactorsAuthenticationSetupModule({
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
onClose: () => void;
|
||||||
|
}) {
|
||||||
|
const [userInformation, setUserInformation] = useState<any>();
|
||||||
|
const [setup2FAStepperCount, setSetup2FAStepperCount] = useState(0);
|
||||||
|
const [disable2FAStepperCount, setDisable2FAStepperCount] = useState(0);
|
||||||
|
const [isTwoFactorEnabled, setIsTwoFactorEnabled] = useState(false);
|
||||||
|
const [setupQRBase64, setSetupQRBase64] = useState("");
|
||||||
|
const [setupBase32Secret, setSetupBase32Secret] = useState("");
|
||||||
|
|
||||||
|
const disableTwoFactor = async () => {
|
||||||
|
instance
|
||||||
|
.post("/users/disable-2fa", {
|
||||||
|
id: userInformation.id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setDisable2FAStepperCount(1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const enableTwoFactor = () => {
|
||||||
|
instance
|
||||||
|
.post("/users/enable-2fa/" + userInformation.id)
|
||||||
|
.then((response) => {
|
||||||
|
setSetupQRBase64(response.data.data.qrCodeUrl);
|
||||||
|
setSetupBase32Secret(response.data.data.secret);
|
||||||
|
setSetup2FAStepperCount(1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const testTwoFactor = () => {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
retrieveUserInformation().then((response) => {
|
||||||
|
setUserInformation(response);
|
||||||
|
checkTwoFactorStatus(response.email).then((answer) => {
|
||||||
|
setIsTwoFactorEnabled(answer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{userInformation && (
|
||||||
|
<>
|
||||||
|
{!isTwoFactorEnabled && (
|
||||||
|
<div>
|
||||||
|
{setup2FAStepperCount === 0 && (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<p>
|
||||||
|
This setup will guide you through the enabling of
|
||||||
|
Two-Factors Authorization (2FA).
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
enableTwoFactor();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{setup2FAStepperCount === 1 && (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<p>
|
||||||
|
Please scan the QR code below using an authenticator app of
|
||||||
|
your choice.
|
||||||
|
</p>
|
||||||
|
{setupQRBase64 && (
|
||||||
|
<Image src={setupQRBase64} alt="2FA SETUP QR" />
|
||||||
|
)}
|
||||||
|
<p>Or alternatively, manually enter the secret in the app:</p>
|
||||||
|
<Input value={setupBase32Secret} readOnly />
|
||||||
|
<div className="w-full flex flex-row justify-end">
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
setSetup2FAStepperCount(2);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{setup2FAStepperCount === 2 && (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<p>Let's give it a try.</p>
|
||||||
|
<TwoFactorAuthenticationModule
|
||||||
|
email={userInformation.email}
|
||||||
|
onTwoFactorSuccess={() => {
|
||||||
|
setSetup2FAStepperCount(3);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="w-full flex flex-row justify-end">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onPress={() => {
|
||||||
|
setSetup2FAStepperCount(3);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Skip
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
enableTwoFactor();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Setup again
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{setup2FAStepperCount === 3 && (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<p>
|
||||||
|
All set! You will be asked to provide the passcode next time
|
||||||
|
you log in.
|
||||||
|
</p>
|
||||||
|
<div className="w-full flex flex-row justify-end">
|
||||||
|
<Button onPress={onClose}>Finish</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isTwoFactorEnabled && (
|
||||||
|
<div>
|
||||||
|
{disable2FAStepperCount === 0 && (
|
||||||
|
<div className="flex flex-col gap-4 w-full">
|
||||||
|
<p>
|
||||||
|
Are you sure you want to disable Two-Factors Authorization
|
||||||
|
(2FA)?
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-row gap-2 w-full justify-end">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="danger"
|
||||||
|
onPress={() => {
|
||||||
|
disableTwoFactor();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
<Button color="primary" onPress={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{disable2FAStepperCount === 1 && (
|
||||||
|
<div className="flex flex-col gap-4 w-full">
|
||||||
|
<p>2FA has been disabled.</p>
|
||||||
|
<div className="w-full flex flex-row justify-end">
|
||||||
|
<Button onPress={onClose}>Finish</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -18,15 +18,17 @@ import { Form, Formik } from "formik";
|
|||||||
import NextUIFormikInput from "./NextUIFormikInput";
|
import NextUIFormikInput from "./NextUIFormikInput";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import UserProfilePicture from "./UserProfilePicture";
|
import UserProfilePicture from "./UserProfilePicture";
|
||||||
import { popErrorToast, popToast } from "../utilities";
|
import { checkTwoFactorStatus, popErrorToast, popToast } from "../utilities";
|
||||||
import instance from "../security/http";
|
import instance from "../security/http";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import NextUIFormikSelect from "./NextUIFormikSelect";
|
import NextUIFormikSelect from "./NextUIFormikSelect";
|
||||||
|
import TwoFactorsAuthenticationSetupModule from "./TwoFactorsAuthenticationSetupModule";
|
||||||
|
|
||||||
export default function UpdateAccountModule() {
|
export default function UpdateAccountModule() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [userInformation, setUserInformation] = useState<any>();
|
const [userInformation, setUserInformation] = useState<any>();
|
||||||
const [townCouncils, setTownCouncils] = useState<string[]>([]);
|
const [townCouncils, setTownCouncils] = useState<string[]>([]);
|
||||||
|
const [is2FAEnabled, setIs2FAEnabled] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isArchiveDialogOpen,
|
isOpen: isArchiveDialogOpen,
|
||||||
@@ -40,6 +42,12 @@ export default function UpdateAccountModule() {
|
|||||||
onOpenChange: onResetPasswordOpenChange,
|
onOpenChange: onResetPasswordOpenChange,
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
|
const {
|
||||||
|
isOpen: isTwoFactorsAuthenticationOpen,
|
||||||
|
onOpen: onTwoFactorsAuthenticationOpen,
|
||||||
|
onOpenChange: onTwoFactorsAuthenticationOpenChange,
|
||||||
|
} = useDisclosure();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
retrieveUserInformation()
|
retrieveUserInformation()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@@ -49,6 +57,9 @@ export default function UpdateAccountModule() {
|
|||||||
.then((values) => {
|
.then((values) => {
|
||||||
setTownCouncils(JSON.parse(values.data).townCouncils);
|
setTownCouncils(JSON.parse(values.data).townCouncils);
|
||||||
});
|
});
|
||||||
|
checkTwoFactorStatus(response.email).then((answer) => {
|
||||||
|
setIs2FAEnabled(answer);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
navigate("/signin");
|
navigate("/signin");
|
||||||
@@ -266,7 +277,7 @@ export default function UpdateAccountModule() {
|
|||||||
}
|
}
|
||||||
className="rounded-xl -m-2 *:px-4"
|
className="rounded-xl -m-2 *:px-4"
|
||||||
>
|
>
|
||||||
<Card className="flex flex-row justify-between *:my-auto bg-primary-50 dark:bg-primary-950 p-4 my-2">
|
<Card className="flex flex-col gap-4 justify-between *:my-auto bg-primary-50 dark:bg-primary-950 p-4 my-2">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<p className="text-lg">Danger zone</p>
|
<p className="text-lg">Danger zone</p>
|
||||||
<p className="opacity-50">
|
<p className="opacity-50">
|
||||||
@@ -275,17 +286,16 @@ export default function UpdateAccountModule() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-4">
|
<div className="flex flex-row gap-4">
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color={is2FAEnabled ? "danger" : "secondary"}
|
||||||
variant="light"
|
onPress={onTwoFactorsAuthenticationOpen}
|
||||||
onPress={onResetPasswordOpen}
|
|
||||||
>
|
>
|
||||||
|
{is2FAEnabled ? "Disable" : "Enable"} Two-Factors
|
||||||
|
Authentication
|
||||||
|
</Button>
|
||||||
|
<Button color="danger" onPress={onResetPasswordOpen}>
|
||||||
Reset your password
|
Reset your password
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button color="danger" onPress={onArchiveDialogOpen}>
|
||||||
color="danger"
|
|
||||||
variant="flat"
|
|
||||||
onPress={onArchiveDialogOpen}
|
|
||||||
>
|
|
||||||
Archive this account
|
Archive this account
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -379,6 +389,40 @@ export default function UpdateAccountModule() {
|
|||||||
}}
|
}}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Two-Factors Authorization Modal */}
|
||||||
|
<Modal
|
||||||
|
isOpen={isTwoFactorsAuthenticationOpen}
|
||||||
|
onOpenChange={onTwoFactorsAuthenticationOpenChange}
|
||||||
|
size="xl"
|
||||||
|
>
|
||||||
|
<ModalContent>
|
||||||
|
{(onClose) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
|
Set up Two-Factors Authentication
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<div className="w-full h-full pb-4">
|
||||||
|
<TwoFactorsAuthenticationSetupModule
|
||||||
|
onClose={() => {
|
||||||
|
checkTwoFactorStatus(userInformation.email)
|
||||||
|
.then((answer) => {
|
||||||
|
setIs2FAEnabled(answer);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
onClose();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ModalBody>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import exportFromJSON, { ExportType } from "export-from-json";
|
import exportFromJSON, { ExportType } from "export-from-json";
|
||||||
|
import instance from "./security/http";
|
||||||
|
|
||||||
export function getTimeOfDay(): number {
|
export function getTimeOfDay(): number {
|
||||||
const currentHour = new Date().getHours();
|
const currentHour = new Date().getHours();
|
||||||
@@ -48,3 +49,14 @@ export const exportData = (
|
|||||||
) => {
|
) => {
|
||||||
exportFromJSON({ data, fileName, exportType });
|
exportFromJSON({ data, fileName, exportType });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const checkTwoFactorStatus = async (email: string) => {
|
||||||
|
try {
|
||||||
|
const answer = await instance.post("/users/has-2fa", {
|
||||||
|
email: email,
|
||||||
|
});
|
||||||
|
return answer.data.enabled;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -546,6 +546,27 @@ router.post("/enable-2fa/:id", async (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post("/disable-2fa/", async (req, res) => {
|
||||||
|
let id = req.body.id;
|
||||||
|
const user = User.findByPk(id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).send("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
await User.update(
|
||||||
|
{ secret: null },
|
||||||
|
{
|
||||||
|
where: { id: id },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
status: "success",
|
||||||
|
message: "2FA disabled",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.post("/has-2fa", async (req, res) => {
|
router.post("/has-2fa", async (req, res) => {
|
||||||
let email = req.body.email;
|
let email = req.body.email;
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
|
|||||||
Reference in New Issue
Block a user