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

# Authentication Functions

## sdk.auth.email

### sendOTP

Send a one-time password to a user's email address.

```dart theme={"system"}
Future<void> sendOTP(String email)
```

#### Parameters

* **email** (String): User's email address

#### Example

```dart theme={"system"}
try {
  await sdk.auth.email.sendOTP('user@example.com');
  // Show OTP input UI
} catch (e) {
  print('Failed to send OTP: $e');
}
```

### verifyOTP

Verify an email OTP code and authenticate the user.

```dart theme={"system"}
Future<void> verifyOTP(String token)
```

#### Parameters

* **token** (String): OTP code entered by user

#### Example

```dart theme={"system"}
try {
  await sdk.auth.email.verifyOTP('123456');
  // User is now authenticated
} catch (e) {
  print('Invalid OTP: $e');
}
```

### resendOTP

Resend the email OTP.

```dart theme={"system"}
Future<void> resendOTP()
```

#### Example

```dart theme={"system"}
try {
  await sdk.auth.email.resendOTP();
} catch (e) {
  print('Failed to resend OTP: $e');
}
```

## sdk.auth.sms

### sendOTP

Send a one-time password to a user's phone number via SMS.

```dart theme={"system"}
Future<void> sendOTP(PhoneData phoneData)
```

#### Parameters

* **phoneData** (PhoneData): Phone data containing dial code, ISO code, and phone number

#### Example

```dart theme={"system"}
try {
  final phoneData = PhoneData(
    dialCode: '+1',
    iso2: 'US',
    phone: '5551234567',
  );
  await sdk.auth.sms.sendOTP(phoneData);
  // Show OTP input UI
} catch (e) {
  print('Failed to send SMS: $e');
}
```

### verifyOTP

Verify an SMS OTP code and authenticate the user.

```dart theme={"system"}
Future<void> verifyOTP(String token)
```

#### Parameters

* **token** (String): OTP code entered by user

#### Example

```dart theme={"system"}
try {
  await sdk.auth.sms.verifyOTP('123456');
  // User is now authenticated
} catch (e) {
  print('Invalid OTP: $e');
}
```

### resendOTP

Resend the SMS OTP.

```dart theme={"system"}
Future<void> resendOTP()
```

#### Example

```dart theme={"system"}
try {
  await sdk.auth.sms.resendOTP();
} catch (e) {
  print('Failed to resend SMS: $e');
}
```

## sdk.auth.social

### connect

Authenticate with a social provider (Google, Apple, Farcaster).

```dart theme={"system"}
Future<void> connect(SocialProvider provider)
```

#### Parameters

* **provider** (SocialProvider): The social provider:
  * `SocialProvider.google`
  * `SocialProvider.apple`
  * `SocialProvider.farcaster`

#### Examples

```dart theme={"system"}
try {
  // Google
  await sdk.auth.social.connect(SocialProvider.google);
  // Apple
  await sdk.auth.social.connect(SocialProvider.apple);
  // Farcaster
  await sdk.auth.social.connect(SocialProvider.farcaster);
} catch (e) {
  print('Sign-in failed: $e');
}
```

## sdk.auth.passkey

### signIn

Authenticate with a passkey using the device's biometric authentication.

```dart theme={"system"}
Future<VerifyResponse> signIn({
  String? relatedOriginRpId,
})
```

#### Parameters

* `relatedOriginRpId` (optional): The Relying Party ID (RP ID) to use when signing in from a related domain, as a bare hostname such as `old.example.com`. Pass it so users can keep signing in with passkeys they registered on a previous domain instead of registering new ones. The related domain must host `https://<related-domain>/.well-known/webauthn` with your current domain in its `origins` list (for example `{ "origins": ["https://app.example.com"] }`), and it must also be covered by your app's associated domain configuration. See [Related Origin Requests](https://passkeys.dev/docs/advanced/related-origins/).

#### Example

```dart theme={"system"}
try {
  await sdk.auth.passkey.signIn();
  // User is authenticated
} catch (e) {
  print('Passkey sign-in failed: $e');
}
```

#### Example: reuse a passkey from a previous domain

```dart theme={"system"}
try {
  await sdk.auth.passkey.signIn(relatedOriginRpId: 'old.example.com');
  // User is authenticated with their existing passkey
} catch (e) {
  print('Passkey sign-in failed: $e');
}
```

## sdk.auth.externalAuth

### signInWithExternalJwt

Sign in using an external JWT token.

```dart theme={"system"}
Future<void> signInWithExternalJwt({required String jwt})
```

#### Parameters

* **jwt** (String): The external JWT token

#### Example

```dart theme={"system"}
try {
  await sdk.auth.externalAuth.signInWithExternalJwt(
    jwt: 'your-jwt-token',
  );
} catch (e) {
  print('JWT sign-in failed: $e');
}
```

## Authentication State

### authenticatedUser

Get the current authenticated user (synchronous).

```dart theme={"system"}
UserProfile? get authenticatedUser
```

