From 7ec6b81b5e0a111bf9026494837866ab5855ebe3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Feb 2023 11:53:08 +0100 Subject: [PATCH] feat: Add disable 2FA --- public/locales/en-US/translation.json | 14 +++ src/apis/delete-2fa.ts | 22 ++++ src/apis/index.ts | 2 + .../Settings2FARoute/Delete2FA.tsx | 105 ++++++++++++++++++ src/routes/Settings2FARoute.tsx | 18 ++- 5 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 src/apis/delete-2fa.ts create mode 100644 src/route-widgets/Settings2FARoute/Delete2FA.tsx diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json index 9bce3e8..bf3a3fa 100644 --- a/public/locales/en-US/translation.json +++ b/public/locales/en-US/translation.json @@ -307,6 +307,20 @@ "description": "These codes are used to recover your account if you lose access to your authenticator app. Note them down and store them in a safe place. You will not be able to view them again. Do not store them in your password manager. IF YOU LOSE YOUR RECOVERY CODES, YOU WILL LOSE ACCESS TO YOUR ACCOUNT. WE WILL NOT BE ABLE TO HELP YOU.", "submit": "I have noted down my recovery codes" } + }, + "delete": { + "showAction": "Disable 2FA", + "askType": { + "code": "I have my 2FA code", + "recoveryCode": "I have a recovery code" + }, + "askCode": { + "label": "Code" + }, + "askRecoveryCode": { + "label": "Recovery Code" + }, + "submit": "Disable 2FA" } } }, diff --git a/src/apis/delete-2fa.ts b/src/apis/delete-2fa.ts new file mode 100644 index 0000000..cf98b2b --- /dev/null +++ b/src/apis/delete-2fa.ts @@ -0,0 +1,22 @@ +import {SimpleDetailResponse} from "~/server-types" +import {client} from "~/constants/axios-client" + +export interface Delete2FAData { + code?: string + recoveryCode?: string +} + +export default async function delete2FA({ + recoveryCode, + code, +}: Delete2FAData): Promise { + const {data} = await client.delete(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/setup-otp`, { + withCredentials: true, + data: { + code, + recoveryCode, + }, + }) + + return data +} diff --git a/src/apis/index.ts b/src/apis/index.ts index 1a76ae7..b131e41 100644 --- a/src/apis/index.ts +++ b/src/apis/index.ts @@ -64,3 +64,5 @@ export * from "./setup-2fa" export {default as setup2FA} from "./setup-2fa" export * from "./setup-2fa-verify" export {default as verify2FASetup} from "./setup-2fa-verify" +export * from "./delete-2fa" +export {default as delete2FA} from "./delete-2fa" diff --git a/src/route-widgets/Settings2FARoute/Delete2FA.tsx b/src/route-widgets/Settings2FARoute/Delete2FA.tsx new file mode 100644 index 0000000..771d57e --- /dev/null +++ b/src/route-widgets/Settings2FARoute/Delete2FA.tsx @@ -0,0 +1,105 @@ +import {ReactElement, useState} from "react" +import {AxiosError} from "axios" +import {useTranslation} from "react-i18next" +import {BsPhone, BsShieldLockFill} from "react-icons/bs" +import {MdSettingsBackupRestore} from "react-icons/md" + +import {useMutation} from "@tanstack/react-query" +import {Button, Grid, TextField} from "@mui/material" +import {LoadingButton} from "@mui/lab" + +import {Delete2FAData, delete2FA} from "~/apis" +import {useErrorSuccessSnacks} from "~/hooks" +import {SimpleDetailResponse} from "~/server-types" + +export interface Delete2FAProps { + onSuccess: () => void +} + +export default function Delete2FA({onSuccess}: Delete2FAProps): ReactElement { + const {t} = useTranslation() + const {showError} = useErrorSuccessSnacks() + const {mutate} = useMutation(delete2FA, { + onSuccess, + onError: showError, + }) + + const [view, setView] = useState<"showAction" | "askType" | "askCode" | "askRecoveryCode">( + "showAction", + ) + const [value, setValue] = useState("") + + switch (view) { + case "showAction": + return ( + + ) + + case "askType": + return ( + + + + + + + + + ) + + case "askCode": + return ( + + + setValue(e.target.value)} + /> + + + mutate({code: value})} + variant="contained" + startIcon={} + > + {t("routes.SettingsRoute.2fa.delete.submit")} + + + + ) + + case "askRecoveryCode": + return ( + + + setValue(e.target.value)} + /> + + + mutate({recoveryCode: value})} + variant="contained" + startIcon={} + > + {t("routes.SettingsRoute.2fa.delete.submit")} + + + + ) + } +} diff --git a/src/routes/Settings2FARoute.tsx b/src/routes/Settings2FARoute.tsx index 8595c14..068dc3c 100644 --- a/src/routes/Settings2FARoute.tsx +++ b/src/routes/Settings2FARoute.tsx @@ -3,9 +3,10 @@ import {AxiosError} from "axios" import {useTranslation} from "react-i18next" import {useQuery} from "@tanstack/react-query" -import {Alert} from "@mui/material" +import {Alert, Grid} from "@mui/material" import {QueryResult, SimplePageBuilder} from "~/components" +import Delete2FA from "~/route-widgets/Settings2FARoute/Delete2FA" import Setup2FA from "~/route-widgets/Settings2FARoute/Setup2FA" import getHas2FAEnabled from "~/apis/get-has-2fa-enabled" @@ -19,11 +20,16 @@ export default function Settings2FARoute(): ReactElement { query={query}> {has2FAEnabled => has2FAEnabled ? ( - <> - - {t("routes.SettingsRoute.2fa.alreadyEnabled")} - - + + + + {t("routes.SettingsRoute.2fa.alreadyEnabled")} + + + + + + ) : ( )