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
-
Go to your Firebase Console.
-
Create Project
-
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.
-
Select your project → Build → App Check from the left menu.
-
Click Get Started.
-
Choose your React Native app from the list (make sure it’s registered with the correct package name).
-
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
-
For the provider, select reCAPTCHA v3 in the Google Admin Console.
-
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.
-
In your
firebase.js
, before callinginitializeAppCheck
:// Enable debug mode (only in dev builds, never in production!) if (__DEV__) { self.FIREBASE_APPCHECK_DEBUG_TOKEN = true; }
-
Run your app once and check the browser/dev logs. You’ll see:
AppCheck debug token: "YOUR_DEBUG_TOKEN"
-
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:
-
Go to Firebase Console → Project Settings → Your Apps.
-
Click Add App → Android.
-
Enter your Android package name (e.g.,
com.djamware.myapp
), nickname, and SHA-1 key. -
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:
-
Go to Firebase Console → Project Settings → Your Apps.
-
Click Add App → iOS.
-
Enter your app’s Bundle ID (e.g.,
com.djamware.myapp
). -
Download
GoogleService-Info.plist
and place it in:ios/GoogleService-Info.plist
Step 2: Enable iOS App Check
-
Go to Firebase Console → App Check.
-
Select your iOS app.
-
Click Register App.
-
Choose DeviceCheck or App Attest.
-
If you're targeting iOS 14+, prefer App Attest
-
Otherwise, use DeviceCheck
-
-
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
-
Run your app and watch App Check logs in the Firebase Console.
-
Try disabling App Check temporarily in the console and observe failures.
-
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:
- Master React Native Animations
- Advanced React Native Topics
- React Native
- Learning React Native Development
- React: React Native Mobile Development: 3-in-1
Thanks!