mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-20 08:15:26 +02:00
feat: Add disable 2FA
This commit is contained in:
parent
564861fc9f
commit
7ec6b81b5e
@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
22
src/apis/delete-2fa.ts
Normal file
22
src/apis/delete-2fa.ts
Normal file
@ -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<SimpleDetailResponse> {
|
||||
const {data} = await client.delete(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/setup-otp`, {
|
||||
withCredentials: true,
|
||||
data: {
|
||||
code,
|
||||
recoveryCode,
|
||||
},
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
@ -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"
|
||||
|
105
src/route-widgets/Settings2FARoute/Delete2FA.tsx
Normal file
105
src/route-widgets/Settings2FARoute/Delete2FA.tsx
Normal file
@ -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<SimpleDetailResponse, AxiosError, Delete2FAData>(delete2FA, {
|
||||
onSuccess,
|
||||
onError: showError,
|
||||
})
|
||||
|
||||
const [view, setView] = useState<"showAction" | "askType" | "askCode" | "askRecoveryCode">(
|
||||
"showAction",
|
||||
)
|
||||
const [value, setValue] = useState<string>("")
|
||||
|
||||
switch (view) {
|
||||
case "showAction":
|
||||
return (
|
||||
<Button onClick={() => setView("askType")} startIcon={<BsShieldLockFill />}>
|
||||
{t("routes.SettingsRoute.2fa.delete.showAction")}
|
||||
</Button>
|
||||
)
|
||||
|
||||
case "askType":
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item>
|
||||
<Button onClick={() => setView("askCode")} startIcon={<BsPhone />}>
|
||||
{t("routes.SettingsRoute.2fa.delete.askType.code")}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
onClick={() => setView("askRecoveryCode")}
|
||||
startIcon={<MdSettingsBackupRestore />}
|
||||
>
|
||||
{t("routes.SettingsRoute.2fa.delete.askType.recoveryCode")}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
|
||||
case "askCode":
|
||||
return (
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t("routes.SettingsRoute.2fa.delete.askCode.label")}
|
||||
value={value}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<LoadingButton
|
||||
onClick={() => mutate({code: value})}
|
||||
variant="contained"
|
||||
startIcon={<BsShieldLockFill />}
|
||||
>
|
||||
{t("routes.SettingsRoute.2fa.delete.submit")}
|
||||
</LoadingButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
|
||||
case "askRecoveryCode":
|
||||
return (
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t("routes.SettingsRoute.2fa.delete.askRecoveryCode.label")}
|
||||
value={value}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<LoadingButton
|
||||
onClick={() => mutate({recoveryCode: value})}
|
||||
variant="contained"
|
||||
startIcon={<BsShieldLockFill />}
|
||||
>
|
||||
{t("routes.SettingsRoute.2fa.delete.submit")}
|
||||
</LoadingButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
}
|
@ -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 {
|
||||
<QueryResult<boolean, AxiosError> query={query}>
|
||||
{has2FAEnabled =>
|
||||
has2FAEnabled ? (
|
||||
<>
|
||||
<Alert severity="success">
|
||||
{t("routes.SettingsRoute.2fa.alreadyEnabled")}
|
||||
</Alert>
|
||||
</>
|
||||
<Grid container spacing={4} direction="column" alignItems="center">
|
||||
<Grid item>
|
||||
<Alert severity="success">
|
||||
{t("routes.SettingsRoute.2fa.alreadyEnabled")}
|
||||
</Alert>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Delete2FA onSuccess={query.refetch} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : (
|
||||
<Setup2FA onSuccess={query.refetch} />
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user