> ## 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

<Card title="Recommended: JavaScript SDK with React Hooks" icon="react" color="#4779FE">
  For new React apps, we recommend the JavaScript SDK with React Hooks (`@dynamic-labs-sdk/react-hooks`) instead of the legacy React SDK documented here. The JS SDK comes with many benefits such as a much smaller bundle size and other optimizations. Use the [React quickstart (JavaScript SDK)](/javascript/reference/react-quickstart) to get started.
</Card>

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/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/wallets/embedded-wallets/mpc/delegated-access/revoking-delegation) and the user next signs in.

## Using step-up authentication (recommended)

Use the `useStepUpAuthentication` hook with `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">
    ```tsx theme={"system"}
    import { useStepUpAuthentication } from "@dynamic-labs/sdk-react-core";
    import { useIsMfaRequiredForAction } from "@dynamic-labs/sdk-react-core";
    import { MFAAction, TokenScope } from '@dynamic-labs/sdk-api-core';
    import { getElevatedAccessToken } from '@dynamic-labs-sdk/client';

    export function EnsureTotpStepUp() {
      const { verifyTotpMfa, state } = useStepUpAuthentication();
      const isMfaRequiredForAction = useIsMfaRequiredForAction();

      const ensureElevatedAccess = async (code: string) => {
        const requires = await isMfaRequiredForAction({
          mfaAction: MFAAction.WalletWaasExport,
        });

        if (!requires) return;

        const existing = getElevatedAccessToken({ scope: 'wallet:export' });
        if (existing) return;

        await verifyTotpMfa({
          code,
          requestedScopes: [TokenScope.Walletexport],
        });

        // Elevated token is now stored in SDK state
        // Perform the action that requires elevated access
      };

      return null;
    }
    ```
  </Tab>

  <Tab title="Passkey">
    ```tsx theme={"system"}
    import { useStepUpAuthentication } from "@dynamic-labs/sdk-react-core";
    import {
      useGetPasskeys,
      useIsMfaRequiredForAction,
      useRegisterPasskey,
    } from "@dynamic-labs/sdk-react-core";
    import { MFAAction, TokenScope } from '@dynamic-labs/sdk-api-core';
    import { getElevatedAccessToken } from '@dynamic-labs-sdk/client';

    export function EnsurePasskeyStepUp() {
      const { verifyPasskeyMfa, state } = useStepUpAuthentication();
      const getPasskeys = useGetPasskeys();
      const registerPasskey = useRegisterPasskey();
      const isMfaRequiredForAction = useIsMfaRequiredForAction();

      const ensureElevatedAccess = async () => {
        const requires = await isMfaRequiredForAction({
          mfaAction: MFAAction.WalletWaasExport,
        });

        if (!requires) return;

        const existing = getElevatedAccessToken({ scope: 'wallet:export' });
        if (existing) return;

        const passkeys = await getPasskeys();
        if (passkeys.length === 0) {
          await registerPasskey();
        }

        await verifyPasskeyMfa({
          requestedScopes: [TokenScope.Walletexport],
        });

        // Elevated token is now stored in SDK state
        // Perform the action that requires elevated access
      };

      return null;
    }
    ```

    * Passkeys require WebAuthn support; production must be over HTTPS.
  </Tab>
</Tabs>

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

## Using MFA tokens (deprecated)

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

<Tabs>
  <Tab title="TOTP">
    ```tsx theme={"system"}
    import { useMfa, useIsMfaRequiredForAction } from "@dynamic-labs/sdk-react-core";
    import { useGetMfaToken } from "@dynamic-labs/sdk-react-core";
    import { MFAAction } from '@dynamic-labs/sdk-api-core';

    export function EnsureTotpMfaToken() {
      const { authenticateDevice } = useMfa();
      const { getMfaToken } = useGetMfaToken();
      const isMfaRequiredForAction = useIsMfaRequiredForAction();

      const ensureToken = async (code?: string) => {
        const requires = await isMfaRequiredForAction({
          mfaAction: MFAAction.WalletWaasExport,
        });
        if (!requires) return;

        const existing = await getMfaToken();
        if (existing) return existing;

        if (!code) throw new Error("OTP code required");
        const token = await authenticateDevice({
          code,
          createMfaToken: { singleUse: true },
        });
        return token;
      };

      return null;
    }
    ```
  </Tab>

  <Tab title="Passkey">
    ```tsx theme={"system"}
    import {
      useAuthenticatePasskeyMFA,
      useGetMfaToken,
      useGetPasskeys,
      useIsMfaRequiredForAction,
      useRegisterPasskey,
    } from "@dynamic-labs/sdk-react-core";
    import { MFAAction } from '@dynamic-labs/sdk-api-core';

    export function EnsurePasskeyMfaToken() {
      const authenticatePasskeyMFA = useAuthenticatePasskeyMFA();
      const { getMfaToken } = useGetMfaToken();
      const getPasskeys = useGetPasskeys();
      const registerPasskey = useRegisterPasskey();
      const isMfaRequiredForAction = useIsMfaRequiredForAction();

      const promptForMfaIfRequired = async () => {
        const requires = await isMfaRequiredForAction({
          mfaAction: MFAAction.WalletWaasExport,
        });
        if (!requires) return;

        const passkeys = await getPasskeys();
        if (passkeys.length === 0) {
          await registerPasskey();
        }

        await authenticatePasskeyMFA({
          createMfaToken: { singleUse: false },
        });
      };

      return null;
    }
    ```
  </Tab>
</Tabs>

## Dynamic UI Implementation

The Dynamic UI is method-agnostic. It automatically prompts with whichever MFA method(s) you have enabled (TOTP and/or Passkeys).

### With step-up authentication (recommended)

Pass `requestedScopes` to `usePromptMfaAuth` to receive an elevated access token through the Dynamic UI:

```tsx theme={"system"}
import { usePromptMfaAuth, useIsMfaRequiredForAction } from "@dynamic-labs/sdk-react-core";
import { MFAAction, TokenScope } from '@dynamic-labs/sdk-api-core';

const isMfaRequiredForAction = useIsMfaRequiredForAction();
const promptMfaAuth = usePromptMfaAuth();

const isMfaRequired = await isMfaRequiredForAction({
  mfaAction: MFAAction.WalletWaasExport,
});

if (isMfaRequired) {
  await promptMfaAuth({
    requestedScopes: [TokenScope.Walletexport],
  });
}

// Elevated token is now stored — the SDK attaches it automatically
await primaryWallet.exportWaasPrivateKey();
```

### With MFA token (deprecated)

```tsx theme={"system"}
import { usePromptMfaAuth, useIsMfaRequiredForAction } from "@dynamic-labs/sdk-react-core";
import { MFAAction } from '@dynamic-labs/sdk-api-core';

const isMfaRequiredForAction = useIsMfaRequiredForAction();
const promptMfaAuth = usePromptMfaAuth();

const isMfaRequired = await isMfaRequiredForAction({
  mfaAction: MFAAction.WalletWaasExport,
});

if (isMfaRequired) {
  await promptMfaAuth({ createMfaToken: true });
}

await primaryWallet.exportWaasPrivateKey();
```

* [usePromptMfaAuth](/react/reference/hooks/login-user-management/usepromptmfa) hook to prompt the user to authenticate with MFA
* [useIsMfaRequiredForAction](/react/reference/hooks/login-user-management/useismfarequiredforaction) hook to check if MFA is required for the action that the user is attempting to perform
