Secure Your React Native App with Firebase App Check and reCAPTCHA

by Didin J. on Jun 27, 2025 Secure Your React Native App with Firebase App Check and reCAPTCHA

Secure your React Native app using Firebase App Check and reCAPTCHA v3 to block unauthorized access and protect Firestore, Storage, and Cloud Functions.

In today's mobile-first world, securing your app against abuse and unauthorized access is just as important as building features your users love. With the rise of bots, spoofed devices, and misuse of backend resources, it’s critical to ensure only genuine users interact with your Firebase-powered services. This is where Firebase App Check comes in.

Firebase App Check helps protect your app's resources—like Firestore, Realtime Database, Cloud Functions, and Storage—by ensuring that incoming requests are from your authentic app. When paired with reCAPTCHA for React Native apps, App Check adds a layer of security that verifies the integrity of the client application without compromising the user experience.

In this tutorial, we’ll walk you through how to integrate Firebase App Check with reCAPTCHA into your React Native app using the latest tools and best practices in 2025. By the end, your app will be protected from unwanted access while maintaining a seamless user experience.

Prerequisites

Before starting, make sure you have the following:

  • A working React Native project (Expo or bare workflow)

  • Firebase project set up in the Firebase Console

  • Firebase SDKs configured in your app (e.g., for Authentication, Firestore, etc.)

  • The latest versions of:

    • firebase (v10+)

    • @react-native-firebase/app (for bare workflow)

    • Node.js v20+


Enable App Check in the Firebase Console

  1. Go to your Firebase Console.

  2. Create Project

  3. Add iOS and Android applications with the package name and bundle ID the same as your iOS and Android applications that you will create next.

  4. Select your project → Build → App Check from the left menu.

  5. Click Get Started.

  6. Choose your React Native app from the list (make sure it’s registered with the correct package name).

  7. Click Play Integrity, then add the SHA-256 certificate fingerprint from your computer. 

    keytool -list -v \
    -alias androiddebugkey -keystore ~/.android/debug.keystore

    The default password is "android". Copy and paste the SHA256 fingerprint.

    DA:39:A3:EE:5E:6B:4B:0D:32:55:BF:EF:95:60:18:90:AF:D8:07:09
  8. For the provider, select reCAPTCHA v3 in the Google Admin Console.

  9. Click Register and copy the generated Site Key. You’ll need this soon.


Create React Native, install Firebase SDK, and App Check dependencies

Create a React Native application using React Native Community CLI.

npx @react-native-community/cli init RNAppCheck
cd RNAppCheck

Install the following dependencies:

npm install @react-native-firebase/app @react-native-firebase/auth

Or with Yarn:

yarn add @react-native-firebase/app @react-native-firebase/auth


Configure Firebase in Your App

Create a firebase.js (or firebaseConfig.js) file to initialize Firebase:

// firebase.js
import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';

const firebaseConfig = {
  apiKey: 'YOUR_API_KEY',
  authDomain: 'YOUR_PROJECT.firebaseapp.com',
  projectId: 'YOUR_PROJECT_ID',
  storageBucket: 'YOUR_PROJECT.appspot.com',
  messagingSenderId: 'SENDER_ID',
  appId: 'APP_ID',
  measurementId: 'G-MEASURE_ID',
};

const app = initializeApp(firebaseConfig);

// Initialize App Check with reCAPTCHA
const appCheck = initializeAppCheck(app, {
  provider: new ReCaptchaV3Provider('YOUR_RECAPTCHA_SITE_KEY'),
  isTokenAutoRefreshEnabled: true, // Optional but recommended
});

export default app;

To get the Firebase config values:

  • Go to the Firebase Console.

  • Select your Firebase project.

  • In the left menu, go to Project Settings (⚙️ gear icon at the top-left).

  • Scroll down to the Your apps section.

  • If you already added your app (Android or iOS), click it. If not:

    • Click Add App → Select Web (</>) to generate firebaseConfig.

  • After selecting or registering the app, you'll see a code snippet like this:

    const firebaseConfig = {
      apiKey: "AIzaSyAxxx...your_key...",
      authDomain: "your-project-id.firebaseapp.com",
      projectId: "your-project-id",
      storageBucket: "your-project-id.appspot.com",
      messagingSenderId: "1234567890",
      appId: "1:1234567890:web:abcdef123456",
      measurementId: "G-XXXXXXX" // Optional
    };

     


Add the reCAPTCHA Meta Tag (WebView Context)

If you are using React Native WebView (or using Expo Web), ensure the following meta tag is available in your web context (for example, if rendering a WebView that loads Firebase):

<meta name="google-site-verification" content="YOUR_RECAPTCHA_SITE_KEY" />

