//=========================================== // THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template //=========================================== import { createTOTPKeyURI, verifyTOTP } from "@oslojs/otp"; import { useAsyncCallback } from '@hexclave/shared/dist/hooks/use-async-callback'; import { generateRandomValues } from '@hexclave/shared/dist/utils/crypto'; import { throwErr } from "@hexclave/shared/dist/utils/errors"; import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises"; import { Button, Input, Typography } from "@hexclave/ui"; import * as QRCode from 'qrcode'; import { useEffect, useState } from "react"; import { CurrentUser, Project } from '../../..'; import { useStackApp, useUser } from "../../../lib/hooks"; import { useTranslation } from "../../../lib/translations"; import { Section } from "../section"; export function MfaSection(props?: { mockMode?: boolean, }) { const { t } = useTranslation(); const project = useStackApp().useProject(); const user = useUser({ or: props?.mockMode ? 'return-null' : "throw" }); // In mock mode, show a placeholder message if (props?.mockMode && !user) { return (
{t("MFA management is not available in demo mode.")}
); } if (!user) { return null; // This shouldn't happen in non-mock mode due to throw } const [generatedSecret, setGeneratedSecret] = useState(null); const [qrCodeUrl, setQrCodeUrl] = useState(null); const [mfaCode, setMfaCode] = useState(""); const [isMaybeWrong, setIsMaybeWrong] = useState(false); const isEnabled = user.isMultiFactorRequired; const [handleSubmit, isLoading] = useAsyncCallback(async () => { await user.update({ totpMultiFactorSecret: generatedSecret, }); setGeneratedSecret(null); setQrCodeUrl(null); setMfaCode(""); }, [generatedSecret, user]); useEffect(() => { setIsMaybeWrong(false); runAsynchronouslyWithAlert(async () => { if (generatedSecret && verifyTOTP(generatedSecret, 30, 6, mfaCode)) { await handleSubmit(); } setIsMaybeWrong(true); }); }, [mfaCode, generatedSecret, handleSubmit]); return (
{!isEnabled && generatedSecret && ( <> {t("Scan this QR code with your authenticator app:")} {t("TOTP {t("Then, enter your six-digit MFA code:")} { setIsMaybeWrong(false); setMfaCode(e.target.value); }} placeholder="123456" maxLength={6} disabled={isLoading} /> {isMaybeWrong && mfaCode.length === 6 && ( {t("Incorrect code. Please try again.")} )}
)}
{isEnabled ? ( ) : !generatedSecret && ( )}
); } async function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) { const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6); return await QRCode.toDataURL(uri) as any; }