# Expo Google Sign-In Without a WebView: The Native Approach Using Clerk - Part 2

> Part 2 of 2. Start with [Expo Google Sign-In Without a WebView: The Native Approach Using Clerk](https://clerk.com/articles/expo-google-sign-in-without-a-webview-the-native-approach-using-clerk.md).

This is the second part of a two-part series on implementing native Google Sign-In in Expo apps. While the first part covered the foundational setup and using the pre-built `<AuthView />` component, this part explores building a custom authentication UI using the `useSignInWithGoogle` hook, combining Google Sign-In with Email/OTP authentication, managing user profiles and sessions, and handling production deployments and errors.

## Implementing Google Sign-In with the useSignInWithGoogle Hook

For full control over the UI, use the `useSignInWithGoogle` hook. This is the approach the complete app example uses.

### The Sign-In Screen Component

```tsx
// components/GoogleSignInButton.tsx
import { useSignInWithGoogle } from '@clerk/expo/google'
import { Alert, TouchableOpacity, Text, StyleSheet, Platform } from 'react-native'

export function GoogleSignInButton() {
  const { startGoogleAuthenticationFlow } = useSignInWithGoogle()

  if (Platform.OS === 'web') return null

  const handlePress = async () => {
    try {
      const { createdSessionId, setActive } = await startGoogleAuthenticationFlow()

      if (createdSessionId && setActive) {
        await setActive({ session: createdSessionId })
      }
    } catch (err: any) {
      // User cancelled: don't show an error toast
      if (err?.code === 'SIGN_IN_CANCELLED' || err?.code === '-5') return

      Alert.alert('Sign-in error', err?.message ?? 'Something went wrong')
    }
  }

  return (
    <TouchableOpacity style={styles.button} onPress={handlePress}>
      <Text style={styles.text}>Continue with Google</Text>
    </TouchableOpacity>
  )
}

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

Import `useSignInWithGoogle` from `@clerk/expo/google` (not from `@clerk/expo` directly).

### Handling Sign-In Success and Errors

`startGoogleAuthenticationFlow()` returns:

- `createdSessionId`: the session ID if authentication succeeded
- `setActive`: function to activate the session
- `signIn` / `signUp`: the underlying Clerk objects (rarely needed)

On success, call `setActive({ session: createdSessionId })`. Clerk's token cache persists the session so the user stays signed in across app restarts.

**Transfer flow:** if someone signs in with Google but doesn't have a Clerk account, one is created automatically. If they sign up but already have an account, Clerk signs them in. No separate sign-in/sign-up screens needed for the Google flow.

**[Account linking](https://clerk.com/glossary/account-linking.md):** if the user's Google email matches an existing Clerk account, accounts are linked automatically when both emails are verified.

### Triggering the Native Google Sign-In Flow

When the user taps the button:

- **Android:** A bottom sheet appears from Credential Manager showing the user's Google accounts. They tap one, and the flow completes. No browser opens.
- **iOS (with native config):** The `ASAuthorization` system credential picker appears. Same pattern: tap, done, no browser.
- **iOS (without native config):** Falls back to `ASWebAuthenticationSession`, which opens a system browser sheet.

The flow is managed entirely by the OS. Your app receives a session ID on success.

## Adding Email + OTP Authentication Alongside Google Sign-In

The complete app combines Google Sign-In with email [one-time passcode](https://clerk.com/glossary/one-time-passcodes-email-sms.md) authentication on the same screen, with a visual separator between them.

### Building the Combined Sign-Up Screen

```tsx
// app/(auth)/sign-up.tsx
import { useState } from 'react'
import { View, Text, TextInput, TouchableOpacity, Alert, StyleSheet } from 'react-native'
import { useSignUp } from '@clerk/expo'
import { Link } from 'expo-router'
import { GoogleSignInButton } from '../../components/GoogleSignInButton'

export default function SignUpScreen() {
  const { signUp } = useSignUp()
  const [email, setEmail] = useState('')
  const [pendingVerification, setPendingVerification] = useState(false)
  const [code, setCode] = useState('')

  const handleEmailSignUp = async () => {
    const { error } = await signUp.create({ emailAddress: email })
    if (error) {
      Alert.alert('Error', error.longMessage ?? 'Could not create account')
      return
    }

    const { error: sendError } = await signUp.verifications.sendEmailCode()
    if (sendError) {
      Alert.alert('Error', sendError.longMessage ?? 'Could not send code')
      return
    }

    setPendingVerification(true)
  }

  const handleVerify = async () => {
    const { error } = await signUp.verifications.verifyEmailCode({ code })
    if (error) {
      Alert.alert('Verification failed', error.longMessage ?? 'Invalid code')
      return
    }

    if (signUp.status === 'complete') {
      await signUp.finalize()
    }
  }

  if (pendingVerification) {
    return (
      <View style={styles.container}>
        <Text style={styles.title}>Verify your email</Text>
        <Text style={styles.subtitle}>We sent a code to {email}</Text>
        <TextInput
          value={code}
          onChangeText={setCode}
          placeholder="Enter 6-digit code"
          keyboardType="number-pad"
          maxLength={6}
          style={styles.input}
        />
        <TouchableOpacity style={styles.primaryButton} onPress={handleVerify}>
          <Text style={styles.primaryButtonText}>Verify</Text>
        </TouchableOpacity>
      </View>
    )
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Create an account</Text>

      <GoogleSignInButton />

      <View style={styles.divider}>
        <View style={styles.dividerLine} />
        <Text style={styles.dividerText}>or</Text>
        <View style={styles.dividerLine} />
      </View>

      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Email address"
        autoCapitalize="none"
        keyboardType="email-address"
        style={styles.input}
      />

      <TouchableOpacity style={styles.primaryButton} onPress={handleEmailSignUp}>
        <Text style={styles.primaryButtonText}>Send code</Text>
      </TouchableOpacity>

      <Link href="/(auth)/sign-in" asChild>
        <TouchableOpacity style={styles.linkButton}>
          <Text style={styles.linkText}>Already have an account? Sign in</Text>
        </TouchableOpacity>
      </Link>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', padding: 24 },
  title: { fontSize: 28, fontWeight: 'bold', marginBottom: 24 },
  subtitle: { fontSize: 14, color: '#666', marginBottom: 16 },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 14,
    borderRadius: 8,
    fontSize: 16,
    marginBottom: 16,
  },
  primaryButton: {
    backgroundColor: '#000',
    paddingVertical: 14,
    borderRadius: 8,
    alignItems: 'center',
    marginBottom: 12,
  },
  primaryButtonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
  divider: {
    flexDirection: 'row',
    alignItems: 'center',
    marginVertical: 20,
  },
  dividerLine: { flex: 1, height: 1, backgroundColor: '#ddd' },
  dividerText: { marginHorizontal: 12, color: '#999', fontSize: 14 },
  linkButton: { alignItems: 'center', marginTop: 8 },
  linkText: { color: '#666', fontSize: 14 },
})
```

### Building the Combined Sign-In Screen

```tsx
// app/(auth)/sign-in.tsx
import { useState } from 'react'
import { View, Text, TextInput, TouchableOpacity, Alert, StyleSheet } from 'react-native'
import { useSignIn } from '@clerk/expo'
import { Link } from 'expo-router'
import { GoogleSignInButton } from '../../components/GoogleSignInButton'

