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

# Migrating from action-based MFA to step-up authentication

> [!NOTE]
>
> This guide is only relevant if you previously used **action-based MFA** to protect sensitive wallet operations. If you didn't, you can skip it.

If you previously used **action-based MFA** to protect sensitive wallet operations, step-up authentication replaces and extends that functionality. The core concept is the same — require users to re-verify before sensitive actions — but step-up auth adds **scoped elevated access tokens**, **re-authentication methods** for non-MFA users, and **automatic token handling** by the SDK.

## What changed

| | Action-based MFA (before) | Step-up authentication (after) |
| - | - | - |
| **Hook** | `useMfa` / `useAuthenticatePasskeyMFA` | `useStepUpAuthentication` |
| **Token type** | Unscoped MFA token (`createMfaToken`) | Scoped elevated access token (`requestedScopes`) |
| **Token handling** | SDK-managed MFA token | SDK-managed elevated access token (scoped JWT) |
| **Who can verify** | Only users with MFA methods (TOTP/Passkey) | All users — MFA methods, email OTP, SMS OTP, wallet signature, or social OAuth |
| **Scope** | None — token accepted for any action | Bound to specific scopes (`wallet:export`, `wallet:sign`, `credential:link`, `credential:unlink`) |
| **Expiration** | Single-use, no time limit | Time-limited (5 min single-use, 10 min multi-use) |

## Console changes

No console changes are required — your existing action-based MFA event
configuration (WaaS Export, WaaS Refresh, WaaS Sign, WaaS Reshare) carries over.

> [!WARNING]
>
> Make sure your code is fully updated before accepting the minimum API version at [Console > Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api). Once set to `2026_04_01`, the backend enforces step-up authentication immediately.

## AI-assisted upgrade prompts

> [!TIP]
>
> These prompts are designed for AI coding agents (Cursor, Claude Code, Copilot). Always review generated code before committing — agents can misread your project structure.
>
> Install the Dynamic MCP below so your agent can reference live documentation automatically. If you don't have it installed, the prompts fall back to `https://www.dynamic.xyz/docs/llms.txt`.

