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

# Social sign-in & OAuth redirect handling

> Start a social login and complete the OAuth redirect so users land back in your app authenticated.

## Prerequisites

Before this: 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)).

Enable at least one social provider (Google, Discord, GitHub, etc.) in your [Dynamic dashboard](https://app.dynamic.xyz/dashboard/log-in-user).

## How social login works

Social login uses OAuth, a protocol that delegates authentication to a third-party provider. The flow has three steps:

1. **Redirect out** — your app sends the user to the provider's login page (e.g. Google's sign-in screen).
2. **Provider authenticates** — the user signs in with the provider. The provider redirects back to your app with an authorization code in the URL.
3. **Complete authentication** — your app detects the redirect, extracts the code, and exchanges it for a Dynamic session.

Step 3 happens automatically in a traditional server-rendered app, but in a single-page app you need to handle it yourself. The SDK gives you two functions for this:

* `detectSocialRedirectUrl` — checks whether the current page load is an OAuth callback.
* `completeSocialRedirect` — finishes the authentication and creates the user session.

<Warning>
  If you skip the redirect detection step, users who sign in with a social provider will land back on your app without being authenticated. This is the most common cause of "social login doesn't work" bugs.
</Warning>

## Implementation

<Tabs>
  <Tab title="TypeScript">
    The redirect detection must run on every page load — before you render your main application. If the current URL contains OAuth parameters, complete the authentication before proceeding.

    ```typescript theme={"system"}
    import {
      detectSocialRedirectUrl,
      completeSocialRedirect,
      signInWithSocialRedirect,
      clearSocialRedirectParams,
    } from '@dynamic-labs-sdk/client';

    // --- 1. Detect the redirect on every page load ---

    async function handleOAuthRedirectIfPresent() {
      const currentUrl = new URL(window.location.href);

      const isReturningFromProvider = await detectSocialRedirectUrl({
        url: currentUrl,
      });

      if (!isReturningFromProvider) return null;

      // Complete authentication and get the user
      const user = await completeSocialRedirect({ url: currentUrl });

      // Remove only the OAuth params from the URL so a page refresh
      // doesn't re-trigger the flow, leaving your own params intact
      clearSocialRedirectParams();

      return user;
    }

    // --- 2. Start a social login ---

    async function signInWithGoogle() {
      await signInWithSocialRedirect({
        provider: 'google',
        redirectUrl: window.location.origin,
      });
      // The browser navigates away here — code after this line won't run.
    }

    // --- Putting it together ---

    async function main() {
      // Always check for a redirect first
      const user = await handleOAuthRedirectIfPresent();

      // renderApp and renderSignIn are your implementation.
      if (user) {
        renderApp(user);
        return;
      }

      // No redirect — render the sign-in screen
      renderSignIn();
    }

    main();
    ```

    **Key points:**

    * `redirectUrl` does not need to be a dedicated callback route. It can be your app's root URL — the SDK uses URL parameters, not a specific path, to identify the callback.
    * `detectSocialRedirectUrl` is cheap — it only checks for two URL parameters (`dynamicOauthState` and `dynamicOauthCode`). Call it on every load without performance concerns.
    * `completeSocialRedirect` returns the authenticated `User` object, or `null` if authentication failed.
  </Tab>

  <Tab title="React">
    Compose three hooks:

    * `useDetectSocialRedirectUrl` — a query hook whose `data` is `true` when the current URL is an OAuth callback.
    * `useCompleteSocialRedirect` — a mutation hook that finishes authentication.
    * `clearSocialRedirectParams` — a helper that removes only the OAuth params (`dynamicOauthState`, `dynamicOauthCode`) from the URL, leaving your own params intact.

    A single effect bridges "detection said yes" → "fire completion". Errors surface on each hook's `error` field, so there's no `try`/`catch`.

    ```tsx theme={"system"}
    import { clearSocialRedirectParams } from '@dynamic-labs-sdk/client';
    import type { SocialProvider } from '@dynamic-labs-sdk/client';
    import {
      useCompleteSocialRedirect,
      useDetectSocialRedirectUrl,
      useSignInWithSocialRedirect,
      useUser,
    } from '@dynamic-labs-sdk/react-hooks';
    import { useEffect } from 'react';

    // The callback URL is fixed for this page load — read it once, outside the
    // component, so it's a stable value (no need for useMemo).
    const callbackUrl = new URL(window.location.href);

    function OAuthRedirectHandler({ children }: { children: React.ReactNode }) {
      const { data: isReturningFromProvider, isLoading: isDetecting } =
        useDetectSocialRedirectUrl({ url: callbackUrl });

      const {
        mutate: completeRedirect,
        isPending: isCompleting,
        isIdle,
        error,
      } = useCompleteSocialRedirect();

      useEffect(() => {
        // Fire completion exactly once, and only when a redirect was detected.
        // `isIdle` guards against re-firing the mutation.
        if (!isReturningFromProvider || !isIdle) return;

        completeRedirect(
          { url: callbackUrl },
          {
            // Runs on success and error: strip the OAuth params so a refresh
            // doesn't re-trigger the flow.
            onSettled: () => clearSocialRedirectParams(),
          },
        );
      }, [isReturningFromProvider, isIdle, completeRedirect]);

      if (isDetecting || isCompleting) return <p>Loading...</p>;

      // Surface completion errors wherever you show auth errors.
      if (error) return <p role="alert">Sign-in failed. Please try again.</p>;

      return <>{children}</>;
    }

    // Sign-in button for a social provider
    function SocialSignInButton({ provider }: { provider: SocialProvider }) {
      const { mutate: signIn, isPending } = useSignInWithSocialRedirect();

      return (
        <button
          disabled={isPending}
          onClick={() =>
            signIn({ provider, redirectUrl: window.location.origin })
          }
        >
          Sign in with {provider}
        </button>
      );
    }

    // App root
    function App() {
      const { data: user } = useUser();

      return (
        <OAuthRedirectHandler>
          {user ? <Dashboard /> : <SignInPage />}
        </OAuthRedirectHandler>
      );
    }
    ```

    <Tip>
      Wrap `OAuthRedirectHandler` as high as possible in your component tree — above any route guards or authenticated-only components. The redirect detection must complete before your app decides whether the user is signed in.
    </Tip>
  </Tab>
