RegisterLogin
DocsPricing
RegisterLogin
  • Getting Started
  • Introduction
  • Quick Start
  • SDKs
  • React
  • TypeScript
  • Next.js
  • Express
  • NestJS
  • Python
  • API Reference
  • Support and Resources
  • FAQ
  • Contact Support

AuthSafe

Product

HighlightFeatureIntegrationPricingFAQ

Company

AboutBlogContactSitemap

Developer

DashboardDocumentation

Legal

Terms & ConditionsPrivacyComplianceShippingCancellation

© 2026 AuthSafe. All rights reserved.

We value your privacy

This website uses cookies for anonymous analytics to help us improve your experience. No personal information is stored or shared. You can allow or reject analytics tracking at any time. See our Privacy Policy.

We use cookies for anonymous analytics. No personal info is stored. See our Privacy Policy.

useMfa()

Hook for managing multi-factor authentication (MFA) methods. Supports TOTP, Email OTP, and WebAuthn.

Import

'use client';

import { useMfa } from 'authsafe-nextjs/client';

Return Value

interface UseMfaReturn {
  methods: MfaMethod[];
  isLoading: boolean;
  error: Error | null;
  fetchMethods: () => Promise<void>;
  registerMethod: (type: MfaMethodType) => Promise<any>;
  confirmMethod: (data: any) => Promise<any>;
  removeMethod: (methodId: string) => Promise<void>;
  makePrimary: (methodId: string) => Promise<void>;
}

Properties

methods - List of configured MFA methods
interface MfaMethod {
  id: string;
  type: MfaMethodType;
  isEnabled: boolean;
  isPrimary: boolean;
}

type MfaMethodType = 'TOTP' | 'WEBAUTHN' | 'EMAIL_OTP' | 'BACKUP_CODE';
isLoading - Whether MFA operation is in progress error - Error from last MFA operation

Methods

fetchMethods() - Fetch all MFA methods for current user registerMethod(type) - Start registration for new MFA method
  • Returns QR code for TOTP
  • Returns challenge for WebAuthn
  • Sends email for EMAIL_OTP
confirmMethod(data) - Confirm MFA method registration
  • For TOTP: pass verification code
  • For WebAuthn: pass credential response
  • For EMAIL_OTP: pass verification code
removeMethod(methodId) - Remove an MFA method makePrimary(methodId) - Set method as primary

Usage Examples

List MFA Methods

'use client';

import { useMfa } from 'authsafe-nextjs/client';
import { useEffect } from 'react';