#### Example

```dart theme={"system"}
final user = sdk.auth.authenticatedUser;
if (user != null) {
  print('User: ${user.email}');
}
```

### authenticatedUserChanges

Observe authentication state changes (reactive stream).

```dart theme={"system"}
Stream<UserProfile?> get authenticatedUserChanges
```

#### Example

```dart theme={"system"}
sdk.auth.authenticatedUserChanges.listen((user) {
  // Handle user state change
  setState(() {
    isAuthenticated = user != null;
  });
});
```

#### StreamBuilder Example

```dart theme={"system"}
StreamBuilder<UserProfile?>(
  stream: sdk.auth.authenticatedUserChanges,
  initialData: sdk.auth.authenticatedUser,
  builder: (context, snapshot) {
    final user = snapshot.data;
    if (user != null) {
      return Text('Logged in as ${user.email}');
    } else {
      return Text('Not logged in');
    }
  },
)
```

### token

Get the current authentication token (synchronous).

```dart theme={"system"}
String? get token
```

#### Example

```dart theme={"system"}
final token = sdk.auth.token;
if (token != null) {
  print('Token: ${token.substring(0, 20)}...');
}
```

### tokenChanges

Observe authentication token changes (reactive stream).

```dart theme={"system"}
Stream<String?> get tokenChanges
```

#### Example

```dart theme={"system"}
sdk.auth.tokenChanges.listen((token) {
  if (token != null && token.isNotEmpty) {
    // Token received, navigate to home
    Navigator.pushReplacementNamed(context, '/home');
  }
});
```

#### StreamBuilder Example

```dart theme={"system"}
StreamBuilder<String?>(
  stream: sdk.auth.tokenChanges,
  initialData: sdk.auth.token,
  builder: (context, snapshot) {
    final hasToken = snapshot.data != null && snapshot.data!.isNotEmpty;
    return Text(hasToken ? 'Authenticated' : 'Not authenticated');
  },
)
```

### minAuthToken

Get the current minified authentication token. This is a compact JWT suitable for bandwidth-constrained environments.

```dart theme={"system"}
String? get minAuthToken
```

#### Example

```dart theme={"system"}
final minToken = sdk.auth.minAuthToken;
if (minToken != null) {
  print('Minified token: ${minToken.substring(0, 20)}...');
}
```

### minAuthTokenChanges

Observe minified authentication token changes (reactive stream).

```dart theme={"system"}
Stream<String?> get minAuthTokenChanges
```

#### Example

```dart theme={"system"}
sdk.auth.minAuthTokenChanges.listen((minToken) {
  if (minToken != null && minToken.isNotEmpty) {
    print('Minified token updated');
  }
});
```

#### StreamBuilder Example

```dart theme={"system"}
StreamBuilder<String?>(
  stream: sdk.auth.minAuthTokenChanges,
  initialData: sdk.auth.minAuthToken,
  builder: (context, snapshot) {
    final hasMinToken = snapshot.data != null && snapshot.data!.isNotEmpty;
    return Text(hasMinToken ? 'Min token available' : 'No min token');
  },
)
```

## Refreshing the user

### refreshUser

Re-fetch the authenticated user from Dynamic and republish it. The SDK receives the usual `authenticatedUserChanged` and `userWalletsChanged` updates, so `authenticatedUser`, `authenticatedUserChanges`, and `wallets.userWallets` reflect the fresh state.

Call it after an operation that changes the user server-side without a new sign-in, such as importing a private key into an embedded wallet. The wallet exists in Dynamic as soon as the import resolves, but the host's wallet list is a snapshot taken at sign-in until it is refreshed.

```dart theme={"system"}
Future<UserProfile?> refreshUser()
```

Returns the fresh profile, or `null` when the webview answers without one. Throws if no user is currently authenticated.

#### Example

```dart theme={"system"}
try {
  final user = await sdk.auth.refreshUser();
  print('Refreshed user: ${user?.userId}');
} catch (e) {
  print('Refresh failed: $e');
}
```

## Logout

### logout

Log out the current user.

```dart theme={"system"}
Future<void> logout()
```

#### Example

```dart theme={"system"}
try {
  await sdk.auth.logout();
} catch (e) {
  print('Logout failed: $e');
}
```

## Complete Authentication Flow Example

### Email Authentication