export default function SignInScreen() {
  const { signIn } = useSignIn()
  const [email, setEmail] = useState('')
  const [pendingVerification, setPendingVerification] = useState(false)
  const [code, setCode] = useState('')

  const handleEmailSignIn = async () => {
    const { error } = await signIn.emailCode.sendCode({ emailAddress: email })
    if (error) {
      Alert.alert('Error', error.longMessage ?? 'Could not send code')
      return
    }

    setPendingVerification(true)
  }

  const handleVerify = async () => {
    const { error } = await signIn.emailCode.verifyCode({ code })
    if (error) {
      Alert.alert('Verification failed', error.longMessage ?? 'Invalid code')
      return
    }

    if (signIn.status === 'complete') {
      await signIn.finalize()
    } else if (signIn.status === 'needs_second_factor') {
      // Handle MFA if enabled. See:
      // /docs/guides/development/custom-flows/authentication/email-sms-otp
      Alert.alert('MFA required', 'Complete second factor authentication')
    }
  }

  if (pendingVerification) {
    return (
      <View style={styles.container}>
        <Text style={styles.title}>Check your email</Text>
        <Text style={styles.subtitle}>We sent a code to {email}</Text>
        <TextInput
          value={code}
          onChangeText={setCode}
          placeholder="Enter 6-digit code"
          keyboardType="number-pad"
          maxLength={6}
          style={styles.input}
        />
        <TouchableOpacity style={styles.primaryButton} onPress={handleVerify}>
          <Text style={styles.primaryButtonText}>Verify</Text>
        </TouchableOpacity>
      </View>
    )
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Sign in</Text>

      <GoogleSignInButton />

      <View style={styles.divider}>
        <View style={styles.dividerLine} />
        <Text style={styles.dividerText}>or</Text>
        <View style={styles.dividerLine} />
      </View>

      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Email address"
        autoCapitalize="none"
        keyboardType="email-address"
        style={styles.input}
      />

      <TouchableOpacity style={styles.primaryButton} onPress={handleEmailSignIn}>
        <Text style={styles.primaryButtonText}>Send code</Text>
      </TouchableOpacity>

      <Link href="/(auth)/sign-up" asChild>
        <TouchableOpacity style={styles.linkButton}>
          <Text style={styles.linkText}>Don't have an account? Sign up</Text>
        </TouchableOpacity>
      </Link>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', padding: 24 },
  title: { fontSize: 28, fontWeight: 'bold', marginBottom: 24 },
  subtitle: { fontSize: 14, color: '#666', marginBottom: 16 },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 14,
    borderRadius: 8,
    fontSize: 16,
    marginBottom: 16,
  },
  primaryButton: {
    backgroundColor: '#000',
    paddingVertical: 14,
    borderRadius: 8,
    alignItems: 'center',
    marginBottom: 12,
  },
  primaryButtonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
  divider: {
    flexDirection: 'row',
    alignItems: 'center',
    marginVertical: 20,
  },
  dividerLine: { flex: 1, height: 1, backgroundColor: '#ddd' },
  dividerText: { marginHorizontal: 12, color: '#999', fontSize: 14 },
  linkButton: { alignItems: 'center', marginTop: 8 },
  linkText: { color: '#666', fontSize: 14 },
})
```

> The Google Sign-In button handles both sign-in and sign-up via Clerk's transfer flow. A user tapping "Continue with Google" on either screen gets the right outcome automatically.

### Verifying the OTP Code

Both screens use inline verification. After `signIn.emailCode.verifyCode()` or `signUp.verifications.verifyEmailCode()` succeeds, call `finalize()` to activate the session. The `<Show>` components in the layouts detect the auth state change and redirect automatically.

These Core 3 sign-in and sign-up methods resolve to an `{ error }` object instead of throwing, so check `error` after each call rather than wrapping them in `try/catch`. (The native `useSignInWithGoogle()` hook is the exception — it throws, so keep its `try/catch`.) Calling `finalize()` converts a completed sign-in or sign-up into the active session and updates anything observing auth state, such as `useUser()` and the `<Show>` components. It is the Core 3 replacement for the older `setActive()` pattern in these custom flows.

For production, handle non-happy-path status values like `needs_second_factor` (MFA enabled) and `needs_client_trust`. See the [Email/SMS OTP Custom Flow](https://clerk.com/docs/guides/development/custom-flows/authentication/email-sms-otp.md) docs for the complete set of status codes.

## Managing Sessions, the User Profile, and Sign-Out

### Checking Authentication State

```tsx
// app/(auth)/_layout.tsx
import { Show } from '@clerk/expo'
import { Redirect, Slot } from 'expo-router'