For native apps, Firebase automatically handles loading the reCAPTCHA challenge.


Protect Firebase Services

After App Check is initialized, head back to the Firebase Console → App Check → Settings and enable protection for:

  • Firestore

  • Realtime Database

  • Cloud Functions

  • Cloud Storage
    (Whichever services your app uses)

⚠️ Warning: Once enabled, unauthorized requests will be blocked. Make sure your app is properly initializing App Check before enabling enforcement in production.


Verify App Check is Working

You can confirm App Check is working by:

  • Going to Firebase Console → App Check → Requests

  • You should see live traffic after the app is opened

If you don’t see any data:

  • Double-check your Site Key

  • Ensure App Check is initialized before using any protected Firebase services


Handling Firebase App Check Errors in React Native

Once App Check enforcement is enabled, Firebase will reject requests from clients that fail attestation. This means your app must gracefully handle cases where App Check fails due to:

  • Invalid or missing reCAPTCHA token

  • Network issues

  • Misconfigured App Check setup

  • Debug/dev environments (if debug mode is not enabled)

Here’s how you can handle these scenarios in React Native:

1. Catch App Check Initialization Errors

Wrap the initialization in a try/catch block for better error visibility:

import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';

try {
  const appCheck = initializeAppCheck(app, {
    provider: new ReCaptchaV3Provider('YOUR_RECAPTCHA_SITE_KEY'),
    isTokenAutoRefreshEnabled: true,
  });
} catch (error) {
  console.warn('Failed to initialize App Check:', error.message);
}

2. Handle App Check Token Errors When Calling Firebase Services

When you make calls to Firestore, Storage, or Cloud Functions, failures due to App Check will typically return a 403 (PERMISSION_DENIED) or 401 (UNAUTHENTICATED).

Example for Firestore:

import { getFirestore, collection, getDocs } from 'firebase/firestore';

const fetchData = async () => {
  try {
    const db = getFirestore();
    const querySnapshot = await getDocs(collection(db, 'items'));
    querySnapshot.forEach(doc => console.log(doc.data()));
  } catch (error) {
    if (error.code === 'permission-denied') {
      Alert.alert('Access Denied', 'App Check verification failed.');
    } else {
      Alert.alert('Error', error.message);
    }
    console.error('Firestore fetch failed:', error);
  }
};

3. Enable Debug Mode in Development

In development, your app may not pass App Check automatically. To avoid being blocked during testing, use debug tokens.

  1. In your firebase.js, before calling initializeAppCheck

    // Enable debug mode (only in dev builds, never in production!)
    if (__DEV__) {
      self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
    }
  2. Run your app once and check the browser/dev logs. You’ll see: 

    AppCheck debug token: "YOUR_DEBUG_TOKEN"
  3. Go to Firebase Console → App Check → Debug Tokens, add this token manually, and give it a name like React Native Dev.

This will allow development requests to pass App Check until you're ready for production.

4. Don’t Show reCAPTCHA to Users

Firebase App Check with reCAPTCHA v3 is invisible by design. However, if you misconfigure the site key or App Check doesn’t load properly, the app may silently fail. Always log errors and display fallback messages when needed:

if (!appCheckToken) {
  Alert.alert('Security Check Failed', 'Please restart the app or try again later.');
}


Registering Android and iOS Apps for Firebase App Check

Firebase App Check supports different attestation providers per platform:

  • Android: SafetyNet (deprecated), Play Integrity API (recommended)

  • iOS: DeviceCheck or App Attest

Here’s how to register each.

Android: Register with Play Integrity

🔐 Recommended: Use Play Integrity API instead of deprecated SafetyNet.

Step 1: Add Your Android App to Firebase

If not already added:

  1. Go to Firebase Console → Project Settings → Your Apps.

  2. Click Add App → Android.

  3. Enter your Android package name (e.g., com.djamware.myapp), nickname, and SHA-1 key.

  4. Download the google-services.json file and place it in: 

    android/app/google-services.json

     

Step 2: Enable Play Integrity for App Check

  • Go to Firebase Console → App Check.

  • Select your Android app.

  • Click Register App.

  • Choose Play Integrity as the provider.

  • Click Save.

✅ If prompted to enable Play Integrity API in Google Cloud Console, do so.

Step 3: Initialize App Check in Code

Firebase automatically handles this when using the JS SDK. Just ensure initializeAppCheck() is called (as we covered earlier), and your Android app will now verify using Play Integrity.

iOS: Register with DeviceCheck or App Attest

💡 App Attest is more secure but requires iOS 14+. Use DeviceCheck for wider compatibility.

Step 1: Add Your iOS App to Firebase

