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

# Non-wallet authentication

> Migrate email, SMS, social, passkey, and external-JWT sign-in from the React SDK to the JavaScript SDK.

Non-wallet auth methods carry over, but each becomes an explicit call (or pair of calls) instead of a context that manages the flow for you. This page maps each React SDK API to its JavaScript SDK equivalent and links to the full implementation guide for each.

## Email & SMS OTP

The React SDK's `useConnectWithOtp` bundled sending and verifying. In the JavaScript SDK these are two explicit steps: send the code, then verify it. Sending returns an `otpVerification` handle that you pass to `verifyOTP`.

| React SDK                                   | JavaScript SDK                         |
| ------------------------------------------- | -------------------------------------- |
| `useConnectWithOtp().connectWithEmail`      | `sendEmailOTP()` / `useSendEmailOTP()` |
| `useConnectWithOtp().connectWithSms`        | `sendSmsOTP()` / `useSendSmsOTP()`     |
| `useConnectWithOtp().verifyOneTimePassword` | `verifyOTP()` / `useVerifyOTP()`       |

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

    const otpVerification = await sendEmailOTP({ email });
    const user = await verifyOTP({ otpVerification, verificationToken: codeFromUser });
    ```
  </Tab>

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

    const { mutateAsync: sendEmailOTP } = useSendEmailOTP();
    const { mutateAsync: verifyOTP } = useVerifyOTP();

    const otpVerification = await sendEmailOTP({ email });
    const user = await verifyOTP({ otpVerification, verificationToken: codeFromUser });
    ```
  </Tab>
</Tabs>

For the full two-step UI (resend, error handling, SMS variant), see [Email & SMS OTP sign-in](/docs/javascript/building-ui/email-sms-otp-sign-in).

## Social sign-in & OAuth redirect

<Warning>
  **This is the change most likely to break a migration.** In the React SDK, OAuth redirect detection was automatic — the SDK inspected the URL on mount and completed sign-in for you. In the JavaScript SDK, **you must complete the redirect yourself** on every app load, or returning users never finish signing in.
</Warning>

Start sign-in with `signInWithSocialRedirect`, then on the page the provider returns to, detect and complete the callback.

| React SDK                                     | JavaScript SDK                                                                                                                    |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `useSocialAccounts().signInWithSocialAccount` | `signInWithSocialRedirect()` / `useSignInWithSocialRedirect()`                                                                    |
| automatic redirect detection                  | `detectSocialRedirectUrl()` + `completeSocialRedirect()` on load / `useDetectSocialRedirectUrl()` + `useCompleteSocialRedirect()` |

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

    const url = new URL(window.location.href);

    if (detectSocialRedirectUrl({ url })) {
      await completeSocialRedirect({ url });
      clearSocialRedirectParams();
    }
    ```
  </Tab>

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

    export function CompleteSocialSignIn() {
      const url = new URL(window.location.href);
      const { data: isReturningFromProvider } = useDetectSocialRedirectUrl({ url });
      const { mutateAsync: completeSocialRedirect } = useCompleteSocialRedirect();

      useEffect(() => {
        if (isReturningFromProvider) {
          void completeSocialRedirect({ url }).then(() => {
            clearSocialRedirectParams();
          });
        }
      }, [isReturningFromProvider]);

      return null;
    }
    ```
  </Tab>
</Tabs>

For sign-in buttons and the complete redirect handler, see [Social sign-in & OAuth redirect handling](/docs/javascript/building-ui/social-oauth-redirect-handling).

## Passkeys

Passkey sign-in keeps the same hook name. Registration is a separate explicit call.

| React SDK                            | JavaScript SDK                                   |
| ------------------------------------ | ------------------------------------------------ |
| `useSignInWithPasskey()`             | `signInWithPasskey()` / `useSignInWithPasskey()` |
| passkey registration (via sync flow) | `registerPasskey()` / `useRegisterPasskey()`     |

<Warning>
  `registerPasskey()` requests a `Credentiallink` elevated access token the same way `registerTotpMfaDevice()` does (see Registering or removing a device needs an elevated token) — expect it to need a `checkStepUpAuth({ scope: TokenScope.Credentiallink })` check beforehand if the user doesn't already hold one.
</Warning>

For sign-in and registration UI, see [Passkey sign-in & registration](/docs/javascript/building-ui/passkey-sign-in-registration).

## External JWT

If you authenticate the user elsewhere and hand Dynamic a JWT, the hook is renamed but the shape is the same.

| React SDK           | JavaScript SDK                                           |
| ------------------- | -------------------------------------------------------- |
| `useExternalAuth()` | `signInWithExternalJwt()` / `useSignInWithExternalJwt()` |

```typescript theme={"system"}
import { signInWithExternalJwt } from '@dynamic-labs-sdk/client';

const user = await signInWithExternalJwt({ externalJwt });
```

This flow is essentially headless — you already authenticated the user, so there's no dedicated Building UI page.

## Social account linking

Managing already-linked social accounts moves from one hook to focused functions.

| React SDK                                 | JavaScript SDK                                           |
| ----------------------------------------- | -------------------------------------------------------- |
| `useSocialAccounts()` (linked accounts)   | `getUserSocialAccounts()` / `useGetUserSocialAccounts()` |
| `useSocialAccounts().unlinkSocialAccount` | `unlinkSocialAccount()` / `useUnlinkSocialAccount()`     |

<Warning>
  `unlinkSocialAccount()` requires a `Credentialunlink` elevated access token — there's no legacy fallback, so it 403s (`Elevated access token required`) if you call it directly. Check with `checkStepUpAuth({ scope: TokenScope.Credentialunlink })` first and, if required, re-verify the user with an existing credential (email OTP, TOTP, passkey, or a connected wallet), passing `requestedScopes: [TokenScope.Credentialunlink]` on that call. See Registering or removing a device needs an elevated token for the full pattern — it's the same mechanism.
</Warning>

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import {
      checkStepUpAuth,
      sendEmailOTP,
      verifyOTP,
      unlinkSocialAccount,
      TokenScope,
    } from '@dynamic-labs-sdk/client';

    async function unlink(verifiedCredentialId: string) {
      const { isRequired } = await checkStepUpAuth({
        scope: TokenScope.Credentialunlink,
      });

      if (isRequired) {
        // Re-verify with an existing credential — email OTP shown here.
        const otpVerification = await sendEmailOTP({ email });
        const code = await promptForCode(); // your implementation
        await verifyOTP({
          otpVerification,
          verificationToken: code,
          requestedScopes: [TokenScope.Credentialunlink],
        });
      }

      // The elevated token from above is picked up automatically.
      await unlinkSocialAccount({ verifiedCredentialId });
    }
    ```
  </Tab>
</Tabs>

Linked-account management typically lives in your profile UI — see [User profile & settings](/docs/javascript/building-ui/user-profile-settings).

## After any method

Signing in no longer runs onboarding, MFA, device registration, or wallet creation for you. Run those steps yourself once auth resolves — see [Post-login user setup](/docs/javascript/building-ui/post-auth-user-setup).

## See also

* [Email & SMS OTP sign-in](/docs/javascript/building-ui/email-sms-otp-sign-in)
* [Social sign-in & OAuth redirect handling](/docs/javascript/building-ui/social-oauth-redirect-handling)
* [Passkey sign-in & registration](/docs/javascript/building-ui/passkey-sign-in-registration)
* Wallet authentication
