Skip to main content
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)
HookuseMfa / useAuthenticatePasskeyMFAuseStepUpAuthentication
Token typeUnscoped MFA token (createMfaToken)Scoped elevated access token (requestedScopes)
Token handlingSDK-managed MFA tokenSDK-managed elevated access token (scoped JWT)
Who can verifyOnly users with MFA methods (TOTP/Passkey)All users — MFA methods, email OTP, SMS OTP, wallet signature, or social OAuth
ScopeNone — token accepted for any actionBound to specific scopes (wallet:export, wallet:sign, credential:link, credential:unlink)
ExpirationSingle-use, no time limitTime-limited (5 min single-use, 10 min multi-use)

Dashboard changes

No dashboard changes are required — your existing action-based MFA event configuration (WaaS Export, WaaS Refresh, WaaS Sign, WaaS Reshare) carries over.
Make sure your code is fully updated before accepting the minimum API version at Dashboard > Developers > API & SDK Keys. Once set to 2026_04_01, the backend enforces step-up authentication immediately.

AI-assisted upgrade prompts

These prompts are designed for AI coding agents (Cursor, Claude Code, Copilot). Always review generated code before committing — agents can misread your project structure.For best results, install the Dynamic MCP in your agent so it can reference live documentation automatically. If you don’t have it installed, the prompts fall back to https://www.dynamic.xyz/docs/llms.txt.
Copy the prompt for your SDK and run it in your agent.
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.

Code migration: before and after

Using Dynamic’s built-in UI

BeforeusePromptMfaAuth with createMfaToken:
    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();
AfteruseStepUpAuthentication with requestedScopes:
    import { useStepUpAuthentication } from '@dynamic-labs/sdk-react-core';
    import { TokenScope } from '@dynamic-labs/sdk-api-core';

    const { isStepUpRequired, promptStepUpAuth } = useStepUpAuthentication();

    if (await isStepUpRequired({ scope: TokenScope.Walletexport })) {
      await promptStepUpAuth({
        requestedScopes: [TokenScope.Walletexport],
      });
    }

    // Token is stored — SDK attaches it automatically
    await primaryWallet.exportWaasPrivateKey();

Headless TOTP

BeforeuseMfa with authenticateDevice and createMfaToken:
    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 },
      });
    }
AfteruseStepUpAuthentication with verifyTotpMfa:
    import { useStepUpAuthentication } from '@dynamic-labs/sdk-react-core';
    import { TokenScope } from '@dynamic-labs/sdk-api-core';

    const { isStepUpRequired, verifyTotpMfa } = useStepUpAuthentication();

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

Headless Passkey

BeforeuseAuthenticatePasskeyMFA with createMfaToken:
    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 },
      });
    }
AfteruseStepUpAuthentication with verifyPasskeyMfa:
    import { useStepUpAuthentication } from '@dynamic-labs/sdk-react-core';
    import { TokenScope } from '@dynamic-labs/sdk-api-core';

    const { isStepUpRequired, verifyPasskeyMfa } = useStepUpAuthentication();

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

Migration checklist

  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. Test the step-up flow end-to-end for each protected action
  6. Accept the minimum API version 2026_04_01 in your dashboard
For the full step-up authentication reference (all verification methods, scopes, token lifecycle), see Step-up authentication.