From 5d721d7a3ed092f3bd5a8ec686a44f3f7199e22a Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Fri, 8 Dec 2023 15:55:25 +0100 Subject: [PATCH] Mfa --- .../src/actions/mfa-verify-action.ts | 23 ++++++++++ apps/dashboard/src/actions/schema.ts | 8 +++- .../src/actions/unenroll-mfa-action.ts | 4 +- apps/dashboard/src/components/enroll-mfa.tsx | 18 ++++++-- apps/dashboard/src/components/mfa-list.tsx | 2 +- .../src/components/modals/add-new-device.tsx | 43 ++++++++++++------- .../src/components/remove-mfa-button.tsx | 6 +-- 7 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 apps/dashboard/src/actions/mfa-verify-action.ts diff --git a/apps/dashboard/src/actions/mfa-verify-action.ts b/apps/dashboard/src/actions/mfa-verify-action.ts new file mode 100644 index 0000000000..dcb2cc2407 --- /dev/null +++ b/apps/dashboard/src/actions/mfa-verify-action.ts @@ -0,0 +1,23 @@ +"use server"; + +import { createClient } from "@midday/supabase/server"; +import { revalidatePath } from "next/cache"; +import { action } from "./safe-action"; +import { mfaVerifySchema } from "./schema"; + +export const mfaVerifyAction = action( + mfaVerifySchema, + async ({ factorId, challengeId, code }) => { + const supabase = createClient(); + + const { data } = await supabase.auth.mfa.verify({ + factorId, + challengeId, + code, + }); + + revalidatePath("/settings/security"); + + return data; + } +); diff --git a/apps/dashboard/src/actions/schema.ts b/apps/dashboard/src/actions/schema.ts index e7eb5a08d3..3ade9b3eae 100644 --- a/apps/dashboard/src/actions/schema.ts +++ b/apps/dashboard/src/actions/schema.ts @@ -85,7 +85,13 @@ export const createFolderSchema = z.object({ }); export const unenrollMfaSchema = z.object({ - factoryId: z.string(), + factorId: z.string(), +}); + +export const mfaVerifySchema = z.object({ + factorId: z.string(), + challengeId: z.string(), + code: z.string(), }); export const shareFileSchema = z.object({ diff --git a/apps/dashboard/src/actions/unenroll-mfa-action.ts b/apps/dashboard/src/actions/unenroll-mfa-action.ts index ead09e3666..511f4d315f 100644 --- a/apps/dashboard/src/actions/unenroll-mfa-action.ts +++ b/apps/dashboard/src/actions/unenroll-mfa-action.ts @@ -7,11 +7,11 @@ import { unenrollMfaSchema } from "./schema"; export const unenrollMfaAction = action( unenrollMfaSchema, - async ({ factoryId }) => { + async ({ factorId }) => { const supabase = createClient(); const { data, error } = await supabase.auth.mfa.unenroll({ - factoryId, + factorId, }); if (error) { diff --git a/apps/dashboard/src/components/enroll-mfa.tsx b/apps/dashboard/src/components/enroll-mfa.tsx index 56a53264a4..3c17cfd25f 100644 --- a/apps/dashboard/src/components/enroll-mfa.tsx +++ b/apps/dashboard/src/components/enroll-mfa.tsx @@ -1,6 +1,6 @@ import { createClient } from "@midday/supabase/client"; +import { Button } from "@midday/ui/button"; import Image from "next/image"; -import Link from "next/link"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import PinField from "react-pin-field"; @@ -48,6 +48,14 @@ export function EnrollMFA() { enroll(); }, []); + const handleOnCancel = () => { + supabase.auth.mfa.unenroll({ + factorId, + }); + + router.push("/onboarding"); + }; + return ( <>
@@ -75,9 +83,13 @@ export function EnrollMFA() {
- +
); diff --git a/apps/dashboard/src/components/mfa-list.tsx b/apps/dashboard/src/components/mfa-list.tsx index e42e74392c..855c6c9eb4 100644 --- a/apps/dashboard/src/components/mfa-list.tsx +++ b/apps/dashboard/src/components/mfa-list.tsx @@ -36,7 +36,7 @@ export async function MFAList() {

- + ); }); diff --git a/apps/dashboard/src/components/modals/add-new-device.tsx b/apps/dashboard/src/components/modals/add-new-device.tsx index 48501fead2..9dcb693c38 100644 --- a/apps/dashboard/src/components/modals/add-new-device.tsx +++ b/apps/dashboard/src/components/modals/add-new-device.tsx @@ -1,9 +1,11 @@ "use client"; +import { mfaVerifyAction } from "@/actions/mfa-verify-action"; import { createClient } from "@midday/supabase/client"; import { Button } from "@midday/ui/button"; import { Dialog, DialogContent } from "@midday/ui/dialog"; import { cn } from "@midday/ui/utils"; +import { useAction } from "next-safe-action/hook"; import Image from "next/image"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; @@ -20,25 +22,21 @@ export function AddNewDeviceModal() { const [qr, setQR] = useState(""); const isOpen = searchParams.get("add") === "device"; + const verify = useAction(mfaVerifyAction, { + onSuccess: () => router.push(pathname), + }); + const onComplete = async (code: string) => { if (!isValidating) { setValidating(true); const challenge = await supabase.auth.mfa.challenge({ factorId }); - try { - const verify = await supabase.auth.mfa.verify({ - factorId, - challengeId: challenge.data.id, - code, - }); - - if (verify.data) { - router.push(pathname); - } - } catch { - setError(true); - } + verify.execute({ + factorId, + challengeId: challenge.data.id, + code, + }); } }; @@ -65,9 +63,22 @@ export function AddNewDeviceModal() { } }, [isOpen]); + const handleOnClose = () => { + router.push(pathname); + + supabase.auth.mfa.unenroll({ + factorId, + }); + }; + return ( - router.push(pathname)}> - + + { + evt.preventDefault(); + }} + >
@@ -96,7 +107,7 @@ export function AddNewDeviceModal() {
);