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

# Device Registration

> Verify new devices and manage trusted devices for your users in the Flutter SDK.

Device registration protects your users from account takeovers by requiring verification when they sign in from an unrecognized device. For a general overview of the feature, see [Device Registration](/overview/security/device-registration).

## Prerequisites

* `DynamicSDK` initialized with your environment ID (see [Quickstart](/flutter/quickstart))
* Device registration enabled in your environment's settings in the [Dynamic Dashboard](https://app.dynamic.xyz)
* Deep links configured for your app to handle email verification redirects
* Flutter SDK v1.2.7 or later

## Using our UI

Device registration is handled automatically by the SDK. When a user signs in from an unrecognized device, the SDK displays a security prompt with the user's email, asking them to verify the device. Once the user taps the verification link in their email, the SDK detects the deep link, completes registration, and dismisses the prompt automatically.

No additional code is needed — just enable device registration in your dashboard.

## Using your UI

### Checking if device registration is required

The `DeviceRegistrationModule` provides a reactive `isDeviceRegistrationRequired` property and a `Stream<bool>` that updates when the SDK detects an unrecognized device during authentication.

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

// Check current state
final isRequired = DynamicSDK.instance.deviceRegistration.isDeviceRegistrationRequired;

// Observe changes reactively
DynamicSDK.instance.deviceRegistration.isDeviceRegistrationRequiredChanges
    .listen((isRequired) {
  if (isRequired) {
    // Show your custom device verification UI
  }
});
```

In a widget:

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

class DeviceRegistrationBanner extends StatelessWidget {
  const DeviceRegistrationBanner({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<bool>(
      stream: DynamicSDK.instance.deviceRegistration
          .isDeviceRegistrationRequiredChanges,
      initialData: DynamicSDK
          .instance.deviceRegistration.isDeviceRegistrationRequired,
      builder: (context, snapshot) {
        if (snapshot.data != true) return const SizedBox.shrink();

        return Container(
          padding: const EdgeInsets.all(16),
          color: Colors.amber.shade100,
          child: const Text(
            'Please check your email to verify this device.',
          ),
        );
      },
    );
  }
}
```

## Getting registered devices

Retrieve all trusted devices for the current user. Each device is returned as a `RegisteredDevice` with the following fields:

| Field             | Type      | Description                                     |
| ----------------- | --------- | ----------------------------------------------- |
| `id`              | `String`  | The device registration ID                      |
| `createdAt`       | `String`  | ISO date of when the device was registered      |
| `displayText`     | `String?` | A friendly device name (e.g., "iPhone 15")      |
| `type`            | `String?` | The device type (e.g., `"mobile"`, `"desktop"`) |
| `isCurrentDevice` | `bool?`   | Whether this is the device making the request   |

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

final devices = await DynamicSDK.instance.deviceRegistration
    .getRegisteredDevices();