export function MfaList() {
  const { methods, isLoading, fetchMethods } = useMfa();

  useEffect(() => {
    fetchMethods();
  }, []);

  if (isLoading) {
    return <div>Loading MFA methods...</div>;
  }

  return (
    <div>
      <h2>Your MFA Methods</h2>
      {methods.length === 0 ? (
        <p>No MFA methods configured</p>
      ) : (
        <ul>
          {methods.map((method) => (
            <li key={method.id}>
              {method.type} {method.isPrimary && '(Primary)'}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Enable TOTP

'use client';

import { useMfa } from 'authsafe-nextjs/client';
import { useState } from 'react';

export function EnableTOTP() {
  const { registerMethod, confirmMethod } = useMfa();
  const [qrCode, setQrCode] = useState<string | null>(null);
  const [code, setCode] = useState('');

  const handleRegister = async () => {
    const result = await registerMethod('TOTP');
    setQrCode(result.qrCode);
  };

  const handleConfirm = async () => {
    await confirmMethod({ code });
    alert('TOTP enabled!');
    setQrCode(null);
  };

  if (!qrCode) {
    return <button onClick={handleRegister}>Enable TOTP</button>;
  }

  return (
    <div>
      <h3>Scan this QR code with your authenticator app</h3>
      <img src={qrCode} alt="TOTP QR Code" />
      <input
        type="text"
        value={code}
        onChange={(e) => setCode(e.target.value)}
        placeholder="Enter 6-digit code"
      />
      <button onClick={handleConfirm}>Verify</button>
    </div>
  );
}

Enable Email OTP

'use client';

import { useMfa } from 'authsafe-nextjs/client';
import { useState } from 'react';

export function EnableEmailOTP() {
  const { registerMethod, confirmMethod } = useMfa();
  const [sent, setSent] = useState(false);
  const [code, setCode] = useState('');

  const handleSend = async () => {
    await registerMethod('EMAIL_OTP');
    setSent(true);
  };

  const handleVerify = async () => {
    await confirmMethod({ code });
    alert('Email OTP enabled!');
    setSent(false);
  };

  if (!sent) {
    return <button onClick={handleSend}>Enable Email OTP</button>;
  }

  return (
    <div>
      <p>Check your email for verification code</p>
      <input
        type="text"
        value={code}
        onChange={(e) => setCode(e.target.value)}
        placeholder="Enter code from email"
      />
      <button onClick={handleVerify}>Verify</button>
    </div>
  );
}

Remove MFA Method

'use client';

import { useMfa } from 'authsafe-nextjs/client';

export function RemoveMfaButton({ methodId }: { methodId: string }) {
  const { removeMethod } = useMfa();

  const handleRemove = async () => {
    if (confirm('Are you sure you want to remove this MFA method?')) {
      await removeMethod(methodId);
      alert('MFA method removed');
    }
  };

  return <button onClick={handleRemove}>Remove</button>;
}

Set Primary Method

'use client';

import { useMfa } from 'authsafe-nextjs/client';

export function MakePrimaryButton({ methodId }: { methodId: string }) {
  const { makePrimary } = useMfa();

  const handleMakePrimary = async () => {
    await makePrimary(methodId);
    alert('Primary method updated');
  };

  return <button onClick={handleMakePrimary}>Make Primary</button>;
}

Complete Example

'use client';

import { useMfa } from 'authsafe-nextjs/client';
import { useEffect, useState } from 'react';

export function MfaSettings() {
  const {
    methods,
    isLoading,
    error,
    fetchMethods,
    registerMethod,
    confirmMethod,
    removeMethod,
    makePrimary,
  } = useMfa();
  const [registering, setRegistering] = useState<string | null>(null);
  const [qrCode, setQrCode] = useState<string | null>(null);
  const [verificationCode, setVerificationCode] = useState('');

  useEffect(() => {
    fetchMethods();
  }, []);

  const handleRegister = async (type: 'TOTP' | 'EMAIL_OTP') => {
    setRegistering(type);
    const result = await registerMethod(type);

    if (type === 'TOTP') {
      setQrCode(result.qrCode);
    }
  };

  const handleConfirm = async () => {
    await confirmMethod({ code: verificationCode });
    setRegistering(null);
    setQrCode(null);
    setVerificationCode('');
    await fetchMethods();
  };

  if (isLoading && methods.length === 0) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div className="text-red-600">Error: {error.message}</div>;
  }

  return (
    <div className="max-w-2xl mx-auto p-6">
      <h1 className="text-2xl font-bold mb-6">Multi-Factor Authentication</h1>

      {/* Existing Methods */}
      {methods.length > 0 && (
        <div className="mb-8">
          <h2 className="text-lg font-semibold mb-4">Active Methods</h2>
          <div className="space-y-3">
            {methods.map((method) => (
              <div
                key={method.id}
                className="flex items-center justify-between p-4 border rounded-lg"
              >
                <div>
                  <span className="font-medium">{method.type}</span>
                  {method.isPrimary && (
                    <span className="ml-2 px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded">
                      Primary
                    </span>
                  )}
                </div>
                <div className="space-x-2">
                  {!method.isPrimary && (
                    <button
                      onClick={() => makePrimary(method.id)}
                      className="px-3 py-1 text-sm text-blue-600 hover:bg-blue-50 rounded"
                    >
                      Make Primary
                    </button>
                  )}
                  <button
                    onClick={() => removeMethod(method.id)}
                    className="px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded"
                  >
                    Remove
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Registration UI */}
      {registering ? (
        <div className="border rounded-lg p-6">
          <h3 className="text-lg font-semibold mb-4">Set up {registering}</h3>

          {qrCode && (
            <div className="mb-4">
              <p className="mb-2">
                Scan this QR code with your authenticator app:
              </p>
              <img src={qrCode} alt="QR Code" className="max-w-xs" />
            </div>
          )}

          {registering === 'EMAIL_OTP' && (
            <p className="mb-4">Check your email for the verification code.</p>
          )}

          <input
            type="text"
            value={verificationCode}
            onChange={(e) => setVerificationCode(e.target.value)}
            placeholder="Enter verification code"
            className="w-full px-3 py-2 border rounded mb-4"
          />

          <div className="flex gap-2">
            <button
              onClick={handleConfirm}
              className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
            >
              Verify
            </button>
            <button
              onClick={() => {
                setRegistering(null);
                setQrCode(null);
                setVerificationCode('');
              }}
              className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300"
            >
              Cancel
            </button>
          </div>
        </div>
      ) : (
        <div>
          <h3 className="text-lg font-semibold mb-4">Add New Method</h3>
          <div className="flex gap-4">
            <button
              onClick={() => handleRegister('TOTP')}
              className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
            >
              Add Authenticator App
            </button>
            <button
              onClick={() => handleRegister('EMAIL_OTP')}
              className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"
            >
              Add Email OTP
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

Authentication Required

The useMfa() hook requires the user to be authenticated. Always check isAuthenticated before showing MFA settings:
'use client';

import { useAuth, useMfa } from 'authsafe-nextjs/client';

export function MfaPage() {
  const { isAuthenticated } = useAuth();

  if (!isAuthenticated) {
    return <div>Please sign in to manage MFA settings</div>;
  }

  return <MfaSettings />;
}

Related

  • useAuth() - Main authentication hook
  • AuthProvider - Required provider
  • Profile Component - Pre-built profile with MFA