```dart theme={"system"}
class EmailAuthScreen extends StatefulWidget {
  @override
  _EmailAuthScreenState createState() => _EmailAuthScreenState();
}

class _EmailAuthScreenState extends State<EmailAuthScreen> {
  final sdk = DynamicSDK.instance;
  final emailController = TextEditingController();
  final otpController = TextEditingController();
  bool otpSent = false;
  bool isLoading = false;

  Future<void> handleSendOTP() async {
    setState(() => isLoading = true);

    try {
      await sdk.auth.email.sendOTP(emailController.text);
      setState(() {
        otpSent = true;
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to send OTP: $e')),
      );
    }
  }

  Future<void> handleVerifyOTP() async {
    setState(() => isLoading = true);

    try {
      await sdk.auth.email.verifyOTP(otpController.text);
      // User is now authenticated, navigation handled by auth state listener
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Invalid OTP: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Email Authentication')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            if (!otpSent) ...[
              TextField(
                controller: emailController,
                decoration: InputDecoration(
                  labelText: 'Email',
                  hintText: 'user@example.com',
                ),
                keyboardType: TextInputType.emailAddress,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: isLoading ? null : handleSendOTP,
                child: isLoading
                    ? CircularProgressIndicator()
                    : Text('Send OTP'),
              ),
            ] else ...[
              TextField(
                controller: otpController,
                decoration: InputDecoration(
                  labelText: 'Enter OTP',
                  hintText: '123456',
                ),
                keyboardType: TextInputType.number,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: isLoading ? null : handleVerifyOTP,
                child: isLoading
                    ? CircularProgressIndicator()
                    : Text('Verify OTP'),
              ),
              TextButton(
                onPressed: () async {
                  try {
                    await sdk.auth.email.resendOTP();
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('OTP resent')),
                    );
                  } catch (e) {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('Failed to resend: $e')),
                    );
                  }
                },
                child: Text('Resend OTP'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    emailController.dispose();
    otpController.dispose();
    super.dispose();
  }
}
```

### SMS Authentication

```dart theme={"system"}
class SmsAuthScreen extends StatefulWidget {
  @override
  _SmsAuthScreenState createState() => _SmsAuthScreenState();
}

class _SmsAuthScreenState extends State<SmsAuthScreen> {
  final sdk = DynamicSDK.instance;
  final phoneController = TextEditingController();
  final otpController = TextEditingController();
  bool otpSent = false;
  bool isLoading = false;

  Future<void> handleSendOTP() async {
    setState(() => isLoading = true);

    try {
      final phoneData = PhoneData(
        dialCode: '+1',
        iso2: 'US',
        phone: phoneController.text,
      );
      await sdk.auth.sms.sendOTP(phoneData);
      setState(() {
        otpSent = true;
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to send SMS: $e')),
      );
    }
  }

  Future<void> handleVerifyOTP() async {
    setState(() => isLoading = true);

    try {
      await sdk.auth.sms.verifyOTP(otpController.text);
      // User is now authenticated
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Invalid OTP: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('SMS Authentication')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            if (!otpSent) ...[
              TextField(
                controller: phoneController,
                decoration: InputDecoration(
                  labelText: 'Phone Number',
                  hintText: '5551234567',
                ),
                keyboardType: TextInputType.phone,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: isLoading ? null : handleSendOTP,
                child: isLoading
                    ? CircularProgressIndicator()
                    : Text('Send OTP'),
              ),
            ] else ...[
              TextField(
                controller: otpController,
                decoration: InputDecoration(
                  labelText: 'Enter OTP',
                  hintText: '123456',
                ),
                keyboardType: TextInputType.number,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: isLoading ? null : handleVerifyOTP,
                child: isLoading
                    ? CircularProgressIndicator()
                    : Text('Verify OTP'),
              ),
              TextButton(
                onPressed: () async {
                  try {
                    await sdk.auth.sms.resendOTP();
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('OTP resent')),
                    );
                  } catch (e) {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('Failed to resend: $e')),
                    );
                  }
                },
                child: Text('Resend OTP'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    phoneController.dispose();
    otpController.dispose();
    super.dispose();
  }
}
```

### Social Authentication

```dart theme={"system"}
class SocialAuthButtons extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton.icon(
          onPressed: () async {
            try {
              await sdk.auth.social.connect(SocialProvider.google);
            } catch (e) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Google sign-in failed: $e')),
              );
            }
          },
          icon: Icon(Icons.g_mobiledata),
          label: Text('Continue with Google'),
        ),
        SizedBox(height: 12),
        ElevatedButton.icon(
          onPressed: () async {
            try {
              await sdk.auth.social.connect(SocialProvider.apple);
            } catch (e) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Apple sign-in failed: $e')),
              );
            }
          },
          icon: Icon(Icons.apple),
          label: Text('Continue with Apple'),
        ),
        SizedBox(height: 12),
        ElevatedButton.icon(
          onPressed: () async {
            try {
              await sdk.auth.social.connect(SocialProvider.farcaster);
            } catch (e) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Farcaster sign-in failed: $e')),
              );
            }
          },
          icon: Icon(Icons.telegram),
          label: Text('Continue with Farcaster'),
        ),
      ],
    );
  }
}
```

### Passkey Authentication

```dart theme={"system"}
class PasskeyAuthButton extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton.icon(
      onPressed: () async {
        try {
          await sdk.auth.passkey.signIn();
          // User is authenticated
        } catch (e) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('Passkey sign-in failed: $e')),
          );
        }
      },
      icon: Icon(Icons.fingerprint),
      label: Text('Sign in with Passkey'),
    );
  }
}
```


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