for (final device in devices) {
  print('${device.displayText ?? "Unknown Device"}');
  print('  ID: ${device.id}');
  print('  Type: ${device.type ?? "N/A"}');
  print('  Current device: ${device.isCurrentDevice ?? false}');
}
```

## Revoking a device

Remove a single trusted device. If the revoked device is the current device, the user will be logged out.

```dart theme={"system"}
await DynamicSDK.instance.deviceRegistration.revokeRegisteredDevice(
  deviceRegistrationId: 'device-registration-id',
);
```

## Revoking all devices

Remove all trusted devices for the current user. This always logs the user out.

```dart theme={"system"}
await DynamicSDK.instance.deviceRegistration.revokeAllRegisteredDevices();
```

## Full example

Here's a complete Flutter screen for managing trusted devices:

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

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

  @override
  State<TrustedDevicesScreen> createState() => _TrustedDevicesScreenState();
}

class _TrustedDevicesScreenState extends State<TrustedDevicesScreen> {
  List<RegisteredDevice>? _devices;
  bool _isLoading = true;
  String? _error;

  @override
  void initState() {
    super.initState();
    _loadDevices();
  }

  Future<void> _loadDevices() async {
    setState(() {
      _isLoading = true;
      _error = null;
    });

    try {
      final devices = await DynamicSDK.instance.deviceRegistration
          .getRegisteredDevices();
      setState(() => _devices = devices);
    } catch (e) {
      setState(() => _error = 'Failed to load devices: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _revokeDevice(String deviceId) async {
    try {
      await DynamicSDK.instance.deviceRegistration.revokeRegisteredDevice(
        deviceRegistrationId: deviceId,
      );
      await _loadDevices();
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to revoke device: $e')),
      );
    }
  }

  Future<void> _revokeAllDevices() async {
    try {
      await DynamicSDK.instance.deviceRegistration.revokeAllRegisteredDevices();
      await _loadDevices();
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to revoke all devices: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Trusted Devices')),
      body: RefreshIndicator(
        onRefresh: _loadDevices,
        child: _buildBody(),
      ),
    );
  }

  Widget _buildBody() {
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (_error != null) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_error!, style: const TextStyle(color: Colors.red)),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _loadDevices,
              child: const Text('Retry'),
            ),
          ],
        ),
      );
    }

    final devices = _devices ?? [];
    if (devices.isEmpty) {
      return const Center(child: Text('No trusted devices registered'));
    }

    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        ...devices.map((device) => _TrustedDeviceCard(
              device: device,
              onRemove: () => _revokeDevice(device.id),
            )),
        if (devices.length > 1) ...[
          const SizedBox(height: 16),
          ElevatedButton(
            onPressed: _revokeAllDevices,
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.red,
              foregroundColor: Colors.white,
            ),
            child: const Text('Remove All Devices'),
          ),
        ],
      ],
    );
  }
}

class _TrustedDeviceCard extends StatelessWidget {
  const _TrustedDeviceCard({
    required this.device,
    required this.onRemove,
  });

  final RegisteredDevice device;
  final VoidCallback onRemove;

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Text(
                  device.displayText ?? 'Unknown Device',
                  style: Theme.of(context).textTheme.titleMedium,
                ),
                const SizedBox(width: 8),
                if (device.isCurrentDevice == true)
                  Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 6,
                      vertical: 2,
                    ),
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.primary,
                      borderRadius: BorderRadius.circular(4),
                    ),
                    child: const Text(
                      'This Device',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 10,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
              ],
            ),
            if (device.type != null) ...[
              const SizedBox(height: 4),
              Text(
                device.type!,
                style: Theme.of(context).textTheme.bodySmall,
              ),
            ],
            const SizedBox(height: 4),
            Text(
              'Registered: ${device.createdAt}',
              style: Theme.of(context).textTheme.bodySmall,
            ),
            const SizedBox(height: 12),
            ElevatedButton(
              onPressed: onRemove,
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.red,
                foregroundColor: Colors.white,
                minimumSize: const Size.fromHeight(40),
              ),
              child: const Text('Remove'),
            ),
          ],
        ),
      ),
    );
  }
}
```

## API reference

### DeviceRegistrationModule

| Member                                           | Description                                                       |
| ------------------------------------------------ | ----------------------------------------------------------------- |
| `isDeviceRegistrationRequired`                   | `bool` — Current state of whether device registration is required |
| `isDeviceRegistrationRequiredChanges`            | `Stream<bool>` — Reactive stream of state changes                 |
| `getRegisteredDevices()`                         | Returns `Future<List<RegisteredDevice>>` of all trusted devices   |
| `revokeRegisteredDevice({deviceRegistrationId})` | Revokes a specific device                                         |
| `revokeAllRegisteredDevices()`                   | Revokes all trusted devices — logs the user out                   |

## Related

* [Device Registration Overview](/overview/security/device-registration) — How device registration works and why it matters
* [Step-up authentication overview](/overview/authentication/step-up-auth) — Concepts, scopes, token lifecycle
* [Security Overview](/overview/security/overview) — Dynamic's security posture