> [tab] Cursor
>
> [Click here to add the MCP to Cursor.](https://cursor.com/en/install-mcp?name=dynamic\&config=eyJ1cmwiOiJodHRwczovL3d3dy5keW5hbWljLnh5ei9kb2NzL21jcCJ9)

> [tab] Claude Code
>
> ```bash theme={"system"}
> claude mcp add --transport http dynamic https://www.dynamic.xyz/docs/mcp
> ```

> [tab] Codex
>
> ```bash theme={"system"}
> codex mcp add dynamic --url https://www.dynamic.xyz/docs/mcp
> ```

> [tab] Other
>
> If your editor or agent supports remote HTTP MCP, add a server that points at `https://www.dynamic.xyz/docs/mcp`. The exact steps depend on the product; use its MCP documentation for URL-based or HTTP transport setup.

Copy the prompt for your SDK and run it in your agent.

> [tab] React
>
> ```
> I'm migrating my Dynamic SDK headless integration from action-based MFA to step-up authentication as part of the 2026_04_01 API upgrade.
>
> If you have the Dynamic MCP installed, use it to reference the latest API.
> Otherwise reference: https://www.dynamic.xyz/docs/llms.txt
>
> In my codebase:
> 1. Find all usages of `useMfa`, `useAuthenticatePasskeyMFA`, and `usePromptMfaAuth`
> 2. Replace them with `useStepUpAuthentication` from `@dynamic-labs/sdk-react-core`
> 3. Replace all `createMfaToken` options with `requestedScopes` using the
>    appropriate `TokenScope` from `@dynamic-labs/sdk-api-core`
> 4. Replace all `useIsMfaRequiredForAction` calls with `isStepUpRequired`
> 5. Do not modify any files outside of the auth flow
>
> After making changes, summarize what was updated and flag anything that
> needs manual review.
> ```

> [tab] JavaScript
>
> ```
> I'm migrating my Dynamic SDK headless integration from action-based MFA
> to step-up authentication as part of the 2026_04_01 API upgrade.
>
> If you have the Dynamic MCP installed, use it to reference the latest API.
> Otherwise reference: https://www.dynamic.xyz/docs/llms.txt
>
> In my codebase:
> 1. Find all usages of `isMfaRequiredForAction` from `@dynamic-labs-sdk/client`
> 2. Replace them with `checkStepUpAuth`
> 3. Replace all `createMfaToken` and `createMfaTokenOptions` with `requestedScopes`
>    using the appropriate `TokenScope` from `@dynamic-labs-sdk/client`
>    on both `authenticateTotpMfaDevice` and `authenticatePasskeyMFA`
> 4. Do not modify any files outside of the auth flow
>
> After making changes, summarize what was updated and flag anything that
> needs manual review.
> ```

> [tab] React Native
>
> ```
> I'm migrating my Dynamic SDK headless integration from action-based MFA
> to step-up authentication as part of the 2026_04_01 API upgrade.
>
> If you have the Dynamic MCP installed, use it to reference the latest API.
> Otherwise reference: https://www.dynamic.xyz/docs/llms.txt
>
> In my codebase:
> 1. Find all usages of `client.mfa.isRequiredForAction` and replace with
>    `client.stepUpAuth.isStepUpRequired`
> 2. Find all usages of `client.mfa.authenticateDevice` and replace with
>    `client.stepUpAuth.verifyTotpMfa`
> 3. Find all usages of `client.passkeys.authenticateMFA` and replace with
>    `client.stepUpAuth.verifyPasskeyMfa`
> 4. Find all usages of `client.ui.mfa.show` and replace with
>    `client.stepUpAuth.promptStepUpAuth`
> 5. Replace all `createMfaToken` options with `requestedScopes` using
>    the appropriate `TokenScope`
> 6. Do not modify any files outside of the auth flow
>
> After making changes, summarize what was updated and flag anything that
> needs manual review.
> ```

> [tab] Flutter
>
> ```
> I'm migrating my Dynamic SDK headless integration from action-based MFA
> to step-up authentication as part of the 2026_04_01 API upgrade.
>
> If you have the Dynamic MCP installed, use it to reference the latest API.
> Otherwise reference: https://www.dynamic.xyz/docs/llms.txt
>
> In my codebase:
> 1. Find all usages of DynamicSDK.instance.mfa related to action-based MFA checks
> 2. Replace them with DynamicSDK.instance.stepUpAuth.isStepUpRequired
> 3. Replace verification calls with the appropriate method:
>    - TOTP: DynamicSDK.instance.stepUpAuth.verifyTotpMfa
>    - Passkey: DynamicSDK.instance.stepUpAuth.verifyPasskeyMfa
>    - Prompt: DynamicSDK.instance.stepUpAuth.promptStepUpAuth
> 4. Replace all scope arguments with the string equivalents:
>    'wallet:export', 'wallet:sign', 'credential:link', 'credential:unlink'
> 5. Do not modify any files outside of the auth flow
>
> After making changes, summarize what was updated and flag anything that
> needs manual review.
> ```

> [tab] Kotlin
>
> ```
> I'm migrating my Dynamic SDK headless integration from action-based MFA
> to step-up authentication as part of the 2026_04_01 API upgrade.
>
> If you have the Dynamic MCP installed, use it to reference the latest API.
> Otherwise reference: https://www.dynamic.xyz/docs/llms.txt
>
> In my codebase:
> 1. Find all usages of action-based MFA checks in my Kotlin integration
> 2. Replace them with sdk.stepUpAuth.isStepUpRequired(scope)
> 3. Replace verification calls with the appropriate method:
>    - TOTP: sdk.stepUpAuth.verifyTotpMfa(code, deviceId, requestedScopes)
>    - Passkey: sdk.stepUpAuth.verifyPasskeyMfa(requestedScopes)
>    - OTP: sdk.stepUpAuth.verifyOtp(verificationToken, requestedScopes)
>    - Or let the SDK decide: sdk.stepUpAuth.promptStepUpAuth(requestedScopes)
> 4. Replace all scope arguments with the appropriate string equivalents:
>    'wallet:export', 'wallet:sign', 'credential:link', 'credential:unlink'
> 5. Do not modify any files outside of the auth flow
>
> After making changes, summarize what was updated and flag anything that
> needs manual review.
> ```

> [tab] Swift
>
> ```
> I'm migrating my Dynamic SDK headless integration from action-based MFA
> to step-up authentication as part of the 2026_04_01 API upgrade.
>
> If you have the Dynamic MCP installed, use it to reference the latest API.
> Otherwise reference: https://www.dynamic.xyz/docs/llms.txt
>
> In my codebase:
> 1. Find all usages of action-based MFA checks in my Swift integration
> 2. Replace them with sdk.stepUpAuth.isStepUpRequired(scope:)
> 3. Replace verification calls with the appropriate method:
>    - TOTP: sdk.stepUpAuth.verifyTotpMfa(code:deviceId:requestedScopes:)
>    - Passkey: sdk.stepUpAuth.verifyPasskeyMfa(requestedScopes:)
>    - OTP: sdk.stepUpAuth.verifyOtp(verificationToken:requestedScopes:)
>    - Or let the SDK decide: sdk.stepUpAuth.promptStepUpAuth(requestedScopes:)
> 4. Replace all scope arguments with the appropriate string equivalents:
>    'wallet:export', 'wallet:sign', 'credential:link', 'credential:unlink'
> 5. Do not modify any files outside of the auth flow
>
> After making changes, summarize what was updated and flag anything that
> needs manual review.
> ```

## Code migration: before and after

> [!NOTE]
>
> The **After** examples include an `else` branch that falls through to legacy action-based MFA. It is only needed if some of your users already have legacy MFA enabled **and** you're deploying the updated SDK before the `2026_04_01` API version is confirmed. Once both the SDK and the minimum API version are upgraded, the transitional `else` branches can be removed since they are no longer needed.

#### Using Dynamic's built-in UI

> [tab] React
>
> **Before** — `usePromptMfaAuth` with `createMfaToken`:
>
> ```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();
> ```
>
> **After** — `useStepUpAuthentication` with `requestedScopes`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window when the frontend is deployed before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```tsx theme={"system"}
>     import {
>       useStepUpAuthentication,
>       usePromptMfaAuth,
>       useIsMfaRequiredForAction,
>     } from '@dynamic-labs/sdk-react-core';
>     import { TokenScope, MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const { isStepUpRequired, promptStepUpAuth } = useStepUpAuthentication();
>     const promptMfaAuth = usePromptMfaAuth();
>     const isMfaRequiredForAction = useIsMfaRequiredForAction();
>
>     if (await isStepUpRequired({ scope: TokenScope.Walletexport })) {
>       await promptStepUpAuth({
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy MFA prompt for users with an existing passkey / TOTP / device.
>       const isMfaRequired = await isMfaRequiredForAction({
>         mfaAction: MFAAction.WalletWaasExport,
>       });
>       if (isMfaRequired) {
>         await promptMfaAuth({ createMfaToken: true });
>       }
>     }
>
>     // Token is stored — SDK attaches it automatically
>     await primaryWallet.exportWaasPrivateKey();
> ```

> [tab] React Native
>
> **Before** — `client.ui.mfa.show` with `createMfaToken`:
>
> ```typescript theme={"system"}
>     import { MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const isMfaRequired = await client.mfa.isRequiredForAction({
>       mfaAction: MFAAction.WalletWaasExport,
>     });
>
>     if (isMfaRequired) {
>       await client.ui.mfa.show({ createMfaToken: true });
>     }
>
>     await primaryWallet.exportWaasPrivateKey();
> ```
>
> **After** — `client.stepUpAuth.promptStepUpAuth` with `requestedScopes`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window when the frontend is deployed before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```typescript theme={"system"}
>     import { TokenScope, MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const isRequired = await client.stepUpAuth.isStepUpRequired({
>       scope: TokenScope.Walletexport,
>     });
>
>     if (isRequired) {
>       await client.stepUpAuth.promptStepUpAuth({
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy MFA prompt for users with an existing passkey / TOTP / device.
>       const mfaRequired = await client.mfa.isRequiredForAction({
>         mfaAction: MFAAction.WalletWaasExport,
>       });
>       if (mfaRequired) {
>         await client.ui.mfa.show({ createMfaToken: true });
>       }
>     }
>
>     // Token is stored — SDK attaches it automatically
>     await primaryWallet.exportWaasPrivateKey();
> ```

> [tab] Flutter (no built-in UI)
>
> Flutter did not have a built-in action-based MFA UI. Use `promptStepUpAuth` to add step-up verification:
>
> ```dart theme={"system"}
>     import 'package:dynamic_sdk/dynamic_sdk.dart';
>
>     final isRequired = await DynamicSDK.instance.stepUpAuth
>         .isStepUpRequired('wallet:export');
>
>     if (isRequired) {
>       await DynamicSDK.instance.stepUpAuth.promptStepUpAuth(
>         requestedScopes: ['wallet:export'],
>       );
>     }
>
>     // Token is stored — SDK attaches it automatically
>     await performExport();
> ```

#### Headless TOTP

> [tab] React
>
> **Before** — `useMfa` with `authenticateDevice` and `createMfaToken`:
>
> ```tsx theme={"system"}
>     import { useMfa, useIsMfaRequiredForAction } from "@dynamic-labs/sdk-react-core";
>     import { MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const { authenticateDevice } = useMfa();
>     const isMfaRequiredForAction = useIsMfaRequiredForAction();
>
>     const requires = await isMfaRequiredForAction({
>       mfaAction: MFAAction.WalletWaasExport,
>     });
>
>     if (requires) {
>       await authenticateDevice({
>         code: totpCode,
>         createMfaToken: { singleUse: true },
>       });
>     }
> ```
>
> **After** — `useStepUpAuthentication` with `verifyTotpMfa`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```tsx theme={"system"}
>     import {
>       useStepUpAuthentication,
>       useMfa,
>       useIsMfaRequiredForAction,
>     } from '@dynamic-labs/sdk-react-core';
>     import { TokenScope, MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const { isStepUpRequired, verifyTotpMfa } = useStepUpAuthentication();
>     const { authenticateDevice } = useMfa();
>     const isMfaRequiredForAction = useIsMfaRequiredForAction();
>
>     if (await isStepUpRequired({ scope: TokenScope.Walletexport })) {
>       await verifyTotpMfa({
>         code: totpCode,
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy TOTP verification for users with an existing MFA device.
>       const requires = await isMfaRequiredForAction({
>         mfaAction: MFAAction.WalletWaasExport,
>       });
>       if (requires) {
>         await authenticateDevice({
>           code: totpCode,
>           createMfaToken: { singleUse: true },
>         });
>       }
>     }
> ```

> [tab] JavaScript
>
> **Before** — `authenticateTotpMfaDevice` with `createMfaTokenOptions`:
>
> ```javascript theme={"system"}
>     import { isMfaRequiredForAction, authenticateTotpMfaDevice, MFAAction } from '@dynamic-labs-sdk/client';
>
>     const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
>
>     if (required) {
>       await authenticateTotpMfaDevice({
>         code: '123456',
>         createMfaTokenOptions: { singleUse: true },
>       });
>     }
> ```
>
> **After** — same function with `requestedScopes`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```javascript theme={"system"}
>     import {
>       checkStepUpAuth,
>       authenticateTotpMfaDevice,
>       isMfaRequiredForAction,
>       MFAAction,
>       TokenScope,
>     } from '@dynamic-labs-sdk/client';
>
>     const { isRequired } = await checkStepUpAuth({ scope: TokenScope.Walletexport });
>
>     if (isRequired) {
>       await authenticateTotpMfaDevice({
>         code: '123456',
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy TOTP verification for users with an existing MFA device.
>       const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
>       if (required) {
>         await authenticateTotpMfaDevice({
>           code: '123456',
>           createMfaTokenOptions: { singleUse: true },
>         });
>       }
>     }
> ```

> [tab] React Native
>
> **Before** — `client.mfa.authenticateDevice` with `createMfaToken`:
>
> ```typescript theme={"system"}
>     import { MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const requires = await client.mfa.isRequiredForAction({
>       mfaAction: MFAAction.WalletWaasExport,
>     });
>
>     if (requires) {
>       await client.mfa.authenticateDevice({
>         code: totpCode,
>         createMfaToken: { singleUse: true },
>       });
>     }
> ```
>
> **After** — `client.stepUpAuth.verifyTotpMfa` with `requestedScopes`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```typescript theme={"system"}
>     import { TokenScope, MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const isRequired = await client.stepUpAuth.isStepUpRequired({
>       scope: TokenScope.Walletexport,
>     });
>
>     if (isRequired) {
>       await client.stepUpAuth.verifyTotpMfa({
>         code: totpCode,
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy TOTP verification for users with an existing MFA device.
>       const requires = await client.mfa.isRequiredForAction({
>         mfaAction: MFAAction.WalletWaasExport,
>       });
>       if (requires) {
>         await client.mfa.authenticateDevice({
>           code: totpCode,
>           createMfaToken: { singleUse: true },
>         });
>       }
>     }
> ```

> [tab] Flutter
>
> ```dart theme={"system"}
>     import 'package:dynamic_sdk/dynamic_sdk.dart';
>
>     final isRequired = await DynamicSDK.instance.stepUpAuth
>         .isStepUpRequired('wallet:export');
>
>     if (isRequired) {
>       await DynamicSDK.instance.stepUpAuth.verifyTotpMfa(
>         code: totpCode,
>         requestedScopes: ['wallet:export'],
>       );
>     }
>
> ```

#### Headless Passkey

> [tab] React
>
> **Before** — `useAuthenticatePasskeyMFA` with `createMfaToken`:
>
> ```tsx theme={"system"}
>     import {
>       useAuthenticatePasskeyMFA,
>       useGetPasskeys,
>       useIsMfaRequiredForAction,
>       useRegisterPasskey,
>     } from "@dynamic-labs/sdk-react-core";
>     import { MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const authenticatePasskeyMFA = useAuthenticatePasskeyMFA();
>     const getPasskeys = useGetPasskeys();
>     const registerPasskey = useRegisterPasskey();
>     const isMfaRequiredForAction = useIsMfaRequiredForAction();
>
>     const requires = await isMfaRequiredForAction({
>       mfaAction: MFAAction.WalletWaasExport,
>     });
>
>     if (requires) {
>       const passkeys = await getPasskeys();
>       if (passkeys.length === 0) {
>         await registerPasskey();
>       }
>
>       await authenticatePasskeyMFA({
>         createMfaToken: { singleUse: false },
>       });
>     }
> ```
>
> **After** — `useStepUpAuthentication` with `verifyPasskeyMfa`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```tsx theme={"system"}
>     import {
>       useStepUpAuthentication,
>       useAuthenticatePasskeyMFA,
>       useGetPasskeys,
>       useRegisterPasskey,
>       useIsMfaRequiredForAction,
>     } from '@dynamic-labs/sdk-react-core';
>     import { TokenScope, MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const { isStepUpRequired, verifyPasskeyMfa } = useStepUpAuthentication();
>     const authenticatePasskeyMFA = useAuthenticatePasskeyMFA();
>     const getPasskeys = useGetPasskeys();
>     const registerPasskey = useRegisterPasskey();
>     const isMfaRequiredForAction = useIsMfaRequiredForAction();
>
>     if (await isStepUpRequired({ scope: TokenScope.Walletexport })) {
>       await verifyPasskeyMfa({
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy passkey MFA for users with an existing registered passkey.
>       const requires = await isMfaRequiredForAction({
>         mfaAction: MFAAction.WalletWaasExport,
>       });
>       if (requires) {
>         const passkeys = await getPasskeys();
>         if (passkeys.length === 0) {
>           await registerPasskey();
>         }
>         await authenticatePasskeyMFA({
>           createMfaToken: { singleUse: false },
>         });
>       }
>     }
> ```

> [tab] JavaScript
>
> **Before** — `authenticatePasskeyMFA` with `createMfaToken`:
>
> ```javascript theme={"system"}
>     import { isMfaRequiredForAction, authenticatePasskeyMFA, MFAAction } from '@dynamic-labs-sdk/client';
>
>     const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
>
>     if (required) {
>       await authenticatePasskeyMFA({
>         createMfaToken: { singleUse: true },
>       });
>     }
> ```
>
> **After** — same function with `requestedScopes`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```javascript theme={"system"}
>     import {
>       checkStepUpAuth,
>       authenticatePasskeyMFA,
>       isMfaRequiredForAction,
>       MFAAction,
>       TokenScope,
>     } from '@dynamic-labs-sdk/client';
>
>     const { isRequired } = await checkStepUpAuth({ scope: TokenScope.Walletexport });
>
>     if (isRequired) {
>       await authenticatePasskeyMFA({
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy passkey MFA for users with an existing registered passkey.
>       const required = await isMfaRequiredForAction({ mfaAction: MFAAction.WalletWaasExport });
>       if (required) {
>         await authenticatePasskeyMFA({
>           createMfaToken: { singleUse: true },
>         });
>       }
>     }
> ```

> [tab] React Native
>
> **Before** — `client.passkeys.authenticateMFA` with `createMfaToken`:
>
> ```typescript theme={"system"}
>     import { MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const requires = await client.mfa.isRequiredForAction({
>       mfaAction: MFAAction.WalletWaasExport,
>     });
>
>     if (requires) {
>       const passkeys = await client.passkeys.get();
>       if (passkeys.length === 0) {
>         await client.passkeys.register();
>       }
>
>       await client.passkeys.authenticateMFA({
>         createMfaToken: { singleUse: false },
>       });
>     }
> ```
>
> **After** — `client.stepUpAuth.verifyPasskeyMfa` with `requestedScopes`. The `else` branch is only needed if some of your users already have legacy MFA enabled — it handles the window before the `2026_04_01` API version is confirmed. Remove it once both the SDK and the minimum API version are upgraded:
>
> ```typescript theme={"system"}
>     import { TokenScope, MFAAction } from '@dynamic-labs/sdk-api-core';
>
>     const isRequired = await client.stepUpAuth.isStepUpRequired({
>       scope: TokenScope.Walletexport,
>     });
>
>     if (isRequired) {
>       await client.stepUpAuth.verifyPasskeyMfa({
>         requestedScopes: [TokenScope.Walletexport],
>       });
>     } else {
>       // Transitional: 2026_04_01 API version not yet confirmed.
>       // Legacy passkey MFA for users with an existing registered passkey.
>       const requires = await client.mfa.isRequiredForAction({
>         mfaAction: MFAAction.WalletWaasExport,
>       });
>       if (requires) {
>         const passkeys = await client.passkeys.get();
>         if (passkeys.length === 0) {
>           await client.passkeys.register();
>         }
>         await client.passkeys.authenticateMFA({
>           createMfaToken: { singleUse: false },
>         });
>       }
>     }
> ```

> [tab] Flutter
>
> ```dart theme={"system"}
>     import 'package:dynamic_sdk/dynamic_sdk.dart';
>
>     final isRequired = await DynamicSDK.instance.stepUpAuth
>         .isStepUpRequired('wallet:export');
>
>     if (isRequired) {
>       await DynamicSDK.instance.stepUpAuth.verifyPasskeyMfa(
>         requestedScopes: ['wallet:export'],
>       );
>     }
>
> ```

## Migration checklist

> [tab] React
>
> 1. Upgrade to React SDK `4.76.0` or later
> 2. Replace `useMfa` / `useAuthenticatePasskeyMFA` / `usePromptMfaAuth` with `useStepUpAuthentication`
> 3. Replace `createMfaToken` with `requestedScopes` using the appropriate `TokenScope`
> 4. Replace `useIsMfaRequiredForAction` with `isStepUpRequired`
> 5. If you have existing legacy MFA users and you're deploying the SDK update before accepting the new API version, keep the transitional `else` branch shown in each **After** example so those users still get prompted
> 6. Test the step-up flow end-to-end for each protected action
> 7. Accept the minimum API version `2026_04_01` in your [console](https://console.dynamic.xyz/dashboard/developer/api)
> 8. Remove the transitional `else` branches
>
> For the full step-up authentication reference (all verification methods, scopes, token lifecycle), see [Step-up authentication](https://docs.dynamic.xyz/docs/javascript/authentication-methods/step-up-auth/overview.md).

> [tab] JavaScript
>
> 1. Upgrade to JavaScript SDK `0.24.1` or later
> 2. Replace `isMfaRequiredForAction` with `checkStepUpAuth`
> 3. Replace `createMfaToken` / `createMfaTokenOptions` with `requestedScopes` on `authenticateTotpMfaDevice` and `authenticatePasskeyMFA`
> 4. If you have existing legacy MFA users and you're deploying the SDK update before accepting the new API version, keep the transitional `else` branch shown in each **After** example so those users still get prompted
> 5. Test the step-up flow end-to-end for each protected action
> 6. Accept the minimum API version `2026_04_01` in your [console](https://console.dynamic.xyz/dashboard/developer/api)
> 7. Remove the transitional `else` branches
>
> For the full step-up authentication reference (all verification methods, scopes, token lifecycle), see [Step-up authentication](https://docs.dynamic.xyz/docs/javascript/authentication-methods/step-up-auth/overview.md).

> [tab] React Native
>
> 1. Upgrade to React Native SDK `4.76.0` or later
> 2. Replace `client.mfa.isRequiredForAction` with `client.stepUpAuth.isStepUpRequired`
> 3. Replace `client.mfa.authenticateDevice` with `client.stepUpAuth.verifyTotpMfa`
> 4. Replace `client.passkeys.authenticateMFA` with `client.stepUpAuth.verifyPasskeyMfa`
> 5. Replace `client.ui.mfa.show` with `client.stepUpAuth.promptStepUpAuth`
> 6. If you have existing legacy MFA users and you're deploying the SDK update before accepting the new API version, keep the transitional `else` branch shown in each **After** example so those users still get prompted
> 7. Test the step-up flow end-to-end for each protected action
> 8. Accept the minimum API version `2026_04_01` in your [console](https://console.dynamic.xyz/dashboard/developer/api)
> 9. Remove the transitional `else` branches
>
> For the full step-up authentication reference (all verification methods, scopes, token lifecycle), see [Step-up authentication](https://docs.dynamic.xyz/docs/react-native/authentication-methods/step-up-auth/overview.md).

> [tab] Flutter
>
> 1. Upgrade to Flutter SDK `1.2.10` or later
> 2. Implement step-up checks with `DynamicSDK.instance.stepUpAuth.isStepUpRequired`
> 3. Add verification flows using `promptStepUpAuth`, `verifyTotpMfa`, or `verifyPasskeyMfa`
> 4. Test the step-up flow end-to-end for each protected action
> 5. Accept the minimum API version `2026_04_01` in your [console](https://console.dynamic.xyz/dashboard/developer/api)
>
> For the full step-up authentication reference (all verification methods, scopes, token lifecycle), see [Step-up authentication](https://docs.dynamic.xyz/docs/flutter/authentication-methods/step-up-auth/overview.md).

```
```


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