> ## 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](https://dynamic.xyz/docs/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](https://dynamic.xyz/docs/wallets/embedded-wallets/mpc/delegated-access/revoking-delegation) and the user next signs in.

## Using step-up authentication (recommended)

Use `requestedScopes` to receive [elevated access tokens](/overview/authentication/step-up-auth) instead of legacy MFA tokens. This is the recommended approach and will be required in the next major version.

<Tabs>
  <Tab title="TOTP">
    ```javascript theme={"system"}
    import {
      isMfaRequiredForAction,
      authenticateTotpMfaDevice,
      MFAAction,
    } from '@dynamic-labs-sdk/client';

    const onExportPrivateKeyClick = async () => {
      const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });

      if (required) {
        await authenticateTotpMfaDevice({
          code: '123456',
          requestedScopes: ['wallet:export'],
        });
      }

      // Elevated token is now stored — the SDK attaches it automatically
      await exportWaasPrivateKey(params);
    };
    ```
  </Tab>

  <Tab title="Passkey">
    ```javascript theme={"system"}
    import {
      isMfaRequiredForAction,
      authenticatePasskeyMFA,
      MFAAction,
    } from '@dynamic-labs-sdk/client';

    const onExportPrivateKeyClick = async () => {
      const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });

      if (required) {
        await authenticatePasskeyMFA({
          requestedScopes: ['wallet:export'],
        });
      }

      // Elevated token is now stored — the SDK attaches it automatically
      await exportWaasPrivateKey(params);
    };
    ```
  </Tab>
</Tabs>

For a complete guide on step-up authentication including all verification methods, see [Step-Up Authentication](/javascript/authentication-methods/step-up-auth/overview).

## React

In React, use `useAuthenticateTotpMfaDevice` for the MFA challenge and call the one-shot `isMfaRequiredForAction` check directly inside your handler before the sensitive operation:

```tsx theme={"system"}
import { isMfaRequiredForAction, MFAAction } from '@dynamic-labs-sdk/client';
import { useAuthenticateTotpMfaDevice } from '@dynamic-labs-sdk/react-hooks';
import { useState } from 'react';

function ExportKeyButton() {
  const [code, setCode] = useState('');
  const [needsMfa, setNeedsMfa] = useState(false);

  const { mutate: authenticateTotpMfaDevice } = useAuthenticateTotpMfaDevice();

  const handleExport = async () => {
    const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
    if (required) {
      setNeedsMfa(true);
      return;
    }
    await exportWaasPrivateKey(params);
  };

  const handleMfaSubmit = () => {
    authenticateTotpMfaDevice(
      { code, requestedScopes: ['wallet:export'] },
      {
        onSuccess: () => {
          setNeedsMfa(false);
          exportWaasPrivateKey(params);
        },
      }
    );
  };

  if (needsMfa) {
    return (
      <div>
        <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="Authenticator code" />
        <button onClick={handleMfaSubmit}>Confirm</button>
      </div>
    );
  }

  return <button onClick={handleExport}>Export private key</button>;
}
```

## Using MFA tokens (deprecated)

<Warning>
  `createMfaToken` and `createMfaTokenOptions` are deprecated and will be removed in the next major version. Use `requestedScopes` instead. See [migration guide](/overview/authentication/step-up-auth#migration-from-mfa-tokens).
</Warning>

<Tabs>
  <Tab title="TOTP">
    ```javascript theme={"system"}
    import { isMfaRequiredForAction, authenticateTotpMfaDevice, MFAAction } from '@dynamic-labs-sdk/client';

    const onExportPrivateKeyClick = async () => {
      const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
      if (required) {
        await authenticateTotpMfaDevice({ code: '123456', createMfaTokenOptions: { singleUse: true } });
      }
      await exportWaasPrivateKey(params);
    };
    ```
  </Tab>

  <Tab title="Passkey">
    ```javascript theme={"system"}
    import { isMfaRequiredForAction, authenticatePasskeyMFA, MFAAction } from '@dynamic-labs-sdk/client';

    const onExportPrivateKeyClick = async () => {
      const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
      if (required) {
        await authenticatePasskeyMFA({ createMfaToken: { singleUse: true } });
      }
      await exportWaasPrivateKey(params);
    };
    ```
  </Tab>
</Tabs>