export default function AuthLayout() {
  return (
    <Show when="signed-out" treatPendingAsSignedOut={false} fallback={<Redirect href="/(home)" />}>
      <Slot />
    </Show>
  )
}
```

```tsx
// app/(home)/_layout.tsx
import { Show } from '@clerk/expo'
import { Redirect, Slot } from 'expo-router'

export default function HomeLayout() {
  return (
    <Show
      when="signed-in"
      treatPendingAsSignedOut={false}
      fallback={<Redirect href="/(auth)/sign-in" />}
    >
      <Slot />
    </Show>
  )
}
```

The `<Show>` component from `@clerk/expo` replaces the older `<SignedIn>` / `<SignedOut>` components. Use `when="signed-in"` or `when="signed-out"` to conditionally render based on auth state.

### The Native UserButton and UserProfile Components

```tsx
// app/(home)/index.tsx
import { View, Text, StyleSheet } from 'react-native'
import { Show } from '@clerk/expo'
import { UserButton } from '@clerk/expo/native'
import { useUser } from '@clerk/expo'

export default function HomeScreen() {
  const { user } = useUser()

  return (
    <Show when="signed-in">
      <View style={styles.container}>
        <View style={styles.header}>
          <Text style={styles.greeting}>Welcome, {user?.firstName ?? 'there'}</Text>
          <View style={styles.avatar}>
            <UserButton />
          </View>
        </View>
        <Text style={styles.email}>{user?.primaryEmailAddress?.emailAddress}</Text>
      </View>
    </Show>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24, paddingTop: 80 },
  header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
  greeting: { fontSize: 24, fontWeight: 'bold' },
  avatar: { width: 44, height: 44, borderRadius: 22, overflow: 'hidden' },
  email: { fontSize: 14, color: '#666', marginTop: 8 },
})
```

`useAuth()` returns `isSignedIn`, `userId`, `sessionId`, and `getToken`. When you read auth state with `useAuth()` alongside native components, pass `{ treatPendingAsSignedOut: false }` — `useAuth({ treatPendingAsSignedOut: false })` — so a pending session task (for example, an unselected organization) isn't treated as signed-out. `useUser()` returns the full user object with `user.firstName`, `user.primaryEmailAddress`, `user.imageUrl`, and more.

`<UserButton />` from `@clerk/expo/native` renders the user's avatar. Tapping it opens a native profile modal powered by `<UserProfileView />`. Sign-out is handled automatically and synced with the JS SDK. The component takes no props; control size and shape through the parent `View`.

For more control, use the `useUserProfileModal()` hook:

```tsx
import { useUserProfileModal } from '@clerk/expo'

