> ## 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] Recommended: JavaScript SDK with React Hooks
>
> 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)](https://docs.dynamic.xyz/docs/javascript/reference/react-quickstart.md) to get started.

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.

## Console Setup

Configure Action-Based MFA in the Dynamic Console before implementing in your application. See [End-User MFA Configuration — Action-Based MFA](https://docs.dynamic.xyz/docs/platform/console/end-user-mfa.md#action-based-mfa) for console 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://docs.dynamic.xyz/docs/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](https://docs.dynamic.xyz/docs/react/wallets/embedded-wallets/mpc/delegated-access/revoking-delegation.md) and the user next signs in.

## Using step-up authentication (recommended)

Use the `useStepUpAuthentication` hook with `requestedScopes` to receive [elevated access tokens](https://docs.dynamic.xyz/docs/auth/step-up-auth.md) instead of legacy MFA tokens. This is the recommended approach and will be required in the next major version.

> [tab] 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] 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.

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

## 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](https://docs.dynamic.xyz/docs/auth/step-up-auth.md#migration-from-mfa-tokens).

> [tab] 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] 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;
> }
> ```

## 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](https://docs.dynamic.xyz/docs/react/reference/hooks/login-user-management/usepromptmfa.md) hook to prompt the user to authenticate with MFA
* [useIsMfaRequiredForAction](https://docs.dynamic.xyz/docs/react/reference/hooks/login-user-management/useismfarequiredforaction.md) hook to check if MFA is required for the action that the user is attempting to perform


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.