import { Key, ShieldCheck } from "@medusajs/icons" import type { AuthMfaSetupResponse } from "@medusajs/js-sdk" import { Badge, Button, Container, Heading, Text, toast, usePrompt, } from "@medusajs/ui" import type { AuthTypes } from "@medusajs/types" import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { useAuthMfa, useDisableAuthMfa, useStartAuthMfa, } from "../../../../../hooks/api" import { MfaDisableModal } from "./mfa-disable-modal" import { MfaSetupModal } from "./mfa-setup-modal" const MFA_DISABLE_CODE_REQUIRED_ERROR = "MFA verification code is required to disable MFA" export const ProfileMfaSection = () => { const { t } = useTranslation() const prompt = usePrompt() const { mfa_factors: factors = [], isPending } = useAuthMfa() const [setupResponse, setSetupResponse] = useState(null) const [disableChallengeFactor, setDisableChallengeFactor] = useState(null) const { mutateAsync: startMfa, isPending: isStarting } = useStartAuthMfa() const enabledFactor = useMemo(() => { return factors.find((factor) => factor.status === "enabled") }, [factors]) const pendingFactor = useMemo(() => { return factors.find((factor) => factor.status === "pending") }, [factors]) const { mutateAsync: disableMfa, isPending: isDisabling } = useDisableAuthMfa( enabledFactor?.id ?? "" ) const { mutateAsync: cancelPendingMfa, isPending: isCancellingPending } = useDisableAuthMfa(pendingFactor?.id ?? "") const handleSetup = async () => { try { if (pendingFactor) { await cancelPendingMfa() } const response = await startMfa({ provider: "totp", label: t("profile.mfa.authenticatorApp"), }) setSetupResponse(response) } catch (e) { toast.error(e instanceof Error ? e.message : t("profile.mfa.setupError")) } } const handleDisable = async () => { if (!enabledFactor) { return } const confirmed = await prompt({ title: t("profile.mfa.disableTitle"), description: t("profile.mfa.disableDescription"), confirmText: t("actions.disable"), cancelText: t("actions.cancel"), variant: "danger", }) if (!confirmed) { return } try { await disableMfa() toast.success(t("profile.mfa.disableSuccess")) } catch (e) { if ( e instanceof Error && e.message.includes(MFA_DISABLE_CODE_REQUIRED_ERROR) ) { setDisableChallengeFactor(enabledFactor) return } toast.error( e instanceof Error ? e.message : t("profile.mfa.disableError") ) } } return ( <>
{t("profile.mfa.title")} {t("profile.mfa.description")}
{enabledFactor ? ( ) : ( )}
{t("profile.mfa.status")}
{enabledFactor ? ( {t("profile.mfa.enabled")} ) : pendingFactor ? ( {t("profile.mfa.pending")} ) : ( {t("profile.mfa.disabled")} )}
{t("profile.mfa.method")}
{enabledFactor || pendingFactor ? ( <> {t("profile.mfa.authenticatorApp")} ) : ( <> {t("profile.mfa.noMethod")} )}
{setupResponse && ( setSetupResponse(null)} /> )} {disableChallengeFactor && ( setDisableChallengeFactor(null)} onSuccess={() => { setDisableChallengeFactor(null) toast.success(t("profile.mfa.disableSuccess")) }} /> )} ) }