> ## Documentation Index
> Fetch the complete documentation index at: https://www.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 in the Flutter SDK.

The Flutter SDK provides step-up authentication through the `StepUpAuthModule`, accessible via `DynamicSDK.instance.stepUpAuth`. You can check requirements, prompt built-in UI flows, or call individual verification methods for a fully custom experience.

After verification, the elevated access token is automatically stored and applied to subsequent API calls. You never need to manually handle the token.

For concepts, scopes, and token lifecycle, see [Step-up authentication overview](/overview/authentication/step-up-auth).

## Prerequisites

* `DynamicSDK` initialized with your environment ID
* At least one verification method enabled in your [dashboard security settings](https://app.dynamic.xyz/dashboard/security)
* Step-up authentication enabled for your environment

## Quick start

The pattern is: **check → verify → proceed**.

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';

Future<void> exportPrivateKey() async {
  // 1. Check if step-up is required
  final isRequired = await DynamicSDK.instance.stepUpAuth
      .isStepUpRequired('wallet:export');

  if (isRequired) {
    // 2. Prompt the user — auto-routes to MFA or re-auth
    await DynamicSDK.instance.stepUpAuth.promptStepUpAuth(
      requestedScopes: ['wallet:export'],
    );
  }

  // 3. Proceed — token is attached automatically
  await performExport();
}
```

## Checking step-up requirements

Use `isStepUpRequired` to check whether the user needs to re-verify for a given scope:

```dart theme={"system"}
final isRequired = await DynamicSDK.instance.stepUpAuth
    .isStepUpRequired('wallet:export');

if (isRequired) {
  // Trigger verification
}
```

## Prompt methods (built-in UI)

These methods show Dynamic's built-in UI for step-up authentication. The SDK handles method selection and user interaction.

### promptStepUpAuth

Automatically chooses between MFA and re-authentication based on the user's configuration. This is the recommended approach.

```dart theme={"system"}
final token = await DynamicSDK.instance.stepUpAuth.promptStepUpAuth(
  requestedScopes: ['wallet:export'],
);
```

### promptMfa

Explicitly prompts the user with MFA methods (passkey or TOTP). Use this when you know the user has MFA configured.

```dart theme={"system"}
final token = await DynamicSDK.instance.stepUpAuth.promptMfa(
  requestedScopes: ['wallet:export'],
);
```

### promptReauthenticate

Explicitly prompts the user to re-authenticate using a non-MFA method (email OTP, SMS OTP, or wallet signature).

```dart theme={"system"}
final token = await DynamicSDK.instance.stepUpAuth.promptReauthenticate(
  requestedScopes: ['wallet:export'],
);
```

## Individual verification methods

For custom UI implementations, you can call individual verification methods directly.

### Email/SMS OTP

```dart theme={"system"}
// 1. Send the OTP
final otpResult = await DynamicSDK.instance.stepUpAuth.sendOtp();

// 2. Collect the code from the user, then verify
final result = await DynamicSDK.instance.stepUpAuth.verifyOtp(
  verificationToken: userEnteredCode,
  requestedScopes: ['wallet:export'],
);
```

### Wallet signature (external wallets only)

Wallet-based step-up verification is only available for external wallets. Embedded wallets cannot be used for step-up authentication.

```dart theme={"system"}
await DynamicSDK.instance.stepUpAuth.verifyWallet(
  walletId: 'wallet-id',
  requestedScopes: ['wallet:export'],
);
```

### Passkey MFA

```dart theme={"system"}
final result = await DynamicSDK.instance.stepUpAuth.verifyPasskeyMfa(
  requestedScopes: ['wallet:export'],
);
```

### TOTP MFA

```dart theme={"system"}
final result = await DynamicSDK.instance.stepUpAuth.verifyTotpMfa(
  code: '123456',
  requestedScopes: ['wallet:export'],
);

// Optionally specify a device ID if the user has multiple TOTP devices
final result = await DynamicSDK.instance.stepUpAuth.verifyTotpMfa(
  code: '123456',
  deviceId: 'device-id',
  requestedScopes: ['wallet:export'],
);
```

### Recovery code

```dart theme={"system"}
final result = await DynamicSDK.instance.stepUpAuth.verifyRecoveryCode(
  code: 'recovery-code-here',
  requestedScopes: ['wallet:export'],
);
```

## Resetting state

Reset the step-up authentication state when needed (for example, when the user navigates away):

```dart theme={"system"}
await DynamicSDK.instance.stepUpAuth.resetState();
```

## Full example

Here's a complete example screen that checks step-up requirements and prompts the user:

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:flutter/material.dart';

class StepUpAuthExample extends StatefulWidget {
  const StepUpAuthExample({super.key});

  @override
  State<StepUpAuthExample> createState() => _StepUpAuthExampleState();
}

class _StepUpAuthExampleState extends State<StepUpAuthExample> {
  bool _isLoading = false;
  String? _result;

  Future<void> _exportWallet() async {
    setState(() {
      _isLoading = true;
      _result = null;
    });

    try {
      final isRequired = await DynamicSDK.instance.stepUpAuth
          .isStepUpRequired('wallet:export');

      if (isRequired) {
        await DynamicSDK.instance.stepUpAuth.promptStepUpAuth(
          requestedScopes: ['wallet:export'],
        );
      }

      // Perform the export — token is attached automatically
      // await performExport();

      setState(() => _result = 'Export completed');
    } catch (e) {
      setState(() => _result = 'Error: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: _isLoading ? null : _exportWallet,
          child: Text(_isLoading ? 'Verifying...' : 'Export Wallet'),
        ),
        if (_result != null) Text(_result!),
      ],
    );
  }
}
```

## Error handling

Wrap verification calls in try-catch blocks to handle user cancellations and verification failures:

```dart theme={"system"}
try {
  await DynamicSDK.instance.stepUpAuth.promptStepUpAuth(
    requestedScopes: ['wallet:export'],
  );
} catch (e) {
  // User cancelled or verification failed
  print('Step-up auth failed: $e');
}
```

## API reference

### StepUpAuthModule

| Method                                              | Description                                                                                               |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `isStepUpRequired(String scope)`                    | Check if step-up is required for a scope                                                                  |
| `promptStepUpAuth({requestedScopes})`               | Auto-route to MFA or re-auth UI                                                                           |
| `promptMfa({requestedScopes})`                      | Show MFA verification UI                                                                                  |
| `promptReauthenticate({requestedScopes})`           | Show re-authentication UI                                                                                 |
| `sendOtp({credentialId?})`                          | Send OTP to the user's verified credential. Pass optional `credentialId` to target a specific credential. |
| `verifyOtp({verificationToken, requestedScopes})`   | Verify an OTP code                                                                                        |
| `verifyWallet({walletId?, requestedScopes})`        | Verify via external wallet signature. Pass optional `walletId` to target a specific wallet.               |
| `verifyPasskeyMfa({requestedScopes})`               | Verify via passkey                                                                                        |
| `verifyTotpMfa({code, deviceId?, requestedScopes})` | Verify via TOTP code                                                                                      |
| `verifyRecoveryCode({code, requestedScopes})`       | Verify via recovery code                                                                                  |
| `resetState()`                                      | Reset step-up auth state                                                                                  |

## Available scopes

See the [complete scopes reference](/overview/authentication/step-up-auth#scopes) for all supported values.

## Related

* [Step-up authentication overview](/overview/authentication/step-up-auth) — Concepts, scopes, token lifecycle
* [MFA overview](/flutter/sdk-reference/authentication) — Flutter SDK authentication reference
