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 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 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, 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 and the user next signs in.
Your UI SDK Implementation
-
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.
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.
-
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.
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>
);
}
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.
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).
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.