Google OAuth on React Native
On the web, 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.
1. Install peer dependencies
Install the peer deps (already present in the default Expo starter):
npx expo install expo-linking expo-web-browser2. Add a deep-link scheme
Make sure a custom scheme is defined in app.json (deep link back into the app):
{
"expo": {
"scheme": "<your custom scheme>",
},
}3. Add the sign-in component
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>
);
}See the useAuthenticateOAuthWithExpoWebBrowser reference for the full API.
4. Add the callback route
Add an OAuth callback route, e.g. app/oauth-callback.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.
5. Allowlist the redirect URL
On the ZeroDev Dashboard, allowlist the redirect URL. After a successful sign-in the browser redirects back into the app.
Using a verified https link as the redirect
Once the 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). Requires an intent filter (below).
- iOS 17.4+ resolves it inside the auth session itself:
ASWebAuthenticationSessionobserves 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-callbackpath. What it does require is thewebcredentials:<your domain>entitlement (the same one passkeys use) from the Domain Association setup.
Android: add an intent filter
Add an intentFilter for /oauth-callback in app.json:
{
"expo": {
"android": {
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "<your domain>",
"pathPrefix": "/oauth-callback",
},
],
"category": ["BROWSABLE", "DEFAULT"],
},
],
},
},
}After
app.jsonchanges, regenerate the native project:npx expo prebuild --clean, then rebuild. Intent filters land inAndroidManifest.xmland App Link verification happens at install time.
iOS: check the requirements
No OAuth-specific setup beyond the Domain Association — but three things must hold:
- The build carries the
webcredentials:<your domain>entitlement (paid Apple Developer team). Entitlement changes only land afternpx expo prebuild --cleanand a rebuild. - The hosted
apple-app-site-associationlists your<team id>.<bundle id>underwebcredentials. 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=developerflag 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
httpscallbacks in the auth session — fall back to the custom scheme there (snippet below).
Pass the https redirect
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:
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 as well.