If not already added:

  1. Go to Firebase Console → Project Settings → Your Apps.

  2. Click Add App → iOS.

  3. Enter your app’s Bundle ID (e.g., com.djamware.myapp).

  4. Download GoogleService-Info.plist and place it in: 

    ios/GoogleService-Info.plist

Step 2: Enable iOS App Check

  1. Go to Firebase Console → App Check.

  2. Select your iOS app.

  3. Click Register App.

  4. Choose DeviceCheck or App Attest.

    • If you're targeting iOS 14+, prefer App Attest

    • Otherwise, use DeviceCheck

  5. Click Save

Step 3: Initialize App Check in Code

Just like Android, initialization is the same on the React Native side with the JS SDK. Firebase handles native attestation behind the scenes.

Optional: Use App Check Debug Mode in Emulators/Simulators

Since Play Integrity and App Attest require real devices, use debug tokens during development.

Add this before initializeAppCheck():

if (__DEV__) {
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}

Then copy the token from logs and register it in Firebase Console → App Check → Debug Tokens.


Full Working Example: React Native + Firestore + App Check

Goal

Create a simple React Native screen that:

  • Fetches a list of documents from Firestore

  • Displays them in a FlatList

  • Fails gracefully if App Check blocks the request

Firebase Setup Recap

Make sure you’ve already:

  • Initialized Firebase and App Check with initializeAppCheck()

  • Protected Firestore in Firebase Console → App Check → Firestore → Enforce

  • Added firebaseConfig with your Site Key and credentials

Example Code: Fetching Firestore Data with App Check

firebase.js

import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';

const firebaseConfig = {
  apiKey: 'YOUR_API_KEY',
  authDomain: 'your-project-id.firebaseapp.com',
  projectId: 'your-project-id',
  storageBucket: 'your-project-id.appspot.com',
  messagingSenderId: 'SENDER_ID',
  appId: 'APP_ID',
};

const app = initializeApp(firebaseConfig);

if (__DEV__) {
  // Enable debug mode for dev builds
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}

initializeAppCheck(app, {
  provider: new ReCaptchaV3Provider('YOUR_RECAPTCHA_SITE_KEY'),
  isTokenAutoRefreshEnabled: true,
});

export default app;

FirestoreScreen.js

import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator, Alert } from 'react-native';
import { getFirestore, collection, getDocs } from 'firebase/firestore';
import app from './firebase';

const db = getFirestore(app);

export default function FirestoreScreen() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchItems = async () => {
    try {
      const querySnapshot = await getDocs(collection(db, 'items')); // Replace 'items' with your collection
      const data = [];
      querySnapshot.forEach(doc => {
        data.push({ id: doc.id, ...doc.data() });
      });
      setItems(data);
    } catch (error) {
      console.error('Error fetching data:', error);
      if (error.code === 'permission-denied') {
        Alert.alert('Access Denied', 'App Check verification failed.');
      } else {
        Alert.alert('Error', error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchItems();
  }, []);

  if (loading) {
    return <ActivityIndicator size="large" style={styles.loader} />;
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Items</Text>
      <FlatList
        data={items}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text>{item.name}</Text> {/* Adjust to your Firestore schema */}
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16 },
  title: { fontSize: 24, fontWeight: 'bold', marginBottom: 12 },
  item: { padding: 10, borderBottomWidth: 1, borderBottomColor: '#ccc' },
  loader: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});

Firestore Rules (Optional but Recommended)

To lock down Firestore and require App Check:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /items/{document} {
      allow read: if request.app != null; // Require App Check
    }
  }
}

Test It

  1. Run your app and watch App Check logs in the Firebase Console.

  2. Try disabling App Check temporarily in the console and observe failures.

  3. Try testing on an emulator with a debug token or on a real device with reCAPTCHA.


Conclusion

Securing your React Native app is more critical than ever in today’s landscape of automated abuse and unauthorized access. With Firebase App Check and reCAPTCHA v3, you can add a robust layer of protection to ensure only genuine, verified instances of your app can access your backend resources.

In this tutorial, you learned how to:

  • Enable App Check with reCAPTCHA in the Firebase Console

  • Integrate App Check into your React Native app using the Firebase JS SDK

  • Register Android and iOS apps with native attestation providers

  • Handle errors and debug issues during development

  • Secure Firestore with App Check and build a fully working data-fetching screen

With this setup in place, your Firebase services are now shielded from abuse while still delivering a seamless user experience. As your app grows, consider pairing App Check with other Firebase security tools like Authentication, Firestore rules, and Cloud Function IAM policies for complete, end-to-end protection.

You can find the full source code on our GitHub.

That's just the basics. If you need more deep learning about React.js, React Native, or related, you can take the following cheap course:

Thanks!