# Native vs. Browser OAuth in Expo

Native [OAuth](https://clerk.com/glossary.md#authorization-code-flow) uses platform SDKs like Google's [Credential Manager](https://clerk.com/glossary.md#credential-management-api) and Apple's `ASAuthorization` to authenticate users without leaving the app, while browser OAuth redirects users to a system browser via `expo-auth-session`. Native OAuth delivers better conversion rates and supports [passkeys](https://clerk.com/glossary.md#passkeys), but requires per-provider native configuration. Browser OAuth is simpler to set up but suffers from redirect failures — one [Expo](https://clerk.com/glossary.md#expo) team with 50,000+ users reported roughly 30% of Android sign-in attempts returned a `DISMISS` result ([Expo GitHub Issue #23781, 2024](https://github.com/expo/expo/issues/23781)).

Industry surveys suggest around 77% of users prefer signing in with existing accounts ([LoginRadius Infographic, 2024](https://www.loginradius.com/blog/identity/social-login-infographic)), and desktop converts at roughly 4.8% versus 2.9% on mobile, with auth friction contributing to that gap ([Corbado, 2026](https://www.corbado.com/blog/logins-impact-checkout-conversion)). This guide walks through both architectures with working code, compares the tradeoffs, and covers the platform requirements that shape your decision.

## How browser-based OAuth works in Expo

The browser OAuth flow follows a predictable sequence:

1. User taps "Sign in with Google" in your app.
2. Your app opens a system browser tab (via `expo-web-browser`).
3. The browser loads the provider's consent screen.
4. User authenticates and grants permissions.
5. The provider redirects to your app's deep link (e.g., `myapp://dashboard`).
6. `expo-web-browser` catches the redirect and passes data back.
7. Your app creates a session from the returned tokens.

Here's what that looks like with Clerk's `useSSO` hook:

```tsx
// components/BrowserGoogleSignIn.tsx
import React from 'react'
import * as WebBrowser from 'expo-web-browser'
import * as Linking from 'expo-linking'
import { useSSO } from '@clerk/expo'
import { useRouter } from 'expo-router'
import { Pressable, StyleSheet, Text, View } from 'react-native'

const useWarmUpBrowser = () => {
  React.useEffect(() => {
    void WebBrowser.warmUpAsync()
    return () => {
      void WebBrowser.coolDownAsync()
    }
  }, [])
}

WebBrowser.maybeCompleteAuthSession()

export function BrowserGoogleSignIn() {
  useWarmUpBrowser()
  const { startSSOFlow } = useSSO()
  const router = useRouter()

  const handleSignIn = async () => {
    try {
      const { createdSessionId, setActive } = await startSSOFlow({
        strategy: 'oauth_google',
        redirectUrl: Linking.createURL('/dashboard', { scheme: 'myapp' }),
      })
      if (createdSessionId && setActive) {
        await setActive({ session: createdSessionId })
        router.replace('/')
      }
    } catch (err) {
      console.error('Browser OAuth error:', JSON.stringify(err, null, 2))
    }
  }

  return (
    <View>
      <Pressable style={styles.button} onPress={handleSignIn}>
        <Text style={styles.text}>Sign in with Google</Text>
      </Pressable>
    </View>
  )
}

const styles = StyleSheet.create({
  button: {
    backgroundColor: '#4285F4',
    padding: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  text: { color: '#fff', fontWeight: '600', fontSize: 16 },
})
```

The `useWarmUpBrowser` hook pre-loads the browser on Android for a snappier open. `WebBrowser.maybeCompleteAuthSession()` handles the redirect when the app resumes.

Several libraries support this pattern. `expo-auth-session` provides low-level OAuth utilities. `react-native-app-auth` wraps AppAuth for both platforms. [Auth0's React Native SDK](https://auth0.com/docs/quickstart/native/react-native-expo) uses browser-only OAuth. [Amplify/Cognito](https://docs.amplify.aws/gen1/react-native/build-a-backend/auth/add-social-provider/) routes through a Hosted UI in the browser. [Descope](https://www.descope.com/blog/post/expo-authentication) uses browser-based OIDC for Expo as well.

### Where browser OAuth shines

**Standards-based.** It works with any OAuth 2.0 provider. If the provider supports web login, it works.

**Simpler initial setup.** No native module configuration, no config plugins, no platform-specific client IDs. You can prototype in Expo Go (though Expo's own docs [warn against relying on it](https://docs.expo.dev/guides/authentication/) for production OAuth).

**Provider-agnostic.** One pattern covers Google, GitHub, Discord, Notion, whatever. Same code shape for all of them.

### Where browser OAuth breaks

The pain points here aren't theoretical. They're documented.

**SDK 53 breakage.** Expo SDK 53 broke browser-based Google OAuth. An Expo maintainer stated plainly: "we no longer maintain any libraries to support google auth" ([Expo GitHub Issue #38666, 2025](https://github.com/expo/expo/issues/38666)). The recommended fix was migrating to native sign-in. Expo's own Google authentication guide now points developers to `@react-native-google-signin/google-signin` instead of `expo-auth-session` ([Expo Google Authentication Guide](https://docs.expo.dev/guides/google-authentication/)).

**The Android DISMISS bug.** As mentioned, one team reported \~30% of Android sign-in attempts returning `DISMISS`. The issue remains open ([Expo GitHub Issue #23781, 2024](https://github.com/expo/expo/issues/23781)). A conversion killer, plain and simple.

**Deep link fragility.** Android's intent system, iOS Universal Links, custom URL schemes: each has its own failure modes. A misconfigured `assetlinks.json` or `apple-app-site-association` file silently swallows redirects.

**UX friction.** iOS shows a system prompt asking whether to open the browser. Users see a flash of Safari. The whole thing feels like leaving the app, because they are.

## How native OAuth works in Expo

Native OAuth replaces the browser redirect with platform APIs. The flow is shorter and stays inside your app:

1. User taps "Sign in with Google."
2. The app calls the platform's native authentication API.
3. Android: Credential Manager displays an account picker. iOS: `ASAuthorization` presents a native sheet.
4. User selects an account (often with [biometric confirmation](https://clerk.com/glossary.md#biometric-authentication)).
5. The platform returns an ID token directly to the app.
6. Your app exchanges the token for a session.

No browser. No redirect. No deep links to debug.

### Native Google Sign-In

On Android, this goes through Credential Manager. On iOS, it uses Google's native SDK (which wraps `ASAuthorization` under the hood).

Here's native Google Sign-In with Clerk:

```tsx
// components/NativeGoogleSignIn.tsx
import { useSignInWithGoogle } from '@clerk/expo/google'
import { useRouter } from 'expo-router'
import { Alert, Platform, Pressable, StyleSheet, Text } from 'react-native'

export function NativeGoogleSignIn() {
  const { startGoogleAuthenticationFlow } = useSignInWithGoogle()
  const router = useRouter()

  if (Platform.OS !== 'ios' && Platform.OS !== 'android') return null

  const handleGoogleSignIn = async () => {
    try {
      const { createdSessionId, setActive } = await startGoogleAuthenticationFlow()
      if (createdSessionId && setActive) {
        await setActive({ session: createdSessionId })
        router.replace('/')
      }
    } catch (err: any) {
      if (err.code === 'SIGN_IN_CANCELLED' || err.code === '-5') return
      Alert.alert('Error', err.message || 'Google Sign-In failed')
    }
  }

  return (
    <Pressable style={styles.button} onPress={handleGoogleSignIn}>
      <Text style={styles.text}>Sign in with Google</Text>
    </Pressable>
  )
}

const styles = StyleSheet.create({
  button: {
    backgroundColor: '#4285F4',
    padding: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  text: { color: '#fff', fontWeight: '600', fontSize: 16 },
})
```

The `useSignInWithGoogle` hook handles all the platform wiring. No `expo-web-browser`, no `expo-linking`, no redirect URL construction.

### Native Apple Sign-In

Apple Sign-In is native on iOS (Face ID bottom sheet) but falls back to browser OAuth on Android. Here's a component that handles both:

```tsx
// components/NativeAppleSignIn.tsx
import { useSignInWithApple } from '@clerk/expo/apple'
import { useSSO } from '@clerk/expo'
import { useRouter } from 'expo-router'
import { Alert, Platform, Pressable, StyleSheet, Text } from 'react-native'

export function NativeAppleSignIn() {
  const { startAppleAuthenticationFlow } = useSignInWithApple()
  const { startSSOFlow } = useSSO()
  const router = useRouter()

  const handleAppleSignIn = async () => {
    try {
      if (Platform.OS === 'ios') {
        const { createdSessionId, setActive } = await startAppleAuthenticationFlow()
        if (createdSessionId && setActive) {
          await setActive({ session: createdSessionId })
          router.replace('/')
        }
      } else {
        const { createdSessionId, setActive } = await startSSOFlow({
          strategy: 'oauth_apple',
        })
        if (createdSessionId && setActive) {
          await setActive({ session: createdSessionId })
          router.replace('/')
        }
      }
    } catch (err: any) {
      if (err?.message?.includes('ERR_REQUEST_CANCELED')) return
      if (err?.code === 'ERR_CANCELED') return
      Alert.alert('Error', err.message || 'Apple Sign-In failed')
    }
  }

  return (
    <Pressable style={styles.button} onPress={handleAppleSignIn}>
      <Text style={styles.text}>Continue with Apple</Text>
    </Pressable>
  )
}

const styles = StyleSheet.create({
  button: {
    backgroundColor: '#000',
    padding: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  text: { color: '#fff', fontWeight: '600', fontSize: 16 },
})
```

Platform detection routes iOS to the native `ASAuthorization` sheet and Android to browser-based OAuth. Your users don't know or care about the difference.

### Native library options

Going native means picking how much you want to assemble yourself.

**DIY multi-library approach.** You can wire `@react-native-google-signin/google-signin` (the Credential Manager version is [paywalled behind a sponsor license](https://react-native-google-signin.github.io/docs/install)), `expo-apple-authentication`, and `expo-crypto` together manually. It works. It's a lot of plumbing.

**Supabase** supports native Apple and Google, but you're wiring 3+ packages together yourself ([Apple guide](https://supabase.com/docs/guides/auth/social-login/auth-apple), [Google guide](https://supabase.com/docs/guides/auth/social-login/auth-google)).

**Firebase** has a similar story. `@react-native-firebase/auth` handles the token exchange, but you're still configuring native modules individually ([Firebase social auth docs](https://rnfirebase.io/auth/social-auth)).

**Stytch** offers a React Native SDK with native Google (Android via Credential Manager) and Apple (iOS) support, but it doesn't include an Expo config plugin and doesn't support native Google Sign-In on iOS ([Stytch Mobile SDKs](https://stytch.com/docs/mobile-sdks)).

**Clerk** bundles native Google and Apple into a single package with an Expo config plugin. `useSignInWithGoogle` and `useSignInWithApple` handle platform detection and token exchange. AuthView (currently in beta) goes further, rendering a complete sign-in UI with native providers built in. ([Native Google docs](https://clerk.com/docs/reference/expo/native-hooks/use-sign-in-with-google.md), [Native Apple docs](https://clerk.com/docs/reference/expo/native-hooks/use-sign-in-with-apple.md))

> Clerk's AuthView for Expo is currently in beta. It provides a pre-built sign-in/sign-up UI with native provider support, but the API may change before stable release.

### Where native OAuth shines

**No redirect chain.** The entire flow happens inside your app. No deep links to misconfigure, no `DISMISS` race conditions, no "Open in Safari?" prompts. Redirect-related failure modes don't apply because there's no redirect.

**Faster.** Credential Manager on Android often auto-selects the user's Google account, skipping the consent screen entirely. Fewer steps, fewer network round-trips.

**Platform-native feel.** The account picker and biometric prompts look like they belong. Because they do.

### The passkey bridge

Here's where native gets interesting beyond just OAuth. The same platform APIs that power native sign-in also power [passkeys](https://clerk.com/glossary.md#passkeys).

Android's Credential Manager is a single API surface for both Google Sign-In _and_ passkeys ([Android Credential Manager docs](https://developer.android.com/identity/sign-in/credential-manager-siwg)). iOS's `ASAuthorizationController` handles Apple Sign-In _and_ passkeys through the same framework ([Apple ASAuthorizationController docs](https://developer.apple.com/documentation/authenticationservices/asauthorizationcontroller)). Apple's iOS 26 Account Creation API pushes this further, unifying account creation and passkey enrollment ([WWDC 2025, Session 279](https://developer.apple.com/videos/play/wwdc2025/279/)).

Browser OAuth and native passkeys operate in completely different layers. In-app browser tabs during an OAuth redirect don't participate in the native passkey ecosystem ([Corbado Native App Passkeys, 2025](https://www.corbado.com/blog/native-app-passkeys)). So if passkeys are on your roadmap (and they probably should be), native OAuth puts you on the right foundation.

The numbers back this up. More than 15 billion online accounts can now use passkeys ([FIDO Alliance, 2025](https://fidoalliance.org/passkey-adoption-doubles-in-2024-more-than-15-billion-online-accounts-can-leverage-passkeys/)). 69% of users have created at least one passkey ([FIDO Alliance, 2025](https://fidoalliance.org/fido-alliance-champions-widespread-passkey-adoption-and-a-passwordless-future-on-world-passkey-day-2025/)). And 87% of businesses are actively deploying passkeys ([FIDO Alliance Passkey Index, 2025](https://fidoalliance.org/passkey-index-2025/)).

> Dashlane reported a 92% sign-in conversion rate with passkeys versus 54% for passwords ([Google Passkey Case Study, Dashlane](https://developers.google.com/identity/passkeys/case-studies/dashlane)). That comparison is passkey vs. password, not native vs. browser OAuth. But the relevance is clear: native OAuth uses the same APIs that make passkeys possible.

For Expo passkey support, Clerk offers `@clerk/expo-passkeys` ([Clerk Expo Passkeys docs](https://clerk.com/docs/reference/expo/passkeys.md)). The community `react-native-passkeys` package ([GitHub](https://github.com/peterferguson/react-native-passkeys)) is another option. Both require development builds; Expo Go can't access the native APIs ([Authsignal Blog, 2025](https://www.authsignal.com/blog/articles/implementing-passkeys-in-react-native-why-expo-go-falls-short-and-how-to-fix-it)).

Here's the minimal Clerk passkey code:

```tsx
import { useUser, useSignIn } from '@clerk/expo'

// Creating a passkey
const { user } = useUser()
await user.createPasskey()

// Signing in with a passkey
const { signIn } = useSignIn()
await signIn.authenticateWithPasskey({ flow: 'discoverable' })
```

Two calls. No browser, no redirect, no deep links.

> RFC 8252 recommends external browser tabs as the secure pattern for mobile OAuth ([RFC 8252](https://www.rfc-editor.org/rfc/rfc8252.html)). Native sign-in is a UX optimization, not a blanket security improvement. The browser isolates credentials from the app. Native APIs achieve security through platform-level attestation instead. Both approaches have valid security models; they protect against different threat vectors.

## Side-by-side comparison

Here's how the three approaches stack up across the dimensions that matter:

| Dimension      | Browser OAuth                                     | Native Hooks                            | Native AuthView         |
| -------------- | ------------------------------------------------- | --------------------------------------- | ----------------------- |
| Google Sign-In | Browser redirect                                  | Credential Manager / ASAuthorization    | Built-in                |
| Apple Sign-In  | Browser redirect                                  | Face ID sheet (iOS) / browser (Android) | Built-in                |
| Browser opens? | Yes                                               | No                                      | No                      |
| Expo Go?       | Limited (not recommended)                         | No                                      | No                      |
| Passkey-ready? | No                                                | Yes                                     | Yes                     |
| Custom UI?     | Full control                                      | Full control                            | Clerk controls UI       |
| Lines of code  | \~40/provider                                     | \~25/provider                           | \~5 total               |
| Extra packages | expo-web-browser, expo-linking, expo-auth-session | expo-apple-authentication, expo-crypto  | None beyond @clerk/expo |
| Min Expo SDK   | 51+                                               | 53+                                     | 53+                     |

The core call for each approach boils down to one line.

Browser OAuth:

```tsx
const result = await startSSOFlow({
  strategy: 'oauth_google',
  redirectUrl: Linking.createURL('/dashboard', { scheme: 'myapp' }),
})
```

Native hooks:

```tsx
const result = await startGoogleAuthenticationFlow()
```

AuthView (beta):

```tsx
<AuthView mode="signInOrUp" />
```

Native approaches require an Expo config plugin. Here's the relevant `app.json` configuration:

```json
{
  "expo": {
    "plugins": ["expo-router", "expo-secure-store", "expo-apple-authentication", "@clerk/expo"]
  }
}
```

The `@clerk/expo` plugin handles the native Google Sign-In configuration. `expo-apple-authentication` enables the Apple Sign-In entitlement. Neither plugin works in Expo Go, which brings us to the next section.

## Expo Go vs. development builds

Expo Go is great for prototyping. Tap a QR code, see your app. But it can't run custom native code, and native OAuth requires custom native code.

**Expo Go limitations for OAuth:**

- No config plugins. The `@clerk/expo` and `expo-apple-authentication` plugins need to modify native project files. Expo Go ships with a fixed set of native modules.
- Browser OAuth works in Expo Go, but with caveats. Expo's own documentation warns that OAuth in Expo Go is unreliable for production use.
- No passkey support. `@clerk/expo-passkeys` and `react-native-passkeys` both need native modules that Expo Go doesn't include.

> Expo Go's OAuth support is meant for quick testing, not production. Redirect handling and deep linking behave differently in Expo Go compared to standalone builds. Test your auth flows in a development build before shipping.

**Development builds** are the answer. They're custom versions of Expo Go that include your project's native modules. The workflow change is minimal:

1. Run `npx expo prebuild` to generate native projects.
2. Run `npx expo run:ios` or `npx expo run:android` instead of `npx expo start`.
3. You still get hot reload, the dev menu, and fast iteration.

The trade-off is real but small. You lose the QR-code-and-go simplicity of Expo Go. You gain access to every native API your app needs. For any app shipping to the App Store or Play Store, you'll need development builds anyway.

**When browser OAuth still makes sense:** Early prototyping in Expo Go. Supporting providers that don't offer native SDKs (Discord, GitHub, Notion). Web platform targets where native APIs don't exist.

## App Store compliance

Apple and Google both have opinions about how authentication should work in mobile apps. Getting these wrong means rejection.

### Apple's Guidelines

**[Guideline 4.8](https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple)** requires that apps offering third-party social login must also offer Sign in with Apple as an option. Exceptions exist: apps that exclusively use your company's own account system, education or enterprise apps using existing credentials, government or industry-backed identity systems, and apps that are clients for a specific third-party service (e.g., a Gmail client doesn't need Apple Sign-In) ([Apple Developer, 2025](https://developer.apple.com/app-store/review/guidelines/)).

**Guideline 4.0 (Design)** is trickier. Apple has rejected apps that present authentication through an embedded web view or browser redirect when a native experience is expected. Auth0 users have reported App Store rejections tied to their browser-based login flow ([Auth0 Community, 2024](https://community.auth0.com/t/app-store-rejection-due-to-web-login-issue-with-phone-passwordless-react-native-auth0-and-sfsafariviewcontroller/189417)). AWS Amplify users have seen similar issues ([Amplify GitHub Issue #13668, 2024](https://github.com/aws-amplify/amplify-js/issues/13668)). The pattern: Apple reviewers flag browser-based login as a substandard experience.

Native Sign in with Apple removes the browser-redirect UX that triggered these rejections. The Face ID bottom sheet aligns with what Apple reviewers expect to see.

### Google's WebView policy

Google blocks OAuth sign-in from embedded WebViews (not system browser tabs, but actual `WebView` components). This has been policy since 2021. `expo-web-browser` uses `SFSafariViewController` on iOS and Chrome Custom Tabs on Android, which are allowed. But if you're using a raw `WebView`, Google will block the request.

### The RFC 8252 nuance

RFC 8252 recommends external browser tabs (not WebViews) as the secure pattern for mobile [OAuth](https://clerk.com/glossary.md#authorization-code-flow). This is what `expo-web-browser` implements. Native OAuth sidesteps the entire redirect pattern, which means RFC 8252's guidance about redirect URIs, [PKCE](https://clerk.com/glossary.md#code-exchange-pkce), and browser security doesn't directly apply. Native APIs handle security through platform attestation and the Credential Management API instead.

Both approaches comply with platform requirements. Native just happens to also satisfy the "native experience" preference that app store reviewers increasingly expect.

## Decision framework

### When to use browser OAuth

- You're prototyping in Expo Go and need quick social login.
- You support providers without native SDKs (GitHub, Discord, Notion, LinkedIn).
- Your app targets the web in addition to iOS/Android.
- You want the simplest possible initial setup.
- You're using a provider that only supports browser flows (Descope, Amplify Hosted UI).

### When to use native OAuth

- You're shipping to the App Store or Play Store (you're already building dev builds).
- Google and Apple are your primary social login providers.
- You want to avoid the redirect-related failure modes documented on Android.
- Passkeys are on your roadmap.
- You want the fastest possible sign-in UX.
- App Store reviewers have flagged your browser-based login.

### The "start browser, migrate native" path

Many teams start with browser OAuth during early development, then migrate to native before launch. That's a reasonable approach. Here's what the migration looks like:

1. **Set up a development build.** Run `npx expo prebuild` and switch from `npx expo start` to `npx expo run:ios` / `npx expo run:android`.
2. **Add config plugins.** Add `@clerk/expo`, `expo-apple-authentication`, and `expo-secure-store` to the plugins array in `app.json`.
3. **Configure platform credentials.** Set up Google OAuth client IDs for iOS and Android in the Google Cloud Console. Enable Sign in with Apple in your Apple Developer account.
4. **Swap the hook calls.** Replace `useSSO` with `useSignInWithGoogle` and `useSignInWithApple`. Remove `expo-web-browser` and `expo-linking` imports.
5. **Add platform fallbacks.** For providers that don't have native SDKs, keep `useSSO` as the fallback.
6. **Test on physical devices.** Credential Manager and `ASAuthorization` behave differently in simulators.

> Test native sign-in on real devices, not just simulators. Android emulators don't always have Google Play Services configured correctly, and iOS simulators don't support Face ID by default (you'll need to enable it in the simulator's Features menu).

### Provider comparison (verified March 2026)

| Provider |  Native Google  |   Native Apple  | Browser OAuth | Config Plugin | Free Tier             | Self-Hosting |
| -------- | :-------------: | :-------------: | :-----------: | :-----------: | --------------------- | :----------: |
| Clerk    |       Yes       |       Yes       |      Yes      |      Yes      | 50,000 MRU            |      No      |
| Supabase |  Manual wiring  |  Manual wiring  |      Yes      |       No      | 50,000 MAU            |      Yes     |
| Firebase | Via RN Firebase | Via RN Firebase |      Yes      |       No      | Unlimited (non-phone) |      No      |
| Auth0    |        No       |        No       |      Yes      |       No      | 25,000 MAU            |      No      |
| Stytch   |     Partial     |  No native iOS  |      Yes      |       No      | 10,000 MAU            |      No      |
| Descope  |        No       |        No       |      Yes      |       No      | 7,500 MAU             |      No      |

Clerk is the only provider in this comparison with an Expo config plugin that handles both Google and Apple native sign-in from a single package. Supabase and Firebase support native flows but require assembling multiple packages manually. Provider capabilities and free tier limits change; check each provider's current pricing page before making commitments.

## Frequently asked questions

## FAQ

### What is the difference between browser OAuth and native OAuth in Expo?

Browser OAuth opens a system browser tab (Safari or Chrome), lets the user authenticate with the provider, and redirects back to your app via a deep link. Native OAuth uses platform APIs like Android Credential Manager and iOS ASAuthorization to handle authentication inside the app without opening a browser. Browser OAuth works with any provider but introduces redirect failures and UX friction. Native OAuth is smoother and faster but only supports providers with native SDKs (primarily Google and Apple).

### Can I use native Google Sign-In with Expo Go?

No. Native Google Sign-In requires custom native modules that Expo Go does not include. You need a development build created with `npx expo prebuild` and config plugins like `@clerk/expo`. Browser-based Google OAuth works in Expo Go, but Expo's documentation warns that OAuth in Expo Go is unreliable for production use.

### Does Apple require native Sign in with Apple for App Store approval?

Apple Guideline 4.8 requires that apps offering third-party social login (Google, Facebook, etc.) also offer Sign in with Apple as an option. Exceptions exist for enterprise apps, education apps, and apps that exclusively use your own account system. While browser-based Sign in with Apple technically complies, Apple reviewers have rejected apps for presenting authentication through browser redirects when a native experience is expected. Native Sign in with Apple removes the browser-redirect UX that triggered these specific rejections.

### Which auth providers support native OAuth in Expo?

Google and Apple are the primary providers with native OAuth support in Expo. Google uses Credential Manager on Android and its native SDK on iOS. Apple uses ASAuthorization on iOS (Face ID sheet) and falls back to browser OAuth on Android. Other providers like GitHub, Discord, LinkedIn, and Notion only support browser-based OAuth. Clerk, Supabase, and Firebase all support native Google and Apple, though setup complexity varies.

### How does Credential Manager relate to passkeys?

Android Credential Manager is a unified API that handles multiple credential types through a single interface: Google Sign-In, passkeys, passwords, and federated identity. When you implement native Google Sign-In through Credential Manager, you are using the same API surface that supports passkeys. This means adopting native OAuth puts your app on the same technical foundation needed for passkey support. Browser-based OAuth operates in a completely separate layer and does not participate in the native passkey ecosystem.

### What broke with Google OAuth in Expo SDK 53?

Expo SDK 53 broke browser-based Google OAuth. An Expo maintainer stated that they "no longer maintain any libraries to support google auth" via browser-based flows. The recommended migration path was switching to native Google Sign-In using Credential Manager on Android and the native Google SDK on iOS. This breaking change pushed many teams from browser to native OAuth.

### Can I start with browser OAuth and migrate to native later?

Yes, and many teams follow this path. Start with browser OAuth using `useSSO` for quick prototyping in Expo Go, then migrate to native before your production launch. Migration involves setting up a development build, adding config plugins to `app.json`, configuring platform credentials (Google Cloud Console, Apple Developer account), and swapping `useSSO` calls for `useSignInWithGoogle` and `useSignInWithApple`. Keep `useSSO` as a fallback for providers without native SDKs.

### How many lines of code does native sign-in take compared to browser OAuth?

Browser OAuth with Clerk takes roughly 40 lines per provider, including browser warm-up, redirect URL construction, and session handling. Native hooks take about 25 lines per provider, since there is no browser warm-up or redirect URL needed. Clerk's AuthView (beta) reduces this to approximately 5 lines total for a complete sign-in UI with native providers built in.

### Does native Apple Sign-In work on Android?

Apple does not provide a native Sign-In SDK for Android. On Android, Apple Sign-In falls back to a browser-based OAuth flow. With Clerk, you can use `useSignInWithApple` on iOS for the native Face ID sheet and `useSSO` with the `oauth_apple` strategy on Android for the browser fallback. The platform detection and routing can be handled in a single component.

### Is native OAuth more secure than browser-based OAuth?

Not inherently. RFC 8252 specifically recommends external browser tabs as the secure pattern for mobile OAuth because the browser isolates provider credentials from the app. Native OAuth achieves security differently, through platform-level attestation and the Credential Management API. Both approaches have valid security models. Native OAuth is primarily a UX optimization (no redirects, no browser switching) rather than a security upgrade. The security trade-offs depend on your specific threat model.