</Tabs>

## Handling errors

Both `detectSocialRedirectUrl` and `completeSocialRedirect` can throw:

| Error                              | Cause                                                                                            | Resolution                                                                                                      |
| ---------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `MissingSocialUrlParamError`       | The callback URL is missing required OAuth parameters.                                           | Check that `redirectUrl` matches one of your configured redirect URIs in the Dynamic dashboard.                 |
| `InvalidRedirectStorageStateError` | The stored OAuth state doesn't match the callback state — possibly a stale or replayed redirect. | This can happen if the user opens multiple tabs. Clear the URL parameters and prompt the user to sign in again. |
| `InvalidSocialCallbackOriginError` | The callback came from an unexpected origin.                                                     | Verify your redirect URI configuration in the dashboard.                                                        |

```typescript theme={"system"}
import {
  completeSocialRedirect,
  clearSocialRedirectParams,
  MissingSocialUrlParamError,
  InvalidRedirectStorageStateError,
} from '@dynamic-labs-sdk/client';

try {
  await completeSocialRedirect({ url: new URL(window.location.href) });
} catch (error) {
  if (error instanceof MissingSocialUrlParamError) {
    console.error('OAuth parameters missing — check your redirect URI configuration.');
  } else if (error instanceof InvalidRedirectStorageStateError) {
    console.error('OAuth state mismatch — the user may need to sign in again.');
    clearSocialRedirectParams();
  } else {
    throw error;
  }
}
```

## Popup-based social login is not supported on web

On web, the redirect flow is the only supported approach for social login — `signInWithSocialPopUp` throws a `MethodNotImplementedError` if called in a browser.

## Multiple providers

Dynamic supports Google, Apple, Discord, GitHub, Facebook, Microsoft, LinkedIn, X (Twitter), Twitch, TikTok, Steam, Epic Games, and Kraken. Enable the ones you want in the dashboard, then render a button for each:

```tsx theme={"system"}
import type { SocialProvider } from '@dynamic-labs-sdk/client';
// SocialSignInButton is defined in the React example above.

const providers: SocialProvider[] = ['google', 'discord', 'github', 'apple'];

function SocialProviderList() {
  return (
    <div>
      {providers.map((provider) => (
        <SocialSignInButton key={provider} provider={provider} />
      ))}
    </div>
  );
}
```

The redirect detection is provider-agnostic — the same `detectSocialRedirectUrl` + `completeSocialRedirect` call handles any provider.

## See also

* [Email & SMS OTP sign-in](/docs/javascript/building-ui/email-sms-otp-sign-in) — add code-based login alongside social
* [Authenticate with social](/docs/javascript/authentication-methods/social) — social auth reference
* [Social linking](/docs/javascript/authentication-methods/social-linking) — link additional social accounts to an existing user
* [Authentication concepts](/docs/overview/authentication/concepts) — users, sessions, and credentials
