> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Action-Based MFA

Action-Based MFA requires users to verify their identity for sensitive actions like transactions.
By default, we only require action-based MFA once the user already has a MFA method registered.

## Dashboard Setup

Configure Action-Based MFA in the dashboard before implementing in your application. See [End-User MFA Configuration - Action-Based MFA](/overview/developer-dashboard/end-user-mfa#action-based-mfa) for dashboard setup instructions.

## Events that trigger Action-Based MFA

* Waas Export - When exporting a private key on an MPC wallet.
* Waas Refresh - When a wallet is [delegated](/react-native/wallets/embedded-wallets/mpc/delegated-access), or when a user claims a pregenerated MPC wallet for the first time.
* WaaS Sign - When any signature is performed i.e. a message, a transaction, typed data, authorization, etc.
* WaaS Reshare - When a wallet is approved or revoked from [delegated access](/react-native/wallets/embedded-wallets/mpc/delegated-access/revoking-delegation) and the user next signs in.

## Your UI SDK Implementation

<Tabs>
  <Tab title="TOTP">
    * **Check requirement**: Use `client.mfa.isRequiredForAction()` for the specific action.

      * **Create token**: If required, collect OTP and call `client.mfa.authenticateDevice({ code, createMfaToken })`.
      * **Perform action**: Token is applied automatically after creation.

      ```typescript theme={"system"}
      import React, { useState } from 'react';
      import { Alert, View, TextInput, Button } from 'react-native';
      import { MFAAction } from '@dynamic-labs/sdk-api-core';
      import { useDynamic } from './path-to-your-client';

      export function EnsureTotpMfaToken() {
        const client = useDynamic();
        const [code, setCode] = useState('');
        const [loading, setLoading] = useState(false);

        const promptForMfaIfRequired = async () => {
          try {
            setLoading(true);

            // Check if MFA is required for this action
            const required = await client.mfa.isRequiredForAction({
              mfaAction: MFAAction.WalletWaasSign,
            });

            if (!required) {
              // No MFA required, proceed with action
              Alert.alert('Success', 'No MFA required for this action');
              return;
            }

            if (!code.trim()) {
              Alert.alert('Error', 'Please enter your 6-digit code');
              return;
            }

            // Create MFA token with the TOTP code
            await client.mfa.authenticateDevice({
              code,
              createMfaToken: { singleUse: true },
            });

            // Now perform the action that requires Action-Based MFA
            // The token is automatically included in subsequent requests
            Alert.alert('Success', 'MFA token created, proceed with action');
          } catch (error) {
            console.error('Failed to authenticate with MFA:', error);
            Alert.alert('Error', 'Failed to authenticate with MFA');
          } finally {
            setLoading(false);
          }
        };

        return (
          <View>
            <TextInput
              placeholder="Enter 6-digit code"
              value={code}
              onChangeText={setCode}
              keyboardType="numeric"
              maxLength={6}
            />
            <Button
              title="Authenticate & Proceed"
              onPress={performSensitiveAction}
              disabled={loading}
            />
          </View>
        );
      }
      ```

      * Only one verified TOTP device per user (device management applies across modes).
      * Recovery codes are single-use; regenerate with `client.mfa.getNewRecoveryCodes()` if exhausted.

      **Troubleshooting**

      * Error: "MFA required" despite prompting — ensure you authenticate before performing the action.
      * Token unexpectedly missing — tokens expire with session; create a new token when needed.
  </Tab>

  <Tab title="Passkey">
    * **Check requirement**: Use `client.mfa.isRequiredForAction()` for the specific action.

      * **Check token**: Use `client.mfa.getMfaToken()` if MFA is required.
      * **Register if needed**: If the user has no passkeys (common when "Require at onboarding" is off), call `client.passkeys.register()` first.
      * **Create token**: Call `client.passkeys.authenticateMFA({ createMfaToken })` if missing.
      * **Perform action**: Token is applied automatically after creation.

      ```typescript theme={"system"}
      import React, { useState } from 'react';
      import { Alert, View, Button } from 'react-native';
      import { MFAAction } from '@dynamic-labs/sdk-api-core';
      import { useDynamic } from './path-to-your-client';

      export function EnsurePasskeyMfaToken() {
        const client = useDynamic();
        const [loading, setLoading] = useState(false);

        const promptForMfaIfRequired = async () => {
          try {
            setLoading(true);

            // Check if MFA is required for this action
            const requires = await client.mfa.isRequiredForAction({
              mfaAction: MFAAction.WalletWaasSign,
            });

            if (!requires) {
              // No MFA required for this action
              return;
            }

            // Important: if you don't have MFA required at onboarding and you want to force the user
            // to setup MFA prior to performing the action, you should skip any checks for MFA requirement above (isMfaRequiredForAction)
            // and force a registration or authentication of the MFA method.

            // If the user doesn't have a passkey yet, register one first
            const passkeys = await client.passkeys.get();
            if (passkeys.length === 0) {
              await client.passkeys.register();
            }

            // Create MFA token using passkey
            await client.passkeys.authenticateMFA({
              createMfaToken: { singleUse: false }, // or true for one-time
            });

            // Now perform the action that requires Action-Based MFA
          } catch (error) {
            console.error('Failed to create MFA token:', error);
            Alert.alert('Error', 'Failed to authenticate with passkey');
            throw error;
          } finally {
            setLoading(false);
          }
        };

        return (
          <View>
            <Button
              title="Authenticate & Proceed"
              onPress={ensureToken}
              disabled={loading}
            />
          </View>
        );
      }
      ```

      * Passkeys require platform setup. See the [React Native Passkey Setup guide](/react-native/reference/setup-passkey).
      * Consider persistent tokens for multiple sensitive actions in-session.

      **Troubleshooting**

      * Error: "MFA required" when performing actions — ensure you check `getMfaToken()` before creating a new token and include it in the request.
      * Error: "Passkey not supported" — verify platform setup and passkey support.
  </Tab>
</Tabs>

## Dynamic UI Implementation

The React Native SDK provides a UI method to prompt MFA authentication. Use `client.mfa.isRequiredForAction()` to check if MFA is required, then use `client.ui.mfa.show()` to prompt the user.

Note: The Dynamic UI is method-agnostic. It automatically prompts with whichever MFA method(s) you have enabled (TOTP only for React Native).

```typescript theme={"system"}
import React, { useState } from 'react';
import { Alert, View, Button } from 'react-native';
import { MFAAction } from '@dynamic-labs/sdk-api-core';
import { useDynamic } from './path-to-your-client';

export function PerformSensitiveAction() {
  const client = useDynamic();
  const [loading, setLoading] = useState(false);

  const performAction = async () => {
    try {
      setLoading(true);

      // Check if MFA is required for the action
      const isMfaRequired = await client.mfa.isRequiredForAction({
        mfaAction: MFAAction.WalletWaasSign, // Specify action configured for Action-Based MFA
      });

      if (isMfaRequired) {
        // Opens the Dynamic UI to prompt the user to authenticate with MFA
        await client.ui.mfa.show({ createMfaToken: true }); // Create a single-use token for the action
      }

      // Perform the action
      // await primaryWallet.signMessage('Hello, world');
      Alert.alert('Success', 'Action completed successfully');
    } catch (error) {
      console.error('Failed to perform action:', error);
      Alert.alert('Error', 'Failed to perform action');
    } finally {
      setLoading(false);
    }
  };

  return (
    <View>
      <Button
        title="Perform Sensitive Action"
        onPress={performAction}
        disabled={loading}
      />
    </View>
  );
}
```

**Note**: For Action-Based MFA in React Native, only TOTP authentication is currently supported. Passkey MFA is not available in React Native.
