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

# Email & SMS Authentication

The Dynamic Flutter SDK provides email and SMS authentication through OTP (One-Time Password) verification. Users can sign in by receiving a verification code sent to their email address or phone number.

<Note>
  Email and SMS authentication must be enabled in your environment's dashboard settings before they can be used in your application.
</Note>

If users report delayed or missing OTP emails, see the [Email OTP delivery](/overview/troubleshooting/email-otp-delivery) troubleshooting guide.

## Email Authentication

### Send Email OTP

Send a verification code to the user's email address:

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

// Send OTP to email
await DynamicSDK.instance.auth.email.sendOTP("user@example.com");
```

### Verify Email OTP

Verify the OTP code entered by the user:

```dart theme={"system"}
// Verify the code
await DynamicSDK.instance.auth.email.verifyOTP("123456");
```

### Resend Email OTP

Allow users to request a new OTP code if the previous one expired or wasn't received:

```dart theme={"system"}
// Resend the code
await DynamicSDK.instance.auth.email.resendOTP();
```

### Rate Limits

Email verification is subject to the following rate limits:

* 3 attempts per 10 minutes per email address

This is in place to protect deliverability of emails and to prevent abuse.

## SMS Authentication

### Send SMS OTP

Send a verification code to the user's phone number using the `PhoneData` structure:

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

// Send OTP via SMS
await DynamicSDK.instance.auth.sms.sendOTP(
  PhoneData(
    dialCode: "+1",
    iso2: "US",
    phone: "5551234567"
  )
);
```

### Verify SMS OTP

Verify the OTP code entered by the user:

```dart theme={"system"}
// Verify the code
await DynamicSDK.instance.auth.sms.verifyOTP("123456");
```

### Resend SMS OTP

Allow users to request a new OTP code:

```dart theme={"system"}
// Resend the code
await DynamicSDK.instance.auth.sms.resendOTP();
```

### PhoneData Structure

The SMS authentication methods use a `PhoneData` object that contains:

* `dialCode`: The country dial code (e.g., "+1" for US, "+44" for UK)
* `iso2`: The two-letter country code (e.g., "US", "GB", "JP")
* `phone`: The phone number without the country code

**Examples:**

```dart theme={"system"}
// US phone number
PhoneData(dialCode: "+1", iso2: "US", phone: "5551234567")

// UK phone number
PhoneData(dialCode: "+44", iso2: "GB", phone: "7700900000")

// Japan phone number
PhoneData(dialCode: "+81", iso2: "JP", phone: "9012345678")
```

## External JWT Authentication

For apps with existing authentication systems, you can authenticate users with an external JWT:

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

await DynamicSDK.instance.auth.externalAuth.signInWithExternalJwt(
  props: SignInWithExternalJwtParams(jwt: yourJwtToken)
);
```

## Authentication State

### Check Current User

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

// Get current authentication token
final token = DynamicSDK.instance.auth.token;

if (token != null) {
  print('User is authenticated');
}
```

### Listen for Authentication Changes

Use streams to react to authentication state changes:

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

// Listen for token changes
StreamBuilder<String?>(
  stream: DynamicSDK.instance.auth.tokenChanges,
  builder: (context, snapshot) {
    final token = snapshot.data;

    if (token != null) {
      return AuthenticatedView();
    } else {
      return LoginView();
    }
  },
)

// Listen for user changes
StreamBuilder<dynamic>(
  stream: DynamicSDK.instance.auth.authenticatedUserChanges,
  builder: (context, snapshot) {
    final user = snapshot.data;

    if (user != null) {
      return Text('Welcome, ${user.email}!');
    } else {
      return const Text('Please sign in');
    }
  },
)
```

### Logout

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

// Logout the current user
await DynamicSDK.instance.auth.logout();
```

## Built-in UI

The easiest way to add authentication is using the built-in UI which handles all authentication methods:

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

