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

# Device registration

> Approve a new device when it's required at sign-in, complete the approval redirect, and manage a user's registered devices.

## Prerequisites

Before this page: create and initialize a Dynamic client (see [Creating a Dynamic client](/docs/javascript/reference/client/create-dynamic-client), [Initializing the Dynamic client](/docs/javascript/reference/client/initialize-dynamic-client)), and have a signed-in user (see [Email & SMS OTP sign-in](/docs/javascript/building-ui/email-sms-otp-sign-in)).

## What you'll build

When your project requires device registration, a user signing in on an unrecognized device must approve it before continuing. You'll build:

1. **Detection** — notice when the current device needs approval.
2. **Completion** — finish approval when the user returns from the approval link.
3. **Management** — list registered devices and let the user revoke them.

## Detecting the requirement

After sign-in, `isDeviceRegistrationRequired` tells you whether the current device still needs approval. You don't send the approval email — the backend emails the link automatically when a user signs in on an unrecognized device. `isDeviceRegistrationRequired` just reflects that the current device is still pending, so use it to render a "check your email" message.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import {
      getDefaultClient,
      isDeviceRegistrationRequired,
    } from '@dynamic-labs-sdk/client';

    // Returns the message to show, or an empty string when nothing is pending.
    function deviceApprovalMessage(): string {
      const { user } = getDefaultClient();
      const needsApproval = Boolean(user && isDeviceRegistrationRequired(user));
      return needsApproval ? 'Check your email to approve this device.' : '';
    }
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useIsDeviceRegistrationRequired } from '@dynamic-labs-sdk/react-hooks';

    function DeviceApprovalGate() {
      const { data: required } = useIsDeviceRegistrationRequired();
      if (!required) return null;
      return <p>Check your email to approve this device.</p>;
    }
    ```
  </Tab>
</Tabs>

## Completing approval on redirect

The approval link brings the user back to your app with a one-time token in the URL. Detect it, extract the token, and complete registration.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import {
      detectDeviceRegistrationRedirect,
      getDeviceRegistrationTokenFromUrl,
      completeDeviceRegistration,
      clearDeviceRegistrationRedirectParams,
    } from '@dynamic-labs-sdk/client';

    async function handleDeviceRedirect() {
      const url = window.location.href;
      if (!detectDeviceRegistrationRedirect({ url })) return;

      const deviceToken = getDeviceRegistrationTokenFromUrl({ url });

      try {
        await completeDeviceRegistration({ deviceToken });
        // Remove the one-time token so a refresh doesn't re-trigger registration.
        clearDeviceRegistrationRedirectParams();
        // showApproved is your implementation.
        showApproved();
      } catch (error) {
        // handleDeviceError is your implementation — see the error table below.
        handleDeviceError(error);
      }
    }
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useEffect } from 'react';
    import {
      detectDeviceRegistrationRedirect,
      getDeviceRegistrationTokenFromUrl,
      clearDeviceRegistrationRedirectParams,
    } from '@dynamic-labs-sdk/client';
    import { useCompleteDeviceRegistration } from '@dynamic-labs-sdk/react-hooks';

    function useDeviceRedirect() {
      const { mutate: complete, isIdle } = useCompleteDeviceRegistration();

      useEffect(() => {
        const url = window.location.href;
        if (!detectDeviceRegistrationRedirect({ url }) || !isIdle) return;

        const deviceToken = getDeviceRegistrationTokenFromUrl({ url });
        complete(
          { deviceToken },
          // Remove the one-time token so a refresh doesn't re-trigger registration.
          { onSettled: () => clearDeviceRegistrationRedirectParams() },
        );
      }, [complete, isIdle]);
    }
    ```
  </Tab>
</Tabs>

## Managing registered devices

List the user's devices and let them revoke any — or all — from a settings screen.

<Warning>
  Revoking the **current** device — or **all** devices — signs the user out immediately. `revokeAllRegisteredDevices` always logs out; `revokeRegisteredDevice` logs out when the device being revoked is the current one. The SDK fires the `logout` event with reason `device-revoked` or `all-devices-revoked`; listen for it and send the user to your login screen so they aren't left on a dead session.
</Warning>

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import {
      getRegisteredDevices,
      revokeRegisteredDevice,
      revokeAllRegisteredDevices,
      onEvent,
    } from '@dynamic-labs-sdk/client';

    async function listDevices(list: HTMLUListElement) {
      const devices = await getRegisteredDevices();
      list.replaceChildren();
      for (const device of devices) {
        const item = document.createElement('li');
        const label = device.isCurrentDevice ? ' (this device)' : '';
        item.textContent = `${device.userAgent ?? 'Unknown device'}${label}`;
        list.append(item);
      }
    }

    async function revoke(deviceRegistrationId: string) {
      await revokeRegisteredDevice({ deviceRegistrationId });
    }

    // setStatus is your implementation — e.g. a toast or banner.
    async function revokeAll(setStatus: (message: string) => void) {
      setStatus('Signing you out of every device…');
      await revokeAllRegisteredDevices();
      setStatus('All devices signed out. Redirecting you to sign in…');
    }

    // Revoking the current device (or all devices) logs the user out.
    // Send them to the login page when it happens.
    onEvent({
      event: 'logout',
      listener: (metadata) => {
        const revokedCurrentSession =
          metadata?.reason === 'device-revoked' ||
          metadata?.reason === 'all-devices-revoked';

        if (revokedCurrentSession) {
          window.location.href = '/login';
        }
      },
    });
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import {
      useGetRegisteredDevices,
      useRevokeRegisteredDevice,
      useOnEvent,
    } from '@dynamic-labs-sdk/react-hooks';

    function RegisteredDevices() {
      const { data: devices } = useGetRegisteredDevices();
      const { mutateAsync: revoke } = useRevokeRegisteredDevice();

      // Revoking the current device (or all devices) logs the user out.
      useOnEvent({
        event: 'logout',
        listener: (metadata) => {
          const revokedCurrentSession =
            metadata?.reason === 'device-revoked' ||
            metadata?.reason === 'all-devices-revoked';

          if (revokedCurrentSession) {
            window.location.href = '/login';
          }
        },
      });

      return (
        <ul>
          {devices?.map((device) => (
            <li key={device.id}>
              {device.userAgent ?? 'Unknown device'}
              {device.isCurrentDevice ? ' (this device)' : null}
              <button
                type="button"
                onClick={() => revoke({ deviceRegistrationId: device.id })}
              >
                Revoke
              </button>
            </li>
          ))}
        </ul>
      );
    }
    ```
  </Tab>
</Tabs>

## Handling errors

| Error                                     | Cause                                 | What to do                                                         |
| ----------------------------------------- | ------------------------------------- | ------------------------------------------------------------------ |
| `InvalidDeviceRegistrationRedirectError`  | The URL has no valid device token     | Don't attempt completion — the link is malformed or expired.       |
| `DeviceRegistrationIdentityMismatchError` | The approval was for a different user | Ask the user to sign in with the account that requested approval.  |
| Approval never completes                  | The user hasn't opened the link       | Keep the "check your email to approve this device" prompt visible. |

## See also

* [Email & SMS OTP sign-in](/docs/javascript/building-ui/email-sms-otp-sign-in) — the sign-in that can trigger device approval
* [Post-login user setup](/docs/javascript/building-ui/post-auth-user-setup) — detect device registration alongside other post-login steps
* [MFA enrollment & management](/docs/javascript/building-ui/mfa-enrollment-management) — another layer of account security
