Back to Blog

How to Add Google Authentication to Your React Native App?

A friendly step-by-step guide to implementing Google Sign-In with Firebase and Expo, complete with code examples and troubleshooting tips.

7 min read

Hey there, friend! In this guide, we'll walk together through integrating Google Authentication into your React Native app with the help of Firebase and Expo. Whether you're looking to spice up your app's login or simply interested in adding a highly reliable authentication method, you're in the right place!


Table of Contents


Introduction

Before diving deep, here's a quick overview of what we'll do:

  • Set up a Firebase project and configure both iOS and Android apps.
  • Configure Expo so that we can leverage dynamic settings.
  • Manage secrets securely with Expo Application Services (EAS).
  • Implement a Google Sign-In screen using a simple UI (we're keeping it clean and friendly!).
  • Build a development version of your app to test everything end-to-end.

Let's jump in and make your app even cooler with Google authentication!


Prerequisites

Make sure you have these ready:

  • Node.js & npm installed

  • Expo CLI installed:

    npm install -g expo-cli
    
    
  • A basic understanding of React Native and Expo

  • A Firebase account

  • Xcode (for iOS) and/or Android Studio (for Android)

You're all set? Great—let's continue!


Firebase Setup

1. Create a Firebase Project

2. Add iOS & Android Apps to Firebase

iOS Configuration

  1. Register your app with a bundle identifier (e.g., com.yourcompany.appname).
  2. Download the GoogleService-Info.plist file.
  3. (Don't worry about the extra setup—Expo will handle much of it for you.)

Android Configuration

  1. Register your app with a package name (e.g., com.yourcompany.appname).

  2. Generate the SHA-1 certificate using Expo EAS:

    eas build:configure
    
    
  3. Add the generated SHA-1 to your Firebase Android app configuration.

  4. Download the google-services.json file.

Tip: Keep these files out of your source control (we'll handle them securely in the next section).


Expo Configuration

Let's get Expo to work its magic with our Firebase credentials!

  1. Install Required Dependencies:

    npx expo install @react-native-google-signin/google-signin expo-dev-client
    
    
  2. Convert app.json to app.config.js:

    This allows you to dynamically reference environment variables.

    // app.config.js
    export default {
      expo: {
        ios: {
          bundleIdentifier: "com.yourcompany.appname",
          googleServicesFile: process.env.GOOGLE_SERVICES_INFOPLIST
        },
        android: {
          package: "com.yourcompany.appname",
          googleServicesFile: process.env.GOOGLE_SERVICES_JSON
        },
        plugins: [
          "expo-router",
          "@react-native-google-signin/google-signin"
        ]
      }
    };
    
    
  3. Configure EAS Development Builds:

    Create or update your eas.json:

    {
      "build": {
        "development": {
          "developmentClient": true,
          "distribution": "internal",
          "ios": {
            "simulator": true
          }
        }
      }
    }
    
    

Secret Management

Now, securely upload your Firebase configuration files using Expo's secret management:

# For Android
eas secret:create --scope project --name GOOGLE_SERVICES_JSON --type file --value /path/to/google-services.json

# For iOS
eas secret:create --scope project --name GOOGLE_SERVICES_INFOPLIST --type file --value /path/to/GoogleService-Info.plist

This way, your sensitive data stays safe, and you don't commit it to your repository.


Implementing the Authentication UI

Let's create a friendly authentication screen. Save this as src/screens/AuthScreen.tsx (or wherever you manage your screens):

// src/screens/AuthScreen.tsx
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { GoogleSignin, GoogleSigninButton } from '@react-native-google-signin/google-signin';
import { Card, CardContent, CardHeader, CardFooter, CardTitle, CardDescription } from '~/components/ui/card';
import { Avatar, AvatarImage, AvatarFallback } from '~/components/ui/avatar';
import { Button } from '~/components/ui/button';
import { Text } from '~/components/ui/text';

export default function AuthScreen() {
  const [user, setUser] = React.useState<SignInSuccessResponse | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [isLoading, setIsLoading] = React.useState(true);

  React.useEffect(() => {
    // Initialize Google Sign-In
    GoogleSignin.configure({
      webClientId: 'YOUR_WEB_CLIENT_ID',
      scopes: ['email']
    });

    // Check for existing sign-in
    checkCurrentUser();
  }, []);

  const checkCurrentUser = async () => {
    try {
      const isSignedIn = await GoogleSignin.hasPlayServices();
      if (isSignedIn) {
        const currentUser = await GoogleSignin.signInSilently();
        setUser(currentUser);
      }
    } catch (error) {
      if (error instanceof Error) {
        setError(error.message);
      }
    } finally {
      setIsLoading(false);
    }
  };

  const signIn = async () => {
    try {
      await GoogleSignin.hasPlayServices();
      const userInfo = await GoogleSignin.signIn();
      setUser(userInfo);
    } catch (e: any) {
      setError(e.toString());
    }
  };

  const signOut = async () => {
    try {
      await GoogleSignin.revokeAccess();
      await GoogleSignin.signOut();
      setUser(null);
    } catch (e: any) {
      setError(e.toString());
    }
  };

  return (
    <View style={styles.container}>
      <Card>
        <CardHeader>
          <CardTitle>Welcome!</CardTitle>
          <CardDescription>Please sign in with your Google account</CardDescription>
        </CardHeader>
        <CardContent>
          {!!error && <Text style={styles.errorText}>Error: {error}</Text>}
          {!user ? (
            <GoogleSigninButton onPress={signIn} />
          ) : (
            <>
              <Avatar>
                <AvatarImage source={{ uri: user.user.photo }} />
                <AvatarFallback>{user.user.name[0]}</AvatarFallback>
              </Avatar>
              <Text style={styles.welcomeText}>Hello, {user.user.name}!</Text>
              <Button onPress={signOut}>Sign Out</Button>
            </>
          )}
        </CardContent>
        <CardFooter>
          <Text>Happy coding! 🎉</Text>
        </CardFooter>
      </Card>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#f0f0f0'
  },
  errorText: {
    color: 'red',
    marginBottom: 10
  },
  welcomeText: {
    fontSize: 18,
    marginVertical: 10
  }
});

A few notes about the code:

  • Replace 'YOUR_WEB_CLIENT_ID' with the actual Web Client ID from your Firebase project configuration (found in your google-services.json under the oauth_client section).
  • The UI components (like Card, Avatar, Button, etc.) come from react-native-reusables library. Feel free to swap them out or style your own components as needed.

Building & Testing Your App

Now that your code is set, it's time to test it on your device:

  1. Create Development Builds:

    # For iOS
    eas build -p ios --profile development
    
    # For Android
    eas build -p android --profile development
    
    
  2. Install and Run Your Build:

    • iOS: Drag and drop the .app file into the iOS Simulator.
    • Android: Install the APK on your device or emulator.
  3. Start the Expo Development Server:

    expo start --dev-client
    
    

Make sure your development server is active when testing the builds.


Troubleshooting Tips

  • SHA-1 Mismatch (Android):

    If your authentication fails, verify that the generated SHA-1 certificate in your Expo EAS configuration matches the one in your Firebase Console.

  • iOS Simulator Issues:

    Double-check that you're using the correct GoogleService-Info.plist and that your bundle identifier is consistent with what you registered in Firebase.

  • Web Client ID Errors:

    It's common to use the wrong client ID. Make sure you pick the one corresponding to a client with the correct client_type from your Firebase config.

  • General Debugging:

    If something isn't working as expected, use console.log or even better, breakpoints to pinpoint the issue.


Wrapping Up

And there you have it! You've successfully set up Google Authentication in your React Native app using Firebase and Expo. Not only have you made your app more robust, but you've also taken another step in becoming an even more awesome developer.

If you enjoyed this guide or have any questions, feel free to leave a comment or reach out. I'm always here to help out a fellow coder. Happy coding, and see you in the next tutorial!


Persisting Authentication State

To maintain the user's authentication state across app restarts, update your authentication screen to check for existing sessions:

export default function AuthScreen() {
  const [user, setUser] = React.useState<SignInSuccessResponse | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [isLoading, setIsLoading] = React.useState(true);

  React.useEffect(() => {
    // Initialize Google Sign-In
    GoogleSignin.configure({
      webClientId: 'YOUR_WEB_CLIENT_ID',
      scopes: ['email']
    });

    // Check for existing sign-in
    checkCurrentUser();
  }, []);

  const checkCurrentUser = async () => {
    try {
      const isSignedIn = await GoogleSignin.hasPlayServices();
      if (isSignedIn) {
        const currentUser = await GoogleSignin.signInSilently();
        setUser(currentUser);
      }
    } catch (error) {
      if (error instanceof Error) {
        setError(error.message);
      }
    } finally {
      setIsLoading(false);
    }
  };

  // ... rest of your component code ...
}

Key points about persistence:

  • Use GoogleSignin.hasPlayServices() to check if Google Play Services is available and the user was previously signed in
  • Use GoogleSignin.signInSilently() to restore the previous session without user interaction
  • Add a loading state to prevent UI flicker while checking authentication status

This approach will automatically restore the user's session when they reopen your app, providing a seamless experience.