# Google OAuth on React Native (/wallets/react-native/google-oauth)

> For the complete documentation index, see [llms.txt](/llms.txt)



On the web, [Google OAuth](/wallets/auth/google-oauth) uses a popup. Native apps don't have popups — instead, the SDK opens the system auth browser and returns to your app via a deep link. The `useAuthenticateOAuthWithExpoWebBrowser` hook handles both ends of that round trip on both platforms: the auth session (`ASWebAuthenticationSession` on iOS, Custom Tabs on Android) intercepts the custom-scheme redirect itself, so no domain setup is needed.

<Callout type="info">
  This flow requires an [Expo development build](https://docs.expo.dev/develop/development-builds/expo-go-to-dev-build/) — see the [quickstart](/wallets/react-native/quickstart#5-switch-to-an-expo-development-build).
</Callout>

<div className="fd-steps">
  <div className="fd-step">
    ## Install peer dependencies [#install-peer-dependencies]

    Install the peer deps (already present in the default Expo starter):

    <Tabs items="[&#x22;npm&#x22;,&#x22;yarn&#x22;,&#x22;pnpm&#x22;,&#x22;bun&#x22;]">
      <Tab value="npm">
        ```bash
        npx expo install expo-linking expo-web-browser
        ```
      </Tab>

      <Tab value="yarn">
        ```bash
        yarn expo install expo-linking expo-web-browser
        ```
      </Tab>

      <Tab value="pnpm">
        ```bash
        pnpm expo install expo-linking expo-web-browser
        ```
      </Tab>

      <Tab value="bun">
        ```bash
        bunx expo install expo-linking expo-web-browser
        ```
      </Tab>
    </Tabs>
  </div>

  <div className="fd-step">
    ## Add a deep-link scheme [#add-a-deep-link-scheme]

    Make sure a custom `scheme` is defined in `app.json` (deep link back into the app):

    ```jsonc
    {
      "expo": {
        "scheme": "<your custom scheme>",
      },
    }
    ```
  </div>

  <div className="fd-step">
    ## Add the sign-in component [#add-the-sign-in-component]

    ```tsx
    import { OAUTH_PROVIDERS } from "@zerodev/wallet-react";
    import { useAuthenticateOAuthWithExpoWebBrowser } from "@zerodev/wallet-react/react-native/oauth/with-expo-web-browser";
    import * as Linking from "expo-linking";
    import { Button, Text, View } from "react-native";
    import { useAccount } from "wagmi";

    /** Renders nothing once the wallet is connected. */
    export function GoogleOauthFlow() {
      const { status } = useAccount();
      const auth = useAuthenticateOAuthWithExpoWebBrowser({
        redirectUri: Linking.createURL("oauth-callback"),
      });

      if (status === "connected") return null;

      return (
        <View style={{ gap: 8, padding: 16, borderWidth: 1, borderRadius: 8 }}>
          <Text style={{ fontWeight: "600" }}>Sign in (Google)</Text>
          <Button
            title={auth.isPending ? "Signing in..." : "Continue with Google"}
            disabled={auth.isPending}
            onPress={() => auth.mutate({ provider: OAUTH_PROVIDERS.GOOGLE })}
          />
          {auth.error ? (
            <Text style={{ color: "red" }}>{auth.error.message}</Text>
          ) : null}
        </View>
      );
    }
    ```
  </div>

  <div className="fd-step">
    ## Add the callback route [#add-the-callback-route]

    Add an OAuth callback route, e.g. `app/oauth-callback.tsx`:

    ```tsx
    import { Redirect } from "expo-router";

    /**
     * Catches the OAuth deep-link redirect (`<scheme>://oauth-callback?...`) so
     * Expo Router doesn't show "Unmatched Route". The ZeroDev SDK reads the
     * session_id from the URL via its own `Linking` listener, so this screen
     * just needs to bounce back to /.
     */
    export default function OAuthCallback() {
      return <Redirect href="/" />;
    }
    ```

    This route is for the **native** OAuth flow. If the same Expo app also runs on web, the `.web` variant should use `useAuthenticateOAuth` and return to the current page instead of relying on `app/oauth-callback.tsx`.
  </div>

  <div className="fd-step">
    ## Allowlist the redirect URL [#allowlist-the-redirect-url]

    On the [ZeroDev Dashboard](https://dashboard.zerodev.app/), **allowlist the redirect URL**. After a successful sign-in the browser redirects back into the app.
  </div>
</div>

## Using a verified `https` link as the redirect [#using-a-verified-https-link-as-the-redirect]

Once the [Domain Association](/wallets/react-native/domain-association) is set up on your domain, you can use a verified `https` link (`https://<your domain>/oauth-callback`) instead of the custom scheme.

The two platforms deliver this redirect through different mechanisms:

* **Android** delivers it as a verified **App Link**: the OS hands the redirect to your app as an intent ([Expo guide](https://docs.expo.dev/linking/android-app-links/)). Requires an intent filter (below).
* **iOS 17.4+** resolves it inside the auth session itself: `ASWebAuthenticationSession` observes the redirect to your domain and returns it to the SDK. This is *not* Universal Link routing — iOS only opens Universal Links from a user tap, never from the server-side redirect that ends an OAuth flow — so the AASA doesn't need to claim the `/oauth-callback` path. What it does require is the `webcredentials:<your domain>` entitlement (the same one passkeys use) from the [Domain Association](/wallets/react-native/domain-association#2-ios-add-the-associated-domains-entitlement) setup.

### Android: add an intent filter [#android-add-an-intent-filter]

Add an `intentFilter` for `/oauth-callback` in `app.json`:

```jsonc
{
  "expo": {
    "android": {
      "intentFilters": [
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "https",
              "host": "<your domain>",
              "pathPrefix": "/oauth-callback",
            },
          ],
          "category": ["BROWSABLE", "DEFAULT"],
        },
      ],
    },
  },
}
```

> After `app.json` changes, regenerate the native project: `npx expo prebuild --clean`, then rebuild. Intent filters land in `AndroidManifest.xml` and App Link verification happens at install time.

### iOS: check the requirements [#ios-check-the-requirements]

No OAuth-specific setup beyond the [Domain Association](/wallets/react-native/domain-association) — but three things must hold:

* The build carries the `webcredentials:<your domain>` entitlement (paid Apple Developer team). Entitlement changes only land after `npx expo prebuild --clean` and a rebuild.
* The hosted `apple-app-site-association` lists your `<team id>.<bundle id>` under `webcredentials`. If the entitlement or AASA is missing or stale, the flow strands the user on the redirect page inside the auth sheet — during development, keep the `?mode=developer` flag on the entitlement so the device fetches the AASA straight from your origin instead of Apple's CDN.
* The device runs **iOS 17.4 or later**. Older versions can't observe `https` callbacks in the auth session — fall back to the custom scheme there (snippet below).

### Pass the `https` redirect [#pass-the-https-redirect]

```tsx
import { RP_ID } from "@/wagmi.config";
// ...
const auth = useAuthenticateOAuthWithExpoWebBrowser({
  redirectUri: `https://${RP_ID}/oauth-callback`,
});
```

The hook detects the `https` redirect and configures the auth session accordingly — no extra option needed on either platform.

To keep older iOS versions working, pick the redirect per platform and version:

```tsx
import * as Linking from "expo-linking";
import { Platform } from "react-native";

import { RP_ID } from "@/wagmi.config";

// iOS can only observe https auth-session callbacks on 17.4+.
const supportsHttpsRedirect =
  Platform.OS !== "ios" || Number.parseFloat(String(Platform.Version)) >= 17.4;

const auth = useAuthenticateOAuthWithExpoWebBrowser({
  redirectUri: supportsHttpsRedirect
    ? `https://${RP_ID}/oauth-callback`
    : Linking.createURL("oauth-callback"),
});
```

Since these are new redirect URLs, remember to [allowlist them on the Dashboard](#5-allowlist-the-redirect-url) as well.
