Skip to content
/Google Sign In with React Native — The Complete Guide
Mar 2025·8 min read

Google Sign In with React Native — The Complete Guide

Everything you need to implement Google Sign In using @react-native-google-signin/google-signin — from SHA-1 fingerprint setup and OAuth client IDs, to silent sign-in, token refresh, Play Integrity, and the iOS URL scheme trap that breaks half of all setups.

React NativeAuthenticationGoogleOAuthAndroid
Text-to-Speech unsupported
Text Size

Google Sign In looks straightforward until it isn't. The library installs fine, the button renders, then you hit a cryptic DEVELOPER_ERROR on Android or a silent failure on iOS with no stack trace. I've set this up across a dozen React Native projects and the bugs are almost always the same three things: wrong SHA-1 fingerprint in Firebase, missing URL scheme in Info.plist, or a mismatched OAuth client ID. This guide eliminates all of them.

Step 1 — Firebase project and OAuth client setup

Go to the Firebase console → create or open your project → add an Android app with your exact package name → download google-services.json and place it at android/app/google-services.json. For iOS, add an iOS app with your bundle ID → download GoogleService-Info.plist → drag it into Xcode under the project root (not a subfolder). The OAuth client IDs are embedded in these files — do not hardcode them manually. For Android, you must register both your debug and release SHA-1 fingerprints or sign-in will silently fail in release builds.

Get SHA-1 fingerprint
# Debug keystore (for development)
keytool -list -v \
  -keystore ~/.android/debug.keystore \
  -alias androiddebugkey \
  -storepass android \
  -keypass android

# Release keystore (for production — use your actual keystore path)
keytool -list -v \
  -keystore ./android/app/release.keystore \
  -alias your-key-alias

Step 2 — Install and link

terminal
npm install @react-native-google-signin/google-signin

# android/build.gradle — add inside dependencies
classpath 'com.google.gms:google-services:4.4.1'

# android/app/build.gradle — add at bottom
apply plugin: 'com.google.gms.google-services'

# iOS
cd ios && pod install

Step 3 — iOS URL scheme (the trap everyone falls into)

Open GoogleService-Info.plist and find the REVERSED_CLIENT_ID value — it looks like com.googleusercontent.apps.xxxxxxxxxx-xxxx. Now open Xcode → your target → Info tab → URL Types → add a new entry → set the URL Scheme to that exact reversed client ID value. Without this, Google's auth callback cannot return to your app on iOS and sign-in will hang indefinitely or silently fail. This is the single most common iOS setup mistake.

Step 4 — Configure and implement sign in

GoogleAuth.js
import {
  GoogleSignin,
  GoogleSigninButton,
  statusCodes,
} from '@react-native-google-signin/google-signin';
import { useEffect } from 'react';

// Call once at app startup (e.g. in App.jsx)
GoogleSignin.configure({
  // Found in GoogleService-Info.plist as CLIENT_ID
  iosClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com',
  // Scopes beyond profile + email
  scopes: ['profile', 'email'],
  // Required if you need offline access / refresh tokens on backend
  offlineAccess: true,
});

export async function signInWithGoogle() {
  try {
    await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });

    const userInfo = await GoogleSignin.signIn();
    const { idToken } = await GoogleSignin.getTokens();

    // Send idToken to your backend for verification
    // Never use the userInfo directly to create sessions
    return { userInfo, idToken };

  } catch (error) {
    if (error.code === statusCodes.SIGN_IN_CANCELLED) {
      return null; // user backed out — not an error
    } else if (error.code === statusCodes.IN_PROGRESS) {
      return null; // already signing in
    } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
      throw new Error('Google Play Services not available');
    }
    throw error;
  }
}

Step 5 — Silent sign-in for returning users

Don't make returning users tap the button every time. On app start, call GoogleSignin.signInSilently() — it restores the previous session using a cached refresh token without any UI. If it throws a statusCodes.SIGN_IN_REQUIRED error, show the sign-in button. This is also where you refresh the idToken, since Google's idTokens expire after 1 hour. Always call getTokens() after signInSilently() to get a fresh idToken before hitting your backend.

useSilentSignIn.js
import { useEffect } from 'react';
import { GoogleSignin, statusCodes } from '@react-native-google-signin/google-signin';
import { useDispatch } from 'react-redux';
import { setUser, clearUser } from '../store/authSlice';

export function useSilentSignIn() {
  const dispatch = useDispatch();

  useEffect(() => {
    async function trySilentSignIn() {
      try {
        const userInfo = await GoogleSignin.signInSilently();
        const { idToken } = await GoogleSignin.getTokens(); // fresh token
        dispatch(setUser({ userInfo, idToken }));
      } catch (error) {
        if (error.code === statusCodes.SIGN_IN_REQUIRED) {
          dispatch(clearUser()); // expected — show login screen
        }
      }
    }

    trySilentSignIn();
  }, []);
}

Backend token verification

On your server, verify the idToken using Google's tokeninfo endpoint or the google-auth-library. Never create a user session based on the client-side userInfo object alone — it can be spoofed. Verify the aud field matches your client ID and the email_verified field is true. If you passed offlineAccess: true during configure, the sign-in response also includes a serverAuthCode — exchange this on your backend for a refresh token, which lets your server call Google APIs on the user's behalf even when the app is closed.

Backend verification (Node.js)
import { OAuth2Client } from 'google-auth-library';

const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

export async function verifyGoogleToken(idToken) {
  const ticket = await client.verifyIdToken({
    idToken,
    audience: process.env.GOOGLE_CLIENT_ID,
  });

  const payload = ticket.getPayload();

  if (!payload.email_verified) {
    throw new Error('Email not verified by Google');
  }

  return {
    googleId: payload.sub,
    email: payload.email,
    name: payload.name,
    avatar: payload.picture,
  };
}

Common DEVELOPER_ERROR causes

DEVELOPER_ERROR on Android almost always means one of three things: the SHA-1 fingerprint registered in Firebase doesn't match the keystore you're building with (debug vs release), the package name in Firebase doesn't exactly match applicationId in build.gradle, or the google-services.json file is outdated and doesn't reflect recent Firebase console changes. Download a fresh copy of google-services.json after any Firebase console change — it's cached locally and won't auto-update.

Checklist before going to production: register both debug AND release SHA-1 fingerprints in Firebase, add the REVERSED_CLIENT_ID URL scheme in Xcode, verify idTokens on the server — never on the client, handle token expiry with signInSilently + getTokens() on app resume, and test sign-out + sign-in again to confirm the flow works after credential revocation.

Share X LinkedIn
NY
Neelesh Yadav
React Native Developer with 3 yr 6 mos years building production mobile apps. Writes about performance, architecture, and mobile engineering.
Follow ↗

Comments

Leave a comment

Comments are reviewed before they appear.

More Articles

Boosting React Native Performance: New Architectures Deep Dive
8 min read
Building Real-Time Chat with WebSocket in React Native
6 min read
AR Try-On: Three.js + MediaPipe in React Native
10 min read