Skip to main content

Dashboard Setup

  1. Go to the Security page.
  2. In the Account MFA section, enable your desired methods (TOTP and/or Passkeys).
  3. (Optional) Toggle “Require at onboarding” to force MFA setup during signup.
  4. (Optional) Toggle “Session-based MFA” to require MFA for every new session.

Your UI SDK Implementation

  • TOTP
  • Passkey
  • React
  • React Native
  • JavaScript SDK
  • React Native
  • Trigger gating: Check userWithMissingInfo?.scope for requiresAdditionalAuth.
  • Detect MFA: Use useSyncMfaFlow to know when to prompt.
  • Add device: Call addDevice() → show QR from uri (install qrcode).
  • Verify: authenticateDevice({ code }) → fetch and show recovery codes → completeAcknowledgement().
  • Refresh: Call getUserDevices() after verification.
import { useMfa, useSyncMfaFlow, useDynamicContext } from "@dynamic-labs/sdk-react-core";

export function AccountTotpMfa() {
  const {
    addDevice,
    authenticateDevice,
    getUserDevices,
    getRecoveryCodes,
    completeAcknowledgement,
  } = useMfa();
  const { userWithMissingInfo } = useDynamicContext();

  useSyncMfaFlow({
    handler: async () => {
      if (userWithMissingInfo?.scope?.includes("requiresAdditionalAuth")) {
        const devices = await getUserDevices();
        if (devices.length === 0) {
          const { uri } = await addDevice();
          // Render QR from `uri`, then prompt for OTP
          return;
        }
        // Prompt for OTP
      } else {
        // MFA already satisfied; show recovery codes if needed
        const codes = await getRecoveryCodes();
        // Show `codes` to the user, then:
        await completeAcknowledgement();
      }
    },
  });

  const verifyOtp = async (code: string) => {
    await authenticateDevice({ code });
    const codes = await getRecoveryCodes();
    // Show `codes` to the user, then:
    await completeAcknowledgement();
    // Refresh device list
    await getUserDevices();
  };

  return null;
}
Only one verified TOTP device per user
  • Recovery codes are single-use. Regenerate with getRecoveryCodes(true) if needed.
Troubleshooting
  • Error: “401 Unauthorized” when adding a second TOTP device — only one device is supported; delete the existing device first.
  • QR code not displaying — ensure qrcode is installed: npm install qrcode @types/qrcode.
  • Recovery codes not working — each code is single-use; generate new codes if exhausted.

Dynamic UI Implementation

  • React
  • React Native
  • JavaScript SDK
With the Dynamic UI, account-based MFA prompts appear automatically during onboarding/login when additional authentication is required. To explicitly trigger in your app, use useSyncMfaFlow to detect the requirement and usePromptMfaAuth to open the UI.Note: The Dynamic UI is method-agnostic. It automatically prompts with whichever MFA method(s) you have enabled (TOTP and/or Passkeys), so separate TOTP/Passkey tabs are not required here.
import { useSyncMfaFlow, useDynamicContext, usePromptMfaAuth } from "@dynamic-labs/sdk-react-core";

const { userWithMissingInfo } = useDynamicContext();
const promptMfaAuth = usePromptMfaAuth();

useSyncMfaFlow({
  handler: async () => {
    if (userWithMissingInfo?.scope?.includes("requiresAdditionalAuth")) {
      // Opens the Dynamic UI to complete account-based MFA
      await promptMfaAuth(); // No MFA token needed for account-based MFA
    }
  },
});

Programmatic MFA Modal Controls

Dynamic SDKs provide granular control over individual MFA modal screens. These methods allow you to programmatically show and hide specific MFA screens in your application.

Available Modal Controls

  • React Native
  • React
Show and hide specific MFA screens:
import React from 'react';
import { View, Button } from 'react-native';
import { dynamicClient } from './path-to-your-client';

export function MfaModalControls() {
  return (
    <View>
      {/* MFA Management Screen */}
      <Button
        title="Show MFA Management"
        onPress={() => dynamicClient.ui.mfa.manage.show()}
      />
      <Button
        title="Hide MFA Management"
        onPress={() => dynamicClient.ui.mfa.manage.hide()}
      />

      {/* MFA Type Selection Screen */}
      <Button
        title="Show MFA Type Selection"
        onPress={() => dynamicClient.ui.mfa.chooseType.show()}
      />
      <Button
        title="Hide MFA Type Selection"
        onPress={() => dynamicClient.ui.mfa.chooseType.hide()}
      />

      {/* QR Code Screen (for TOTP setup) */}
      <Button
        title="Show QR Code"
        onPress={() => dynamicClient.ui.mfa.qrCode.show()}
      />
      <Button
        title="Hide QR Code"
        onPress={() => dynamicClient.ui.mfa.qrCode.hide()}
      />

      {/* OTP Verification Screen */}
      <Button
        title="Show OTP Verification"
        onPress={() => dynamicClient.ui.mfa.otpVerification.show()}
      />
      <Button
        title="Hide OTP Verification"
        onPress={() => dynamicClient.ui.mfa.otpVerification.hide()}
      />

      {/* View Backup Codes Screen */}
      <Button
        title="Show Backup Codes"
        onPress={() => dynamicClient.ui.mfa.viewBackupCodes.show()}
      />
      <Button
        title="Hide Backup Codes"
        onPress={() => dynamicClient.ui.mfa.viewBackupCodes.hide()}
      />

      {/* Enter Backup Codes Screen */}
      <Button
        title="Show Enter Backup Codes"
        onPress={() => dynamicClient.ui.mfa.enterBackupCodes.show()}
      />
      <Button
        title="Hide Enter Backup Codes"
        onPress={() => dynamicClient.ui.mfa.enterBackupCodes.hide()}
      />
    </View>
  );
}