Implementing Authentication, Navigation, and Firebase Integration in React Native
Course: React Native Training
Topic: Implementing Authentication, Navigation, and Firebase Integration
Course Link: React Native Training at JustAcademy
Demo Link: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction
Authentication, navigation, and Firebase integration are essential parts of a modern React Native application. Authentication verifies a user's identity, navigation controls movement between application screens, and Firebase provides backend services such as user authentication, databases, file storage, and application monitoring.
These features work together to create applications in which users can register, sign in, access protected screens, manage their profiles, and securely interact with cloud data according to their permissions.
For example, a student management application may allow students to sign in and view their profiles, while authorized administrators can manage student records. An employee directory may allow employees to view colleagues while restricting editing and deletion to administrators.
1.1 What You Will Build
This module explains how to build a React Native application with the following capabilities:
- User registration using email and password.
- User sign-in and sign-out.
- Firebase Authentication integration.
- Navigation between authentication and application screens.
- Protected screens that are available only to authenticated users.
- Authentication-state persistence across app restarts.
- Cloud data storage using Firebase Firestore.
- Basic user profile management.
- Loading indicators and error handling.
- Security rules for protecting user data.
2. Understanding Authentication
Authentication is the process of verifying that a user is who they claim to be. In a mobile application, users commonly authenticate through an email and password, a phone number, or an identity provider such as Google.
2.1 Why Authentication Is Important
- Restricts access to private application screens.
- Identifies the current user.
- Allows users to access their own profiles and records.
- Supports account creation and account recovery.
- Helps personalize the application experience.
- Provides an identity that backend security rules can use when authorizing requests.
2.2 Authentication vs. Authorization
| Authentication |
Authorization |
| Verifies the user's identity. |
Determines what the user is permitted to do. |
| Answers: Who are you? |
Answers: What can you access? |
| Example: Signing in with email and password. |
Example: Allowing only an administrator to delete student records. |
A successful login does not automatically make a user an administrator. Authorization must be implemented separately through trusted backend logic, database rules, and carefully defined application permissions.
3. Understanding Navigation in React Native
Navigation allows users to move between screens. React Native provides the building blocks for user interfaces, while a navigation library such as React Navigation manages routes, navigation history, and screen transitions.
3.1 Common Navigation Types
| Navigation Type |
Description |
Example |
| Stack Navigation |
Places screens in a navigable stack. |
Login to Home to Profile. |
| Tab Navigation |
Provides persistent navigation tabs. |
Home, Directory, and Settings. |
| Drawer Navigation |
Displays a side navigation menu. |
Dashboard, Employees, Reports. |
| Conditional Navigation |
Displays different screens based on application state. |
Login screen when signed out and Home when signed in. |
3.2 Authentication-Based Navigation
Authentication-based navigation separates the app into two main screen groups:
- Authentication screens: Sign In, Sign Up, Forgot Password, and Reset Password.
- Application screens: Home, Profile, Dashboard, Directory, and Settings.
When the user signs in, the application switches to the authenticated screen group. When the user signs out, it returns to the authentication screen group. This prevents users from navigating back to private screens through ordinary navigation history after signing out.
4. Introduction to Firebase
Firebase is Google's application development platform. It provides managed services that help developers build applications without implementing every backend feature from scratch.
4.1 Important Firebase Services
| Service |
Purpose |
Example Use |
| Firebase Authentication |
Manages user identity and authentication. |
Registering and signing in users. |
| Cloud Firestore |
Stores structured documents in collections. |
Saving user profiles and directory records. |
| Realtime Database |
Stores JSON data with realtime synchronization. |
Applications requiring realtime updates. |
| Cloud Storage for Firebase |
Stores files such as images and documents. |
Uploading profile photographs. |
| Cloud Messaging |
Supports push messaging. |
Sending application notifications. |
| Crashlytics |
Collects crash reports. |
Investigating production crashes. |
For this project, Firebase Authentication manages accounts and Cloud Firestore stores application records. Other services can be added when the application requires them.
5. Project Requirements and Setup
5.1 Required Tools
- Node.js and npm.
- Expo or a React Native development environment.
- Android emulator, iOS simulator, or a compatible physical device.
- A Google account to create a Firebase project.
- A code editor such as Visual Studio Code.
- Internet access during installation and Firebase configuration.
5.2 Create a React Native Project with Expo
npx create-expo-app SecureDirectoryApp
cd SecureDirectoryApp
npx expo start
This creates a starter application and starts the development server. Follow the terminal instructions to open the app on a simulator or supported device.
5.3 Install React Navigation
npm install @react-navigation/native
npm install @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
Use the installation instructions for the navigation version in your project. If you choose a different navigation library or a different React Native setup, the installation steps may vary.
5.4 Install Firebase and AsyncStorage
npm install firebase
npx expo install @react-native-async-storage/async-storage
This example uses the Firebase JavaScript SDK. AsyncStorage is used as the persistence adapter for Firebase Authentication on React Native when configured with the compatible SDK version.
6. Creating a Firebase Project
Before connecting Firebase to React Native, create and configure a Firebase project.
6.1 Create the Project
- Open the Firebase Console.
- Sign in with your Google account.
- Create a new Firebase project.
- Follow the setup steps shown in the console.
- Add an application to the project using the appropriate configuration for your development setup.
- Open the project settings and locate the Firebase application configuration.
6.2 Enable Authentication
- Open the Firebase Console.
- Select Authentication from the Build section.
- Open the sign-in method settings.
- Enable Email/Password authentication.
- Save the configuration.
Firebase Authentication will now be able to process email-and-password registration and sign-in requests for the configured project.
6.3 Create a Firestore Database
- Open Firestore Database in the Firebase Console.
- Create the database for your project.
- Choose the appropriate deployment region.
- Configure database security rules before using real user data.
Do not leave production data accessible through broad test rules. Use a development-only configuration while learning and replace it with restrictive rules before deployment.
7. Organizing the Application Files
A well-organized folder structure makes authentication, navigation, and Firebase code easier to maintain.
SecureDirectoryApp/
├── App.js
├── src/
│ ├── firebase/
│ │ └── firebaseConfig.js
│ ├── context/
│ │ └── AuthContext.js
│ ├── navigation/
│ │ └── RootNavigator.js
│ ├── screens/
│ │ ├── LoginScreen.js
│ │ ├── RegisterScreen.js
│ │ ├── ForgotPasswordScreen.js
│ │ ├── HomeScreen.js
│ │ └── ProfileScreen.js
│ └── services/
│ └── userService.js
For a small demonstration, some of these files can be combined. In larger applications, separating navigation, authentication state, Firebase configuration, and screen UI helps prevent duplicated logic.
8. Configuring Firebase in React Native
The Firebase configuration connects the application to the Firebase project. The web configuration values identify the project and app; Firebase Authentication and Firestore security are enforced by Firebase services and their rules.
8.1 Create the Configuration File
Create src/firebase/firebaseConfig.js and add the Firebase project configuration obtained from the Firebase Console.
import { initializeApp, getApp, getApps } from "firebase/app";
import {
initializeAuth,
getReactNativePersistence
} from "firebase/auth";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "YOUR_FIREBASE_API_KEY",
authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_PROJECT_ID.firebasestorage.app",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_FIREBASE_APP_ID"
};
const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
let auth;
try {
auth = initializeAuth(app, {
persistence: getReactNativePersistence(AsyncStorage)
});
} catch (error) {
if (error.code === "auth/already-initialized") {
const { getAuth } = require("firebase/auth");
auth = getAuth(app);
} else {
throw error;
}
}
const db = getFirestore(app);
export { app, auth, db };
Important implementation note: Replace the placeholder configuration values with the values from your Firebase project. Do not copy the placeholders literally. Firebase SDK initialization should occur once. In a modular JavaScript project, it is often cleaner to initialize the app and authentication instance in one module and import the resulting instances elsewhere.
The exact AsyncStorage import and persistence adapter depend on the versions of the Firebase JavaScript SDK and AsyncStorage in your project. If your installed Firebase version does not expose getReactNativePersistence as shown, follow the current Firebase Authentication documentation for the compatible React Native setup. Avoid initializing Firebase Auth twice.
8.2 Understanding the Configuration
- apiKey: Identifies the Firebase project for client SDK requests; it is not a substitute for database security rules.
- authDomain: Used by Firebase authentication flows that rely on the configured domain.
- projectId: Identifies the Firebase project.
- storageBucket: Identifies the project's Cloud Storage bucket where applicable.
- messagingSenderId: Identifies the Firebase Cloud Messaging sender project.
- appId: Identifies the registered Firebase application.
Firebase client configuration is not equivalent to a server secret. Never put service-account private keys, administrative credentials, or private backend secrets inside a mobile application.
9. Understanding Firebase Authentication Methods
Firebase Authentication provides SDK methods for common authentication workflows. The following table lists methods used in this module.
| Method |
Purpose |
| createUserWithEmailAndPassword |
Creates an account using an email address and password. |
| signInWithEmailAndPassword |
Signs in an existing account. |
| signOut |
Signs out the current user. |
| onAuthStateChanged |
Observes changes to the signed-in user. |
| sendPasswordResetEmail |
Sends a password-reset email. |
| sendEmailVerification |
Sends an email verification message to the current user. |
| updateProfile |
Updates supported profile properties such as display name or photo URL. |
10. Implementing User Registration
Registration creates a new Firebase Authentication account. The application should validate the input, call Firebase, and display a useful result.
10.1 Registration Workflow
- The user opens the registration screen.
- The user enters a name, email address, and password.
- The application validates required fields.
- The application submits the credentials to Firebase Authentication.
- Firebase creates the account if the request is valid and permitted.
- The app displays an appropriate result.
- The authentication-state listener updates the application when the user becomes signed in.
10.2 Example Registration Function
import {
createUserWithEmailAndPassword,
updateProfile,
sendEmailVerification
} from "firebase/auth";
import { auth } from "../firebase/firebaseConfig";
export async function registerUser(name, email, password) {
const cleanName = name.trim();
const cleanEmail = email.trim().toLowerCase();
if (!cleanName || !cleanEmail || !password) {
throw new Error("Complete all required fields.");
}
if (password.length < 8) {
throw new Error("Use a password with at least 8 characters.");
}
const credential = await createUserWithEmailAndPassword(
auth,
cleanEmail,
password
);
await updateProfile(credential.user, {
displayName: cleanName
});
await sendEmailVerification(credential.user);
return credential.user;
}
This example demonstrates a basic email/password registration workflow. Firebase's password policy, email verification settings, and any organization-specific requirements should be configured according to the application's needs.
10.3 Registration Best Practices
- Use a suitable email keyboard on mobile devices.
- Provide a show/hide password control where appropriate.
- Validate required fields before sending a request.
- Handle errors from Firebase rather than assuming registration succeeded.
- Prevent accidental repeated submissions while a request is in progress.
- Do not log passwords or include them in analytics events.
- Require email verification when the application needs verified email identities.
11. Implementing User Login
Login allows an existing user to authenticate with their registered credentials. Firebase checks the credentials and returns a user credential when authentication succeeds.
11.1 Login Function
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase/firebaseConfig";
export async function loginUser(email, password) {
const cleanEmail = email.trim().toLowerCase();
if (!cleanEmail || !password) {
throw new Error("Enter your email and password.");
}
const credential = await signInWithEmailAndPassword(
auth,
cleanEmail,
password
);
return credential.user;
}
11.2 Login Screen Example
import React, { useState } from "react";
import {
View,
Text,
TextInput,
Pressable,
ActivityIndicator,
Alert,
StyleSheet
} from "react-native";
import { loginUser } from "../services/authService";
export default function LoginScreen({ navigation }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const handleLogin = async () => {
if (!email.trim() || !password) {
Alert.alert("Missing information", "Enter your email and password.");
return;
}
try {
setLoading(true);
await loginUser(email, password);
} catch (error) {
Alert.alert("Sign-in failed", "Check your details and try again.");
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.heading}>Welcome Back</Text>
<TextInput
style={styles.input}
placeholder="Email address"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
accessibilityLabel="Email address"
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
autoCapitalize="none"
accessibilityLabel="Password"
/>
<Pressable
style={styles.button}
onPress={handleLogin}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#FFFFFF" />
) : (
<Text style={styles.buttonText}>Sign In</Text>
)}
</Pressable>
<Pressable onPress={() => navigation.navigate("Register")}>
<Text style={styles.link}>Create an account</Text>
</Pressable>
<Pressable onPress={() => navigation.navigate("ForgotPassword")}>
<Text style={styles.link}>Forgot password?</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 24,
backgroundColor: "#F5F7FA"
},
heading: {
fontSize: 28,
fontWeight: "700",
marginBottom: 24
},
input: {
borderWidth: 1,
borderColor: "#CDD4DC",
backgroundColor: "#FFFFFF",
borderRadius: 8,
padding: 13,
marginBottom: 14
},
button: {
backgroundColor: "#2457C5",
padding: 14,
borderRadius: 8,
alignItems: "center"
},
buttonText: {
color: "#FFFFFF",
fontWeight: "700"
},
link: {
marginTop: 18,
color: "#2457C5"
}
});
This screen calls the login service and does not manually navigate to the Home screen after success. The root authentication listener should control which screen group is displayed. This avoids inconsistent navigation when the authentication state changes asynchronously.
12. Handling Authentication Errors
Authentication can fail for several reasons, including invalid credentials, disabled accounts, network problems, or too many requests.
| Error Situation |
Recommended Handling |
| Missing email or password |
Ask the user to complete the required fields. |
| Invalid credentials |
Display a neutral sign-in failure message. |
| Email already in use |
Explain that the address may already have an account and provide a sign-in option. |
| Weak password |
Explain the password requirements. |
| Network failure |
Offer a retry option. |
| Too many requests |
Ask the user to wait before retrying. |
| Account disabled |
Explain that access is unavailable and provide an appropriate support route. |
Firebase error codes and account-enumeration protections can vary according to project configuration. Avoid exposing unnecessary account details or raw technical error messages to end users.
13. Implementing Password Reset
A password-reset flow allows users to recover access to an account without asking an administrator to know or manually change their password.
13.1 Password Reset Function
import { sendPasswordResetEmail } from "firebase/auth";
import { auth } from "../firebase/firebaseConfig";
export async function requestPasswordReset(email) {
const cleanEmail = email.trim().toLowerCase();
if (!cleanEmail) {
throw new Error("Enter your email address.");
}
await sendPasswordResetEmail(auth, cleanEmail);
}
13.2 Password Reset Screen Behavior
- Ask the user for their email address.
- Validate that the field is not empty.
- Call the password-reset function.
- Display a neutral confirmation message.
- Handle network or request errors gracefully.
- Do not request or store the user's existing password.
Use neutral confirmation wording where appropriate so the screen does not unnecessarily reveal whether an account exists.
14. Implementing Sign-Out
Sign-out ends the user's authenticated session. The app should respond to Firebase's authentication-state change rather than only navigating to the login screen.
14.1 Sign-Out Function
import { signOut } from "firebase/auth";
import { auth } from "../firebase/firebaseConfig";
export async function logoutUser() {
await signOut(auth);
}
14.2 Add a Sign-Out Button
import React, { useState } from "react";
import {
Pressable,
Text,
Alert,
ActivityIndicator
} from "react-native";
import { logoutUser } from "../services/authService";
export default function SignOutButton() {
const [loading, setLoading] = useState(false);
const handleSignOut = async () => {
try {
setLoading(true);
await logoutUser();
} catch (error) {
Alert.alert("Unable to sign out", "Please try again.");
} finally {
setLoading(false);
}
};
return (
<Pressable onPress={handleSignOut} disabled={loading}>
{loading ? (
<ActivityIndicator />
) : (
<Text>Sign Out</Text>
)}
</Pressable>
);
}
When Firebase reports that the user is signed out, the root navigator should display the authentication screens. Do not simply hide a button or navigate away while leaving private data accessible elsewhere in the app.
15. Persisting Authentication State
Authentication persistence allows users to remain signed in after restarting the application, depending on the configured persistence mechanism and session state.
For the Firebase JavaScript SDK in React Native, configure Firebase Auth with the appropriate React Native persistence adapter. Do not assume that manually saving a user ID or a Boolean value such as isLoggedIn is sufficient to authenticate a user.
15.1 Why Use onAuthStateChanged?
The onAuthStateChanged listener observes when Firebase determines that a user has signed in or out. It also supports the initial restoration of authentication state when the application starts.
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "../firebase/firebaseConfig";
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
console.log("Authenticated user:", user.uid);
} else {
console.log("No authenticated user");
}
});
// Call unsubscribe when the listener is no longer needed.
Use the listener in a React effect or an authentication provider. Avoid registering a new listener on every render, and unsubscribe when the component unmounts.
16. Creating an Authentication Context
React Context allows multiple screens to access shared authentication information without passing the same props through every component. It can expose the current Firebase user and a loading flag.
16.1 Example AuthContext
import React, {
createContext,
useContext,
useEffect,
useState
} from "react";
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "../firebase/firebaseConfig";
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [initializing, setInitializing] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
setInitializing(false);
});
return unsubscribe;
}, []);
return (
<AuthContext.Provider value={{ user, initializing }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
}
16.2 Understanding the Context
- user: Contains the current Firebase user when signed in, or null when signed out.
- initializing: Indicates whether the initial authentication state is still being determined.
- AuthProvider: Makes the authentication state available to child components.
- useAuth: Provides a reusable hook for reading authentication context.
During initialization, show a loading screen instead of briefly displaying the wrong navigation group.
17. Implementing Authentication-Based Navigation
The root navigator should render one screen group when the user is signed out and another when the user is signed in.
17.1 Root Navigator Example
import React from "react";
import { ActivityIndicator, View } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { useAuth } from "../context/AuthContext";
import LoginScreen from "../screens/LoginScreen";
import RegisterScreen from "../screens/RegisterScreen";
import ForgotPasswordScreen from "../screens/ForgotPasswordScreen";
import HomeScreen from "../screens/HomeScreen";
import ProfileScreen from "../screens/ProfileScreen";
const Stack = createNativeStackNavigator();
export default function RootNavigator() {
const { user, initializing } = useAuth();
if (initializing) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<NavigationContainer>
<Stack.Navigator>
{user ? (
<>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: "Dashboard" }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{ title: "My Profile" }}
/>
</>
) : (
<>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ title: "Sign In" }}
/>
<Stack.Screen
name="Register"
component={RegisterScreen}
options={{ title: "Create Account" }}
/>
<Stack.Screen
name="ForgotPassword"
component={ForgotPasswordScreen}
options={{ title: "Reset Password" }}
/>
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
This conditional navigator pattern keeps the authentication and private screen groups separate. Confirm that the screen imports and route names match the actual files in your project.
17.2 Connect the Provider in App.js
import React from "react";
import { AuthProvider } from "./src/context/AuthContext";
import RootNavigator from "./src/navigation/RootNavigator";
export default function App() {
return (
<AuthProvider>
<RootNavigator />
</AuthProvider>
);
}
17.3 Why Not Navigate Manually After Login?
Firebase authentication changes are asynchronous. If the login screen manually navigates to Home while the root navigation state still represents a signed-out user, the app can enter an inconsistent state.
Instead, let Firebase update the authentication state and allow the root navigator to render the correct screen group. This also makes sign-out, restored sessions, and account changes easier to manage.
18. Building the Home Screen
The Home screen is the starting point for authenticated users. It can show a welcome message, navigation options, a profile link, and a sign-out action.
import React from "react";
import { View, Text, Pressable, StyleSheet } from "react-native";
import { useAuth } from "../context/AuthContext";
import SignOutButton from "../components/SignOutButton";
export default function HomeScreen({ navigation }) {
const { user } = useAuth();
return (
<View style={styles.container}>
<Text style={styles.heading}>Welcome</Text>
<Text style={styles.email}>
{user?.email || "Signed-in user"}
</Text>
<Pressable
style={styles.button}
onPress={() => navigation.navigate("Profile")}
>
<Text style={styles.buttonText}>View Profile</Text>
</Pressable>
<SignOutButton />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
justifyContent: "center"
},
heading: {
fontSize: 26,
fontWeight: "700"
},
email: {
marginVertical: 12
},
button: {
padding: 14,
borderRadius: 8,
backgroundColor: "#2457C5",
marginBottom: 16
},
buttonText: {
color: "#FFFFFF",
textAlign: "center",
fontWeight: "700"
}
});
Make sure the SignOutButton import path matches the location where you create that component. If you keep the sign-out button inside another file, adjust the import accordingly.
19. Implementing Firebase Firestore
Cloud Firestore is a cloud-hosted NoSQL database that stores documents inside collections. It is useful for storing user profiles, directory records, application settings, and other structured information.
19.1 Understanding Collections and Documents
| Term |
Meaning |
Example |
| Collection |
A group of documents. |
users |
| Document |
A record containing fields. |
users/user-123 |
| Field |
A value inside a document. |
displayName |
| Document ID |
An identifier for the document. |
Firebase Authentication UID |
19.2 Store a User Profile
After a user registers, the application can create a Firestore document containing the additional profile information the app needs.
import {
doc,
setDoc,
serverTimestamp
} from "firebase/firestore";
import { db } from "../firebase/firebaseConfig";
export async function saveUserProfile(user, extraData = {}) {
if (!user?.uid) {
throw new Error("A signed-in user is required.");
}
const profileRef = doc(db, "users", user.uid);
await setDoc(profileRef, {
uid: user.uid,
email: user.email ?? null,
displayName: user.displayName ?? "",
...extraData,
updatedAt: serverTimestamp()
}, { merge: true });
}
Use the Firebase Authentication UID as a stable link between the authenticated account and its profile document. Validate which fields may be written by the user, and do not allow client-provided values to assign administrative privileges.
19.3 Read a User Profile
import { doc, getDoc } from "firebase/firestore";
import { db } from "../firebase/firebaseConfig";
export async function getUserProfile(uid) {
const profileRef = doc(db, "users", uid);
const snapshot = await getDoc(profileRef);
if (!snapshot.exists()) {
return null;
}
return snapshot.data();
}
Before calling this function, confirm that the current user is authenticated. Firestore security rules must independently enforce which documents the user may read.
19.4 Update a User Profile
import {
doc,
updateDoc,
serverTimestamp
} from "firebase/firestore";
import { db } from "../firebase/firebaseConfig";
export async function updateUserProfileData(uid, updates) {
const profileRef = doc(db, "users", uid);
await updateDoc(profileRef, {
...updates,
updatedAt: serverTimestamp()
});
}
Only allow the specific fields that the user is authorized to update. Do not pass arbitrary form objects to a privileged backend without validating them.
20. Creating a User Service
Keeping Firestore operations in a service file separates data access from screen design.
import {
collection,
addDoc,
getDocs,
query,
orderBy,
serverTimestamp
} from "firebase/firestore";
import { db } from "../firebase/firebaseConfig";
export async function createDirectoryRecord(record) {
const recordsRef = collection(db, "directoryRecords");
const docRef = await addDoc(recordsRef, {
...record,
createdAt: serverTimestamp(),
updatedAt: serverTimestamp()
});
return docRef.id;
}
export async function fetchDirectoryRecords() {
const recordsRef = collection(db, "directoryRecords");
const recordsQuery = query(recordsRef, orderBy("name", "asc"));
const snapshot = await getDocs(recordsQuery);
return snapshot.docs.map((document) => ({
id: document.id,
...document.data()
}));
}
The collection in this example is named directoryRecords. Configure its security rules before using it. Add appropriate validation, error handling, and permissions to create or retrieve records. Firestore may require an index for certain compound queries.
21. Designing Firestore Security Rules
Firestore security rules determine which authenticated users can read or write documents. Hiding screens in React Native does not protect database records by itself.
21.1 Example: Users Can Read and Edit Their Own Profile
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth != null
&& request.auth.uid == userId;
allow create: if request.auth != null
&& request.auth.uid == userId
&& request.resource.data.uid == userId;
allow update: if request.auth != null
&& request.auth.uid == userId
&& request.resource.data.uid == resource.data.uid;
allow delete: if false;
}
}
}
This is an illustrative starting point, not a complete production policy. It allows authenticated users to read and update their own profile document and denies profile deletion. A production policy should validate allowed fields, field types, immutable properties, and other requirements.
21.2 Example: Private Directory Collection
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /directoryRecords/{recordId} {
allow read: if request.auth != null;
allow write: if false;
}
}
}
This example permits any authenticated user to read directory records and blocks client writes. It is appropriate only if the intended design is read-only for authenticated users. To support administrators, implement a trustworthy role model and enforce it in the rules or through a secure backend. Never assume that a user is an administrator because the mobile app sends an isAdmin field.
22. Handling User Roles and Permissions
Many applications have different types of users. For example, a student portal may have students and administrators, while an employee directory may have employees and HR managers.
| Role |
Example Permissions |
| Student |
View and update permitted personal details. |
| Employee |
View permitted directory information and personal profile details. |
| Teacher or Manager |
Access specific records needed for assigned responsibilities. |
| Administrator |
Manage records according to explicitly assigned privileges. |
For a production system, store authoritative roles in a trusted backend or use Firebase custom claims where appropriate. Configure security rules to check those roles. The app may hide unavailable actions for a better user experience, but the backend must enforce the actual permissions.
23. Connecting Authentication to Firestore
Authentication and Firestore work together through the signed-in user's identity. After authentication, Firebase provides a UID that can be used to identify the user in database operations.
- The user creates an account or signs in.
- Firebase Authentication verifies the credentials.
- The app receives the authenticated user object.
- The app reads or creates the user's permitted profile document.
- Firestore evaluates security rules for each request.
- The app displays data only after the request succeeds.
- When the user signs out, the app switches back to the authentication screens and clears any private in-memory data that should no longer remain visible.
Do not rely on the UID alone as proof of permission to access another user's records. Database rules must verify the identity and the requested operation.
24. Loading and Error States
Authentication and Firebase requests are asynchronous. The app should communicate progress and handle failures rather than freezing the interface or assuming that a request succeeded.
| Situation |
Recommended Behavior |
| Initial authentication check |
Show a loading screen until the user state is known. |
| Login in progress |
Disable repeated submission and show a progress indicator. |
| Registration succeeds |
Show the authenticated screen group or the required verification flow. |
| Network unavailable |
Display a retry option where appropriate. |
| Firestore request denied |
Explain that access is unavailable without revealing sensitive implementation details. |
| Profile not found |
Offer an appropriate profile setup or recovery workflow. |
| Sign-out fails |
Display a message and allow the user to retry. |
25. Testing Authentication and Navigation
Testing verifies that the correct screens are shown for every authentication state and that users cannot access private data through unintended paths.
25.1 Authentication Test Cases
| Test Case |
Action |
Expected Result |
| TC01 |
Register with valid details |
Firebase creates the account. |
| TC02 |
Register with invalid details |
A clear error appears and registration is not completed. |
| TC03 |
Sign in with valid credentials |
The authenticated screen group appears. |
| TC04 |
Sign in with invalid credentials |
The user remains on the authentication screen. |
| TC05 |
Request a password reset |
The app handles the request and displays a neutral result. |
| TC06 |
Restart the app with a valid persisted session |
The app restores the authenticated state. |
| TC07 |
Sign out |
The app returns to the authentication screen group. |
| TC08 |
Attempt to open a private route while signed out |
The private screen is not available through the configured navigator. |
25.2 Firebase Test Cases
- Read the current user's permitted profile document.
- Attempt to read another user's private profile.
- Attempt an unauthorized write.
- Verify that database rules reject unauthorized requests.
- Test network failures and permission-denied errors.
- Verify that sign-out clears private screen state.
- Check that user roles cannot be changed by an ordinary profile update.
Use Firebase Emulator Suite when appropriate to test authentication and database rules safely before deploying changes to production.
26. Common Problems and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Firebase app initialization error |
Incorrect configuration or duplicate initialization |
Check project settings and initialize the app only once. |
| Authentication state is lost after restart |
Persistence is not configured correctly |
Configure the supported React Native persistence adapter. |
| Login succeeds but Home does not appear |
Authentication listener or navigation condition is incorrect |
Verify AuthProvider, RootNavigator, and the user state. |
| App briefly shows the wrong screen |
Navigation renders before initial auth state is resolved |
Show a loading screen until initialization finishes. |
| Firestore returns permission denied |
Rules reject the requested operation |
Inspect the rules and verify the user's intended permissions. |
| User profile is missing |
Profile document was never created |
Implement a controlled profile creation workflow. |
| Registration works but profile update fails |
Firestore rules or network request fails |
Handle the failure and provide a retry or recovery path. |
| Navigation can return to a private screen |
Authentication screens and app screens share an incorrect stack design |
Use conditional screen groups based on Firebase auth state. |
| Firebase module cannot be imported |
Package missing or import path incorrect |
Check installation and file paths, then restart the development server if needed. |
27. Security Best Practices
- Use Firebase Authentication or another trusted identity provider instead of creating a custom password system.
- Never store raw passwords in AsyncStorage, Firestore, or application state for later reuse.
- Do not treat a locally stored login flag as proof of authentication.
- Use restrictive Firestore security rules.
- Validate data on the backend or through database rules, not only in the mobile interface.
- Keep private service credentials out of the client application.
- Use secure storage for sensitive local information when required.
- Use email verification and password-reset features when appropriate.
- Limit permissions according to user roles and the principle of least privilege.
- Clear sensitive in-memory data when the user signs out.
- Avoid exposing raw Firebase errors or private record information in production logs.
- Review security rules and authentication settings before every production release.
28. Improving the Application
After the basic implementation works, the app can be expanded with additional features.
- Google Sign-In: Allow users to authenticate with a supported identity provider.
- Phone Authentication: Verify users through a phone-based workflow where supported.
- Email Verification: Require verified email addresses for selected features.
- Profile Editing: Let users update permitted profile fields.
- Profile Pictures: Store photographs in Cloud Storage for Firebase and save the appropriate file reference in Firestore.
- Role-Based Navigation: Display role-appropriate screens while enforcing permissions in backend rules.
- Realtime Updates: Use Firestore listeners where live updates are needed.
- Push Notifications: Add Firebase Cloud Messaging for relevant notifications.
- Offline Handling: Design a suitable offline and synchronization strategy.
- Account Deletion: Build a secure workflow for deleting or disabling accounts and related records.
29. Mini Project: Secure Student or Employee Portal
Project Title: Secure Student Management or Employee Directory App
Objective: Build a React Native app with Firebase Authentication, authentication-aware navigation, and Firestore-backed user data.
29.1 Functional Requirements
- Register a user with an email address and password.
- Sign in and sign out.
- Provide a password-reset workflow.
- Restore authentication state when the app starts.
- Display a Home screen only for authenticated users.
- Display a Profile screen with permitted user details.
- Save and retrieve the profile from Firestore.
- Protect records using Firestore security rules.
- Show loading and error states.
- Test invalid credentials and unauthorized database requests.
29.2 Suggested Screens
| Screen |
Purpose |
| Login |
Authenticate an existing user. |
| Register |
Create a new account. |
| Forgot Password |
Request a password-reset email. |
| Home |
Display a welcome message and main navigation options. |
| Profile |
Display the current user's permitted profile data. |
| Directory |
Display records permitted by the user's role. |
| Settings |
Provide account preferences and sign-out. |
29.3 Project Deliverables
- Working React Native source code.
- Firebase project configuration instructions.
- Authentication and navigation implementation.
- Firestore data model and security rules.
- Test cases for sign-in, registration, sign-out, and permissions.
- A README containing setup steps and known limitations.
30. Development Workflow
- Plan Requirements: Define the users, authentication methods, and protected resources.
- Create the Project: Set up React Native and install the required packages.
- Configure Firebase: Create the Firebase project and enable Authentication and Firestore.
- Build Authentication Services: Implement registration, login, password reset, and sign-out.
- Create Auth Context: Observe and share the Firebase authentication state.
- Build Navigation: Display authentication or private screens according to the current user state.
- Connect Firestore: Create, read, and update permitted user data.
- Write Security Rules: Enforce data access permissions independently of the UI.
- Handle Errors: Add loading indicators, validation, and recovery actions.
- Test: Verify authentication flows, screen transitions, and database permissions.
- Review Security: Check configuration, storage, logging, and access rules.
- Prepare for Release: Test release builds and document setup requirements.
31. Interview Questions and Answers
Q1. What is authentication in React Native?
Answer: Authentication verifies the identity of a user before allowing access to account-related features or protected resources.
Q2. What is Firebase Authentication?
Answer: Firebase Authentication is a managed service that provides account creation, sign-in, sign-out, and supported identity-provider integrations.
Q3. What is React Navigation?
Answer: React Navigation is a navigation library that manages movement between screens, navigation history, and common mobile navigation patterns.
Q4. What is authentication-based navigation?
Answer: It is a navigation design that displays authentication screens when the user is signed out and application screens when the user is signed in.
Q5. What is onAuthStateChanged?
Answer: It is a Firebase Authentication listener that notifies the application when the current authentication state changes.
Q6. Why use an AuthContext?
Answer: AuthContext makes shared authentication information available across components without passing the same props through every level of the component tree.
Q7. What is Firebase Firestore?
Answer: Cloud Firestore is a managed NoSQL database that stores structured data in collections and documents.
Q8. What is the difference between authentication and authorization?
Answer: Authentication verifies who a user is, while authorization determines what resources and operations that user is permitted to access.
Q9. Why are Firestore security rules important?
Answer: They enforce data access permissions at the database level. Hiding a screen or button in the mobile app does not prevent an unauthorized database request.
Q10. How can authentication persist after restarting the app?
Answer: Configure Firebase Authentication with the supported persistence mechanism for the React Native environment and observe the restored state through onAuthStateChanged.
Q11. Why should private screens not be shown before authentication initializes?
Answer: The initial authentication state may not be known immediately. A loading screen prevents a temporary display of the wrong screen group.
Q12. How should sign-out be implemented?
Answer: Call Firebase's signOut method and let the authentication-state listener update the root navigator. Clear any sensitive in-memory data that should no longer be visible.
Q13. Can Firebase configuration values be placed in a mobile app?
Answer: The Firebase client configuration is designed to identify the project, but database rules and authentication must protect resources. Private service-account credentials and server secrets must never be embedded in the app.
Q14. How should user roles be implemented?
Answer: Use a trusted backend or Firebase custom claims for authoritative roles, then enforce permissions through database rules or server-side checks.
Q15. What should happen if Firestore returns permission denied?
Answer: Handle the error gracefully, check the authenticated user's intended permissions and security rules, and do not bypass the rules to make the request succeed.
32. Quick Revision Checklist
- Understand authentication and authorization.
- Create and configure a Firebase project.
- Enable Firebase Authentication.
- Configure Firebase Auth persistence for React Native.
- Implement registration and login.
- Implement password reset and sign-out.
- Observe authentication changes with onAuthStateChanged.
- Create an AuthContext for shared user state.
- Build conditional authentication navigation.
- Display a loading screen during initialization.
- Connect Firestore for profile or directory data.
- Write restrictive Firestore security rules.
- Handle loading, validation, and network errors.
- Test authenticated and unauthenticated navigation flows.
- Review user roles, sensitive data handling, and production security.
33. Conclusion
Implementing authentication, navigation, and Firebase integration brings together several core skills required for real-world React Native development. Firebase Authentication manages user identity, React Navigation organizes screen transitions, and Cloud Firestore stores data according to the application's access rules.
A reliable implementation uses authentication-state listeners, handles the initial loading state, separates public and private screen groups, and enforces data permissions in Firebase rather than relying on the user interface alone. Begin with email/password authentication and a protected Home screen, then add Firestore profiles, password recovery, user roles, and more advanced features.
To learn more, visit JustAcademy's React Native Training course, register for a course demo, and explore the JustAcademy website.
34. Official Learning Resources