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

# Step-Up Authentication

> Require users to re-verify their identity before sensitive actions using elevated access tokens. Includes re-auth (email, SMS, social/OAuth, wallet) and MFA (passkey, TOTP, recovery codes).

> [!NOTE]
>
> Step-up authentication is enforced when you set your minimum API version to `2026-04-01` in [Console > Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api). Implement the required flows in your app before enabling enforcement. See the [API version upgrade guide](https://docs.dynamic.xyz/docs/get-started/migrations/api/2026_04_01.md) for details.

## Overview

Step-up authentication requires users to re-verify their identity before performing sensitive actions, such as exporting a private key, linking a new credential, deleting a passkey, etc. After successful verification, the user receives an **elevated access token**: a short-lived, scope-bound JWT that authorizes the specific operation.

Step-up authentication encompasses both re-authentication and MFA:

* **Re-authentication**: Email OTP, SMS OTP, OAuth (social), or external wallet signature. The user proves control of an existing credential. Embedded wallets cannot be used for step-up verification.
* **MFA (multi-factor authentication)**: Passkey, TOTP, or recovery code. The user proves possession of a registered second factor.

When MFA is enabled and the user has registered MFA methods, re-authentication methods are blocked for elevated token issuance. The user must verify through MFA instead.

> [!NOTE]
>
> For MFA-specific setup (enrollment, session-based MFA, device management), see [Multi-Factor Authentication (MFA)](https://docs.dynamic.xyz/docs/auth/mfa.md). If your users have MFA enabled, the SDK automatically routes step-up verification through MFA.

> [!WARNING]
>
> **Password-encrypted wallets** (WaaS wallets protected by a user-set password) are outside the scope of step-up authentication. Password decryption is handled by the wallet service independently.

## Enabling step-up authentication

Step-up authentication is enforced once you set your minimum API version to `2026_04_01` via the [Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api) page in your console.

Until you set the minimum version, the backend will not require elevated access tokens on any endpoints, so you can implement the step-up flows in your app first and enable enforcement when ready.

New environments have step-up authentication enforced by default. Only environments created before `2026_04_01` was introduced need to opt in.

> [!NOTE]
>
> After enabling, step-up is enforced server-side. Even if a client sends requests without an elevated token, the backend will reject them with a `403`. Make sure your integration handles the step-up flow before enabling enforcement.

## When is step-up authentication required?

When step-up authentication is enabled for your environment, the backend enforces it on sensitive operations. The user must present a valid elevated access token or the request is rejected with a `403`.

| Action | Required scope | Enforced |
| - | - | - |
| Export a wallet private key | `wallet:export` | Always |
| Sign a message or transaction | `wallet:sign` | Only if configured |
| Link a new credential (wallet, email, phone, social, passkey, MFA device) | `credential:link` | Always |
| Unlink a credential (wallet, email, phone, social, passkey, MFA device) | `credential:unlink` | Always |

These are the most common operations. Additional scopes are also enforced, including other wallet operations (`wallet:delegate`, `wallet:delete`, `wallet:refresh`, `wallet:reshare`, `wallet:restore`, `wallet:offline_recovery_trigger`), account changes (`user:update`, `user:delete`), device and MFA management (`device:register`, `device:delete`, `mfa_device:update`, `mfa_device:delete`, `passkey:delete`), embedded wallet policy changes (`waas:policy:update`), and business account operations (`business_account:*`). All business account scopes are always enforced except `business_account:link_wallet`, which is required by default but can be turned off in Console > Security under Business Account Actions.

Step-up is **not** required for:

* Initial sign-in and sign-up flows
* Reading user data or wallet balances
* Signing transactions with an embedded wallet (unless [`wallet:sign`](#scopes) is configured)

If you're using Dynamic's SDK widget (React SDK), these prompts are handled automatically. The widget detects when step-up is needed and shows the verification UI before proceeding. For headless SDKs (JavaScript, React Native), use the SDK methods documented in the implementation guides below.

## How it works

1. **Check**: Before a sensitive action, the SDK checks whether the user already has a valid elevated access token for the required scope.
2. **Verify**: If no valid token exists, the user is prompted to verify. The SDK automatically selects MFA or re-authentication based on the user's configuration.
3. **Use**: After successful verification, the token is stored in SDK state. The SDK automatically attaches it to the relevant API calls, so no manual token handling is needed.

```mermaid theme={"system"}
sequenceDiagram
    participant App
    participant SDK
    participant Backend

    App->>SDK: checkStepUpAuth({ scope })
    SDK-->>App: { isRequired: true, credentials, defaultCredentialId }

    App->>SDK: promptStepUpAuth({ requestedScopes })
    note right of SDK: Auto-routes to MFA or re-auth
    SDK->>Backend: Verify identity + requestedScopes
    Backend-->>SDK: Elevated access token (scoped JWT)
    SDK->>SDK: Store token in client state

    App->>SDK: Perform sensitive operation
    note right of SDK: Token attached automatically
    SDK->>Backend: Operation + elevated token
    Backend-->>App: Success
```

## Supported verification methods

| Method | Type | Description |
| - | - | - |
| **Email OTP** | Re-auth | One-time code sent to the user's verified email |
| **SMS OTP** | Re-auth | One-time code sent to the user's verified phone number |
| **Wallet signature** | Re-auth | Sign a message with a connected wallet |
| **Social (OAuth)** | Re-auth | Re-authenticate with a linked social account (Google, GitHub, etc.) |
| **Passkey** | MFA | Authenticate with a registered passkey (WebAuthn) |
| **TOTP** | MFA | Enter a code from an authenticator app |
| **Recovery codes** | MFA | Use a backup recovery code |

## Scopes

Elevated access tokens are bound to specific scopes. When requesting step-up authentication, specify which scopes you need via `requestedScopes`. The table below shows each scope, which SDK operations require it, and the underlying API endpoints that enforce it.

**Exclusive** scopes must be requested alone; they cannot be combined with other scopes in a single `requestedScopes` request. Non-exclusive scopes can be freely combined (e.g., `['credential:link', 'credential:unlink']`).

### Wallet operations

| Scope | SDK operation | API endpoint | Single-use | Exclusive |
| - | - | - | - | - |
| `wallet:export` | Export a wallet's private key or seed phrase | [`POST .../waas/{walletId}/privateKey/export`](https://docs.dynamic.xyz/docs/api-reference/sdk/export-private-key-of-a-waas-account) | Yes | Yes |
| `wallet:sign` | Sign a message or transaction with an embedded wallet | [`POST .../waas/{walletId}/delegatedAccess/signMessage`](https://docs.dynamic.xyz/docs/api-reference/waas/sign-a-message-with-a-delegated-waas-account) | Configurable | Yes |

### Credential operations

| Scope | SDK operation | API endpoint | Single-use | Exclusive |
| - | - | - | - | - |
| `credential:link` | Link a wallet | [`POST .../verify/link`](https://docs.dynamic.xyz/docs/api-reference/sdk/link-wallet-to-user) | No | No |
| | Verify a wallet (linking) | [`POST .../wallets/verify`](https://docs.dynamic.xyz/docs/api-reference/sdk/verify-payload-and-return-jwt) | | |
| | Link an OAuth social account | [`POST .../providers/{providerType}/verify`](https://docs.dynamic.xyz/docs/api-reference/sdk/oauth-provider-verify-endpoint) | | |
| | Verify email OTP (linking) | [`POST .../emailVerifications/verify`](https://docs.dynamic.xyz/docs/api-reference/sdk/verify-email-verification-request) | | |
| | Verify SMS OTP (linking) | [`POST .../smsVerifications/verify`](https://docs.dynamic.xyz/docs/api-reference/sdk/verify-sms-verification-request) | | |
| | Register a passkey MFA device | [`POST .../users/mfa/passkey`](https://docs.dynamic.xyz/docs/api-reference/sdk/register-a-new-passkey-mfa-device) | | |
| | Register a TOTP MFA device | [`POST .../users/mfa/totp`](https://docs.dynamic.xyz/docs/api-reference/sdk/verify-totp-mfa-device) | | |
| `credential:unlink` | Unlink a credential | [`POST .../verify/unlink`](https://docs.dynamic.xyz/docs/api-reference/sdk/unlink-wallet-from-user) | No | No |
| | Delete a passkey | [`DELETE .../users/passkeys`](https://docs.dynamic.xyz/docs/api-reference/sdk/delete-a-passkey) | | |
| | Delete an MFA device (TOTP) | [`DELETE .../users/mfa/{mfaDeviceId}`](https://docs.dynamic.xyz/docs/api-reference/sdk/delete-a-device-if-not-default-device) | | |

> [!NOTE]
>
> **Single-use scopes** (like `wallet:export`) are consumed after the SDK uses them for the first qualifying API call. The user must re-verify to perform the action again.

> [!NOTE]
>
> The `credential:link` scope is required on verification endpoints (email, SMS, OAuth, wallet) **only when the request includes `requestedScopes`**, meaning the caller is performing step-up reauthentication. Normal sign-in and sign-up flows through these same endpoints do not require an elevated token.

## Token lifecycle

| Property | Single-use tokens | Multi-use tokens |
| - | - | - |
| **Expiration** | 5 minutes (default) | 10 minutes (default) |
| **Consumed after use** | Yes: removed from state after the SDK uses it for the first qualifying API call | No: remains valid until expiration |
| **Can combine scopes** | No: must be the only scope requested | Yes: multiple scopes in one request |
| **Scope-bound** | Yes | Yes |
| **Automatic cleanup** | Expired tokens are filtered out automatically | Same |

> [!NOTE]
>
> These are defaults. For `wallet:sign`, `wallet:refresh`, and `user:update`, per-action settings can change the token behavior (single-use vs multi-use) and the expiration (between 60 and 600 seconds). All other scopes use the fixed defaults.

## Dynamic's built-in UI

When using Dynamic's built-in UI (available in the React SDK), step-up authentication is handled automatically for Dynamic's own operations:

* **Wallet export**: The export flow prompts for step-up before showing the private key.
* **Credential linking/unlinking**: Linking a new wallet, social account, or email/phone prompts for step-up in the widget UI.
* **Passkey/TOTP deletion**: The MFA management views prompt for step-up before deletion.

No additional code is needed for these flows when step-up authentication is enabled in your [console settings](https://console.dynamic.xyz/dashboard/security).

For **custom operations** or **headless implementations**, use the SDK methods documented in the guides below.

## Migration from MFA tokens

> [!WARNING]
>
> The `createMfaToken` parameter on MFA verification methods is deprecated. Use `requestedScopes` instead. See [migration guide](https://docs.dynamic.xyz/docs/get-started/migrations/api/2026_04_01-mfa-migration.md).

| | `createMfaToken` (deprecated) | `requestedScopes` (recommended) |
| - | - | - |
| **Token type** | Unscoped MFA token | Scoped elevated access token (JWT) |
| **Expiration** | Single-use, no time limit | Time-limited (5 or 10 min) |
| **Scope** | None: accepted for any action | Bound to specific scopes |
| **Storage** | Manual handling required | Automatically stored and applied by SDK |
| **MFA enforcement** | Not enforced for non-MFA methods | Enforced when MFA is enabled |

For a complete migration walkthrough with before/after code examples, see the [action-based MFA to step-up migration guide](https://docs.dynamic.xyz/docs/get-started/migrations/api/2026_04_01.md#migrating-from-action-based-mfa-to-step-up-authentication).

## SDK implementation guides

> [card] [JavaScript SDK](https://docs.dynamic.xyz/docs/javascript/authentication-methods/step-up-auth/overview.md)
>
> Headless implementation with direct function calls. Use this for vanilla JS, Vue, Svelte, Angular, or any non-React framework.

> [card] [React SDK](https://docs.dynamic.xyz/docs/react/authentication-methods/step-up-auth/overview.md)
>
> Hooks with automatic routing, Dynamic UI prompts, and headless React patterns.

> [card] [React Native SDK](https://docs.dynamic.xyz/docs/react-native/authentication-methods/step-up-auth/overview.md)
>
> Built-in UI prompts and headless verification for React Native mobile apps.

> [card] [Flutter SDK](https://docs.dynamic.xyz/docs/flutter/authentication-methods/step-up-auth/overview.md)
>
> Step-up auth module with built-in UI prompts and individual verification methods.

> [card] [Swift SDK](https://docs.dynamic.xyz/docs/swift/step-up-auth.md)
>
> Step-up auth module with built-in UI prompts and individual verification methods for iOS.

> [card] [Kotlin SDK](https://docs.dynamic.xyz/docs/kotlin/step-up-auth.md)
>
> Step-up auth module with built-in UI prompts and individual verification methods for Android.


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