DynamicSDK.instance.ui.showAuth();
```

## Complete Authentication Flow

Here's a complete example combining email and SMS authentication:

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

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

  @override
  State<AuthenticationScreen> createState() => _AuthenticationScreenState();
}

class _AuthenticationScreenState extends State<AuthenticationScreen> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _otpController = TextEditingController();

  bool _otpSent = false;
  bool _useEmail = true; // Toggle between email and SMS

  Future<void> _sendOTP() async {
    try {
      if (_useEmail) {
        await DynamicSDK.instance.auth.email.sendOTP(_emailController.text);
      } else {
        await DynamicSDK.instance.auth.sms.sendOTP(
          PhoneData(
            dialCode: "+1",
            iso2: "US",
            phone: _phoneController.text,
          ),
        );
      }

      setState(() {
        _otpSent = true;
      });
    } catch (e) {
      // Handle error
      print('Error sending OTP: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error sending OTP: $e')),
      );
    }
  }

  Future<void> _verifyOTP() async {
    try {
      if (_useEmail) {
        await DynamicSDK.instance.auth.email.verifyOTP(_otpController.text);
      } else {
        await DynamicSDK.instance.auth.sms.verifyOTP(_otpController.text);
      }

      // User is now authenticated - navigation handled by stream listeners
    } catch (e) {
      // Handle error
      print('Error verifying OTP: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error verifying OTP: $e')),
      );
    }
  }

  Future<void> _resendOTP() async {
    try {
      if (_useEmail) {
        await DynamicSDK.instance.auth.email.resendOTP();
      } else {
        await DynamicSDK.instance.auth.sms.resendOTP();
      }

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('OTP resent successfully')),
      );
    } catch (e) {
      // Handle error
      print('Error resending OTP: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_useEmail ? 'Email Authentication' : 'SMS Authentication'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // Toggle between email and SMS
            SegmentedButton<bool>(
              segments: const [
                ButtonSegment<bool>(value: true, label: Text('Email')),
                ButtonSegment<bool>(value: false, label: Text('SMS')),
              ],
              selected: {_useEmail},
              onSelectionChanged: (Set<bool> newSelection) {
                setState(() {
                  _useEmail = newSelection.first;
                  _otpSent = false;
                });
              },
            ),
            const SizedBox(height: 24),

            if (!_otpSent) ...[
              // Input for email or phone
              if (_useEmail)
                TextField(
                  controller: _emailController,
                  decoration: const InputDecoration(
                    labelText: 'Email Address',
                    border: OutlineInputBorder(),
                  ),
                  keyboardType: TextInputType.emailAddress,
                )
              else
                TextField(
                  controller: _phoneController,
                  decoration: const InputDecoration(
                    labelText: 'Phone Number',
                    border: OutlineInputBorder(),
                    helperText: 'Enter number without country code',
                  ),
                  keyboardType: TextInputType.phone,
                ),
              const SizedBox(height: 16),
              ElevatedButton(
                onPressed: _sendOTP,
                child: const Text('Send Verification Code'),
              ),
            ] else ...[
              // OTP verification
              Text(
                _useEmail
                    ? 'Enter the code sent to ${_emailController.text}'
                    : 'Enter the code sent to +1${_phoneController.text}',
              ),
              const SizedBox(height: 16),
              TextField(
                controller: _otpController,
                decoration: const InputDecoration(
                  labelText: 'Verification Code',
                  border: OutlineInputBorder(),
                ),
                keyboardType: TextInputType.number,
                maxLength: 6,
              ),
              const SizedBox(height: 16),
              ElevatedButton(
                onPressed: _verifyOTP,
                child: const Text('Verify Code'),
              ),
              const SizedBox(height: 8),
              TextButton(
                onPressed: _resendOTP,
                child: const Text('Resend Code'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _emailController.dispose();
    _phoneController.dispose();
    _otpController.dispose();
    super.dispose();
  }
}
```

## Configuration

### Dashboard Settings

Configure email and SMS authentication in your Dynamic dashboard:

1. **Enable Email Authentication**:
   * Go to [Login & User Profile](https://app.dynamic.xyz/dashboard/log-in-user-profile)
   * Toggle "Email" on to enable email authentication
   * No additional configuration is required

2. **Enable SMS Authentication**:
   * Go to [Login & User Profile](https://app.dynamic.xyz/dashboard/log-in-user-profile)
   * Toggle "SMS" on to enable SMS authentication
   * You can optionally provide your own SMS provider credentials in the phone number settings
   * This is required for phone numbers outside of US and Canada

## Best Practices

### 1. Error Handling

Always handle errors gracefully and provide clear feedback to users:

```dart theme={"system"}
try {
  await DynamicSDK.instance.auth.email.sendOTP(email);
} catch (e) {
  if (e.toString().contains('rate limit')) {
    showError('Too many attempts. Please try again later.');
  } else if (e.toString().contains('invalid email')) {
    showError('Please enter a valid email address.');
  } else {
    showError('Failed to send OTP: $e');
  }
}
```

### 2. User Experience

* Show loading states during authentication
* Provide clear error messages
* Allow users to resend OTP if needed
* Display the email/phone where OTP was sent
* Add countdown timer for resend button

### 3. Security

* Never store OTP codes
* Always use HTTPS connections
* Implement proper session management
* Handle token refresh automatically

## Troubleshooting

### OTP Not Received

* Check that the email/phone number is valid
* Look in spam folder for email OTP
* Verify that the provider is enabled in Dynamic dashboard
* Check rate limits haven't been exceeded

### Authentication Fails

* Ensure OTP code is entered correctly
* Check that the code hasn't expired (codes typically expire after 10 minutes)
* Verify network connectivity
* Check for any error messages in logs

### Session Not Persisting

* Ensure you're listening to `tokenChanges` or `authenticatedUserChanges` streams
* Verify that the SDK is properly initialized
* Check that you're not clearing app data
* Make sure the `DynamicSDK.instance.dynamicWidget` is included in your widget tree

## What's Next

Now that you have authentication set up:

1. **[Social Authentication](/flutter/social-authentication)** - Add social login options
2. **[Session Management](/flutter/session-management)** - Manage authenticated sessions with Streams
3. **[Wallet Creation](/flutter/wallet-creation)** - Learn about automatic wallet creation
4. **[Wallet Operations](/flutter/wallets/general/token-balances)** - Work with user wallets

## Reference

For more details on the authentication API, see:

* [SDK Reference - Authentication](/flutter/sdk-reference/authentication)