const { presentUserProfile, isAvailable } = useUserProfileModal()

// Open the profile modal programmatically
if (isAvailable) {
  await presentUserProfile()
}
```

### Signing Out Correctly

```tsx
import { useClerk } from '@clerk/expo'
import { useRouter } from 'expo-router'

export function SignOutButton() {
  const { signOut } = useClerk()
  const router = useRouter()

  const handleSignOut = async () => {
    await signOut()
    router.replace('/(auth)/sign-in')
  }

  return (
    <TouchableOpacity onPress={handleSignOut}>
      <Text>Sign out</Text>
    </TouchableOpacity>
  )
}
```

`signOut()` clears the session and the token cache. If you're using `<UserButton />`, sign-out is built in and syncs automatically with the JS SDK.

## Error Handling Reference

### Android: SHA-1 Fingerprint Mismatch

A common configuration failure. The root cause is a certificate fingerprint mismatch, but the symptom depends on which library you use. With Clerk's native Google Sign-In — backed by Android [Credential Manager](https://developer.android.com/identity/sign-in/credential-manager) through the `clerk-android` SDK — a mismatch does **not** surface as the legacy `DEVELOPER_ERROR` / `code 10` you may have hit with `@react-native-google-signin/google-signin`. Credential Manager fails to return a Google ID token and throws a `GetCredentialException` (commonly `NoCredentialException`), which Clerk surfaces as a "No Google account available" error. The account chooser may not appear, or it appears and then reports no usable account, instead of completing sign-in.

> `DEVELOPER_ERROR` with `code 10` is specific to the legacy Google Play Services Sign-In API (and the `@react-native-google-signin/google-signin` library that wraps it). If you see a literal `code 10`, you are on that path, not `@clerk/expo`'s native flow.

**Root cause:** the SHA-1 registered in Google Cloud Console (or the SHA-256 in the Clerk Dashboard) doesn't match the keystore that signed the current build.

**Three different SHA-1 values to manage:**

1. **Debug keystore** (local `npx expo run:android`):

```bash
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android
```

2. **EAS managed keystore** (cloud builds):

```bash
eas credentials --platform android
```

3. **Google Play App Signing key** (production): found in Play Console > **Release** > **Setup** > **App Integrity**.

Each needs its own Android OAuth Client ID in Google Cloud Console.

**Also check:** the `webClientId` environment variable must reference the **Web Application** type Client ID, not the Android one.

### iOS: "The operation could not be completed"

Usually a configuration mismatch:

- `EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID` doesn't match the Google Cloud Console iOS Client ID
- `ios.bundleIdentifier` in `app.config.ts` doesn't match what's registered in Google Cloud Console
- `EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME` isn't set (or isn't the reversed client ID format, e.g., `com.googleusercontent.apps.123456`)

### Expo Go Limitations with Native Sign-In

`useSignInWithGoogle()` and `<AuthView />` won't work in Expo Go. The TurboModule `NativeClerkGoogleSignIn` isn't available.

Use a development build (`npx expo run:ios`) or EAS Build (`eas build --profile development`). JS-only email flows via `useSignIn`/`useSignUp` work in Expo Go for testing other parts of the app.

### Deep Link and Bundle Identifier Issues

For Clerk's native Google flow, the main iOS pitfall is the Google callback URL scheme and native app identifiers, not a custom Expo redirect URI.

Common mistakes:

- Bundle ID or package name in `app.config.ts` doesn't match Google Cloud Console and Clerk Dashboard entries
- The iOS URL scheme doesn't match the reversed client ID
- Forgetting to register Native Applications in the Clerk Dashboard (Team ID + Bundle ID for iOS, package name + SHA-256 for Android)

## Platform-Specific Configuration

### iOS: Info.plist and URL Schemes

The `@clerk/expo` config plugin handles iOS configuration automatically:

- Injects the iOS URL scheme from `EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME`
- Sets the deployment target to iOS 17.0
- Adds the clerk-ios SPM package
- Includes the Apple Privacy Manifest (required since May 1, 2024)

No manual `Info.plist` editing required.

### Android: Credential Manager

Key differences from Firebase/Supabase approaches:

- **No `google-services.json` required.** Clerk doesn't use Firebase for authentication.
- SHA-1 is required in Google Cloud Console for the Android OAuth Client ID. SHA-256 is required in the Clerk Dashboard's Native Applications page. Both come from `keytool -list -v`.
- Credential Manager requires Google Play Services. Your emulator must include the Google Play Store image.
- Supports Android 4.4+ for passwords, Android 9+ for passkeys.

## EAS Build Configuration for Native Google Sign-In

```json
{
  "cli": {
    "version": ">= 14.0.0"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "env": {
        "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY": "pk_test_..."
      }
    },
    "preview": {
      "distribution": "internal"
    },
    "production": {
      "autoIncrement": true
    }
  }
}
```

### Development Builds

```bash
eas build --profile development --platform ios
```

Set `developmentClient: true` and `distribution: "internal"`. Environment variables can be set per profile or in the EAS Dashboard.

### Preview and Production Builds

For production, the most common Google Sign-In failure is SHA-1 mismatch:

- Google Play App Signing uses an **app signing key** that's different from the **upload key**
- Both need their own Android OAuth Client IDs in Google Cloud Console

> Production Expo apps still need a domain on the Clerk production instance, even if there's no traditional web frontend. See the [Expo Deployment Guide](https://clerk.com/docs/guides/development/deployment/expo.md) for details.

## Migrating from Browser-Based Google OAuth

### From expo-auth-session

**Remove:** `useAuthRequest`, Google provider imports, redirect URI config, `makeRedirectUri`, `promptAsync`.

**Keep:** `expo-auth-session` and `expo-web-browser` (peer dependencies of `@clerk/expo`).

**Add:** `@clerk/expo`, `expo-secure-store`, `expo-dev-client`, and `expo-crypto` (hook approach only).

**Replace:** `useAuthRequest` and the entire OAuth flow with `useSignInWithGoogle` or `<AuthView />`. The native flow is one function call: `startGoogleAuthenticationFlow()`. No `discovery` object, no `makeRedirectUri`, no `promptAsync`.

### From @react-native-google-signin/google-signin

**Remove:** `@react-native-google-signin/google-signin`, `GoogleSignin.configure()`, `GoogleSignin.signIn()`, manual token extraction, `GoogleSignin.hasPlayServices()`.

**Remove (if only used for Google auth):** `google-services.json`, `GoogleService-Info.plist`, Firebase config. If you use Firebase for other features, keep these files.

**Add:** `@clerk/expo`, configure Clerk Dashboard with your existing Google Cloud credentials.

**Benefits of switching:**

- No separate Google Sign-In library needed
- No `google-services.json` or `GoogleService-Info.plist` config files (unless Firebase is needed for other features)
- Session management, user profiles, and sign-out are built in
- Credential Manager support included (the standalone library ships Credential Manager only in its paid [Universal Sign In](https://universal-sign-in.com/) tier; the free module uses the deprecated legacy Google Sign-In SDK)

> **Import path change from Core 2 to Core 3:** The package renamed from `@clerk/clerk-expo` to `@clerk/expo`. Hooks import from `@clerk/expo/google`, native components from `@clerk/expo/native`. Native Google Sign-In and the native components shipped in `@clerk/expo` 3.1 and require Expo SDK 53 or later.

## Clerk vs. Other Expo Authentication Solutions

| If you want...                            | Best fit                      | Why                                                                     |
| ----------------------------------------- | ----------------------------- | ----------------------------------------------------------------------- |
| Browser-based OAuth that works in Expo Go | `expo-auth-session`           | No native build required, but you keep redirect complexity              |
| Full DIY native Google Sign-In            | `@react-native-google-signin` | Native provider control, but you still own session management           |
| Native Google Sign-In plus managed auth   | **Clerk**                     | Native provider flow plus Clerk-managed sessions, users, and profile UI |

Clerk's advantage isn't just the native Google UI. It's that the token exchange, session creation, session refresh, and user management happen automatically. The other approaches give you a Google ID token and leave the rest to you.

## Key Takeaways

- **Native Google Sign-In in Expo doesn't need a browser.** Clerk uses Credential Manager on Android (always native) and `ASAuthorization` on iOS (when `EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME` is configured). Without the iOS URL scheme, iOS falls back to a system browser sheet.
- **Two approaches: `<AuthView />` for zero-code auth, `useSignInWithGoogle` for custom UI.** Both use the same native flow under the hood.
- **Certificate fingerprint management is the hardest part.** Debug, EAS, and production builds each have different fingerprints. Register SHA-1 in Google Cloud Console and SHA-256 in the Clerk Dashboard for each.
- **Expo Go can't run native sign-in.** Use development builds from the start.
- **Clerk handles the full auth lifecycle.** Sign-in, sign-up, transfer flow, session management, user profiles, and sign-out are included.

Get started: [Expo Quickstart](https://clerk.com/docs/expo/getting-started/quickstart.md) | [Sign in with Google Guide](https://clerk.com/docs/expo/guides/configure/auth-strategies/sign-in-with-google.md) | [clerk-expo-quickstart examples](https://github.com/clerk/clerk-expo-quickstart)

## Frequently Asked Questions

## FAQ

### What's the difference between useSSO and useSignInWithGoogle?

`useSSO()` runs browser-based [SSO](https://clerk.com/glossary/single-sign-on-sso.md) for any provider (Google, GitHub, Apple) via a system browser. `useSignInWithGoogle()` uses the native Google credential picker, no browser. Use the native hook for Google; use `useSSO` for providers without a native hook.

### How do I handle the SHA-1 mismatch error on Android?

This error occurs when the SHA-1 registered in Google Cloud Console does not match the keystore used to sign the app. Ensure you have registered the correct SHA-1 for your debug keystore, EAS managed keystore, or Google Play App Signing key depending on your build environment.

## In this series

1. [Expo Google Sign-In Without a WebView: The Native Approach Using Clerk](https://clerk.com/articles/expo-google-sign-in-without-a-webview-the-native-approach-using-clerk.md)
2. **Expo Google Sign-In Without a WebView: The Native Approach Using Clerk - Part 2** (you are here)
