diff --git a/apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx b/apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
new file mode 100644
index 000000000..f95657d9f
--- /dev/null
+++ b/apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
@@ -0,0 +1,159 @@
+import { useState } from 'react';
+
+import { msg } from '@lingui/core/macro';
+import { useLingui } from '@lingui/react';
+import { Trans } from '@lingui/react/macro';
+import type { User } from '@prisma/client';
+import { useRevalidator } from 'react-router';
+import { match } from 'ts-pattern';
+
+import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
+import { trpc } from '@documenso/trpc/react';
+import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
+import { Button } from '@documenso/ui/primitives/button';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '@documenso/ui/primitives/dialog';
+import { Input } from '@documenso/ui/primitives/input';
+import { useToast } from '@documenso/ui/primitives/use-toast';
+
+export type AdminUserResetTwoFactorDialogProps = {
+ className?: string;
+ user: User;
+};
+
+export const AdminUserResetTwoFactorDialog = ({
+ className,
+ user,
+}: AdminUserResetTwoFactorDialogProps) => {
+ const { _ } = useLingui();
+ const { toast } = useToast();
+ const { revalidate } = useRevalidator();
+ const [email, setEmail] = useState('');
+ const [open, setOpen] = useState(false);
+
+ const { mutateAsync: resetTwoFactor, isPending: isResettingTwoFactor } =
+ trpc.admin.user.resetTwoFactor.useMutation();
+
+ const onResetTwoFactor = async () => {
+ try {
+ await resetTwoFactor({
+ userId: user.id,
+ });
+
+ toast({
+ title: _(msg`2FA Reset`),
+ description: _(msg`The user's two factor authentication has been reset successfully.`),
+ duration: 5000,
+ });
+
+ await revalidate();
+ setOpen(false);
+ } catch (err) {
+ const error = AppError.parseError(err);
+
+ const errorMessage = match(error.code)
+ .with(AppErrorCode.NOT_FOUND, () => msg`User not found.`)
+ .with(
+ AppErrorCode.UNAUTHORIZED,
+ () => msg`You are not authorized to reset two factor authentcation for this user.`,
+ )
+ .otherwise(
+ () => msg`An error occurred while resetting two factor authentication for the user.`,
+ );
+
+ toast({
+ title: _(msg`Error`),
+ description: _(errorMessage),
+ variant: 'destructive',
+ duration: 7500,
+ });
+ }
+ };
+
+ const handleOpenChange = (newOpen: boolean) => {
+ setOpen(newOpen);
+
+ if (!newOpen) {
+ setEmail('');
+ }
+ };
+
+ return (
+
+
+
+ Reset Two Factor Authentication
+
+
+ Reset the users two factor authentication. This action is irreversible and will
+ disable two factor authentication for the user.
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx b/apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
index fb05128a6..458553dae 100644
--- a/apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
+++ b/apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
@@ -27,6 +27,7 @@ import { AdminOrganisationCreateDialog } from '~/components/dialogs/admin-organi
import { AdminUserDeleteDialog } from '~/components/dialogs/admin-user-delete-dialog';
import { AdminUserDisableDialog } from '~/components/dialogs/admin-user-disable-dialog';
import { AdminUserEnableDialog } from '~/components/dialogs/admin-user-enable-dialog';
+import { AdminUserResetTwoFactorDialog } from '~/components/dialogs/admin-user-reset-two-factor-dialog';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { AdminOrganisationsTable } from '~/components/tables/admin-organisations-table';
@@ -219,10 +220,11 @@ const AdminUserPage = ({ user }: { user: User }) => {
/>
-