Skip to main content

Dashboard Setup

MFA enrollment requires users to register a TOTP device or Passkey. Configure it in the dashboard before implementing in your application. See End-User MFA - Enrollment for dashboard setup instructions.

React

In React, drive the multi-step MFA flow (register → scan QR → verify) with the mutation hooks. useRegisterTotpMfaDevice exposes the QR URI as its data:
import {
  useAuthenticateTotpMfaDevice,
  useRegisterTotpMfaDevice,
} from '@dynamic-labs-sdk/react-hooks';
import { useState } from 'react';

function TotpEnrollment() {
  const [code, setCode] = useState('');

  const { mutate: registerDevice, data: registration, reset } = useRegisterTotpMfaDevice();
  const { mutate: authenticateDevice } = useAuthenticateTotpMfaDevice();

  const handleVerify = () => {
    authenticateDevice({ code }, { onSuccess: () => reset() });
  };

  if (registration) {
    return (
      <div>
        <p>Scan this QR code with your authenticator app:</p>
        {/* Render QR from registration.uri using a library like qrcode.react */}
        <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="Enter code" />
        <button onClick={handleVerify}>Verify</button>
      </div>
    );
  }

  return <button onClick={() => registerDevice()}>Set up authenticator app</button>;
}

Your UI SDK Implementation

  • Register device: registerTotpMfaDevice() returns a QR uri and secret.
  • Authenticate: authenticateTotpMfaDevice({ code }) completes the challenge.
  • Manage devices: getMfaDevices() lists devices; deleteMfaDevice() deletes.
  • Recovery codes: getMfaRecoveryCodes() to display; createNewMfaRecoveryCodes() to rotate; authenticateMfaRecoveryCode({ code }) to unblock login.
import { registerTotpMfaDevice, authenticateTotpMfaDevice, getMfaDevices } from '@dynamic-labs-sdk/client';

const register = async () => {
  const { uri } = await registerTotpMfaDevice();
  // Render QR code from `uri`
};

const verify = async (code) => {
  await authenticateTotpMfaDevice({ code });
};

const listDevices = async () => {
  const devices = await getMfaDevices();
  console.log(devices);
};
import {
  getMfaRecoveryCodes,
  createNewMfaRecoveryCodes,
  authenticateMfaRecoveryCode,
} from '@dynamic-labs-sdk/client';

const showCodes = async () => {
  const { recoveryCodes } = await getMfaRecoveryCodes();
  console.log(recoveryCodes);
};

const rotateCodes = async () => {
  const { recoveryCodes } = await createNewMfaRecoveryCodes();
  console.log(recoveryCodes);
};

const authWithRecovery = async (code) => {
  await authenticateMfaRecoveryCode({ code });
};
import { authenticateTotpMfaDevice, deleteMfaDevice } from '@dynamic-labs-sdk/client';

const deleteTotpDevice = async (deviceId, code) => {
  // Create a single-use MFA token using the device to be deleted
  await authenticateTotpMfaDevice({
    code,
    createMfaTokenOptions: { singleUse: true },
  });

  // Use the MFA token from the client to authorize deletion
  const mfaToken = dynamicClient.mfaToken;
  await deleteMfaDevice({ deviceId, mfaAuthToken: mfaToken });
};
Last modified on June 24, 2026