🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsHeader and Footer not showing in Excel(14.09.2026 um 22:43 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsKB5129194 Windows 11 26H1 Out of Band Update - Deskmodder.de(14.09.2026 um 19:25 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsHeader and Footer not showing in Excel(14.09.2026 um 22:43 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsKB5129194 Windows 11 26H1 Out of Band Update - Deskmodder.de(14.09.2026 um 19:25 Uhr)

🔧 Programmierung 🕛 vor 4 Monaten 9 Min Lesezeit
0

I Was Tired of Writing the Same Firebase Auth Boilerplate in Every React Native App — So I Built FireGuard

↗ Quelle (dev.to)
🗣️ Stimme:




How I went from copying and pasting 300 lines of auth code across projects to a single npm package that handles everything — protected routes, role-based access, and security best practices out of the box.



The Problem I Kept Running Into

Every time I started a new React Native project with Firebase, I found myself writing the same code.




CODE
// The same listener. Every. Single. Project.
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (user) => {
if (user) {
const doc = await getDoc(doc(db, 'users', user.uid));
setRole(doc.data()?.role ?? 'user');
setUser(user);
} else {
setUser(null);
setRole(null);
}
setIsLoaded(true);
});
return unsubscribe;
}, []);






Then the route guard logic. Then the redirect logic. Then the "show this only for admins" logic. Then the error messages. Then realizing I'm storing the raw Firebase User object in state — which contains your ID token, refresh token, and all kinds of internals — and that React DevTools can expose it to anyone inspecting the app.

It was boilerplate on top of boilerplate, and I kept making the same security mistakes every time.

If you've used Clerk with Expo, you know how different it feels:




CODE
// Clerk makes this effortless
<SignedIn>
<ProtectedScreen />
</SignedIn>

<SignedOut>
<LoginScreen />
</SignedOut>






That declarative, component-based approach is exactly what Firebase developers deserve too. So I built it.



Introducing FireGuard 🔥🛡️

FireGuard is a security-first authentication wrapper for Firebase + Expo React Native apps. It gives you the Clerk-like developer experience on top of Firebase Auth.




CODE
npm install react-native-fireguard






364 downloads in its first 4 days on npm. Clearly, I'm not the only one who felt this pain.

Here's what it gives you in under 5 minutes of setup.



From This... To This

Before FireGuard

Here's what a typical Firebase auth setup looks like in a real Expo app:




CODE
// _layout.jsx — the typical Firebase boilerplate nightmare
import { useEffect, useState } from 'react';
import { useRouter, useSegments } from 'expo-router';
import { onAuthStateChanged } from 'firebase/auth';
import { getDoc, doc } from 'firebase/firestore';
import { auth, db } from './firebase';

export default function RootLayout() {
const [user, setUser] = useState(null);
const [role, setRole] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const router = useRouter();
const segments = useSegments();

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
if (firebaseUser) {
try {
const snap = await getDoc(doc(db, 'users', firebaseUser.uid));
setRole(snap.exists() ? snap.data().role : 'user');
} catch {
setRole('user');
}
setUser(firebaseUser); // ⚠️ raw User object in state — security risk
} else {
setUser(null);
setRole(null);
}
setIsLoaded(true);
});
return unsubscribe;
}, []);

useEffect(() => {
if (!isLoaded) return;
const inAuthGroup = segments[0] === '(auth)';
if (!user && !inAuthGroup) router.replace('/(auth)/login');
else if (user && inAuthGroup) router.replace('/(tabs)');
}, [user, isLoaded, segments]);

// Now pass user/role through context... more boilerplate
// Then protect individual screens... more boilerplate
// Then handle admin-only content... even more boilerplate
}






That's just the root layout. You haven't even touched the individual screens yet.

After FireGuard




CODE
// _layout.jsx — the entire auth setup, done
import { Slot } from 'expo-router';
import { FirebaseAuthProvider, RouteGuard } from 'react-native-fireguard';

const firebaseConfig = {
apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
};

export default function RootLayout() {
return (
<FirebaseAuthProvider config={firebaseConfig}>
<RouteGuard loginRoute="/(auth)/login" homeRoute="/(tabs)">
<Slot />
</RouteGuard>
</FirebaseAuthProvider>
);
}






That's it. Route protection, Firestore role fetching, token management — all handled.



The Full Feature Set




  1. Declarative Auth Components
    Show and hide content based on auth state — no if statements scattered across your components:




CODE
import { SignedIn, SignedOut, useAuth } from 'react-native-fireguard';

export default function HomeScreen() {
const { user, role, signOut } = useAuth();

return (
<View>
<SignedIn>
<Text>Welcome, {user?.email}!</Text>

{/* Role-based access control */}
<Protect role="admin">
<AdminDashboard />
</Protect>

<Protect role={["admin", "moderator"]}>
<ModerationTools />
</Protect>

<Button title="Sign Out" onPress={signOut} />
</SignedIn>

<SignedOut>
<Text>Please sign in to continue</Text>
</SignedOut>
</View>
);
}






2. Pre-Built Auth Screens

Stop building login forms from scratch:




CODE
// app/(auth)/login.jsx
import { SignIn } from 'react-native-fireguard';

export default function LoginScreen() {
return <SignIn />; // Full form with validation, loading states, error handling
}






3. Role-Based Tab Visibility

Hide entire tabs from users who don't have permission — not just the content inside them:




CODE
// app/(tabs)/_layout.jsx
import { Tabs } from 'expo-router';
import { useAuth } from 'react-native-fireguard';

export default function TabsLayout() {
const { role } = useAuth();
const isAdmin = role === 'admin';

return (
<Tabs>
<Tabs.Screen name="home" options={{ title: 'Home' }} />
<Tabs.Screen name="profile" options={{ title: 'Profile' }} />
<Tabs.Screen
name="admin"
options={{
title: 'Admin',
href: isAdmin ? undefined : null, // completely invisible to non-admins
}}
/>
</Tabs>
);
}






4. Secure API Calls




CODE
const { getIdToken } = useAuth();

const fetchSensitiveData = async () => {
// Force-refresh the token before a payment or sensitive operation
const token = await getIdToken(true);

const response = await fetch('https://api.yourapp.com/payment', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ amount: 100 }),
});
};






The Security Decisions That Matter

This isn't just a convenience wrapper. FireGuard was built with specific security problems in mind that most Firebase implementations get wrong.



Problem 1: Raw Firebase User in React State

Most tutorials tell you to do this:




CODE
const [user, setUser] = useState(null);

onAuthStateChanged(auth, (firebaseUser) => {
setUser(firebaseUser); // ❌ This puts your ID token in React state
});






The Firebase User object contains stsTokenManager — your raw ID token and refresh token. When you put it in React state, it becomes visible in React DevTools to anyone with access to your development environment, and it can be accidentally logged or serialized.

FireGuard's fix: The raw User is stored in a useRef — completely invisible to React DevTools — and only a sanitized SafeUser object is exposed through context:




CODE
// What FireGuard exposes — no tokens, no internals
interface SafeUser {
uid: string;
email: string | null;
displayName: string | null;
emailVerified: boolean;
isAnonymous: boolean;
// ... safe properties only
}






Problem 2: onAuthStateChanged Misses Token Revocations

If you disable a user in the Firebase Console or revoke their token via the Admin SDK, onAuthStateChanged never fires. The user stays signed in indefinitely on the client.



FireGuard's fix: Uses onIdTokenChanged instead, which fires on every token refresh cycle (~every hour) and catches server-side revocations immediately.




CODE
// FireGuard uses this instead of onAuthStateChanged
onIdTokenChanged(auth, (firebaseUser) => {
// Fires on: sign in, sign out, token refresh, AND token revocation
});






Problem 3: Email Enumeration

Most auth error handlers do this:




CODE
'auth/user-not-found': 'No account found with this email.',  // ❌
'auth/wrong-password': 'Incorrect password.', // ❌






An attacker can use different error messages to test which emails are registered in your app. This is a well-documented attack called email enumeration — and it's trivially easy to automate.



FireGuard's fix: Both codes return the same message:




CODE
'auth/user-not-found': 'Invalid email or password.',  // ✅
'auth/wrong-password': 'Invalid email or password.', // ✅






Problem 4: Route Guard Race Conditions

Expo Router's navigation is async. If you call router.replace() before the navigator is mounted, it silently fails — leaving an unauthenticated user on a protected screen with no redirect.



FireGuard's fix: Checks useRootNavigationState before any redirect:




CODE
const navigationState = useRootNavigationState();
const isNavigatorReady = !!navigationState?.key;

useEffect(() => {
if (!isLoaded || !isNavigatorReady) return; // wait for navigator
if (!user) router.replace(loginRoute);
}, [isLoaded, isNavigatorReady, user]);






Setting Up Role-Based Access Control

Roles are stored in Firestore. The structure is simple:

Collection: users

└── Document: {firebase_auth_uid} ← must be the user's UID

├── role: "admin"

└── email: "

GitHub:

If this saves you time on your next Firebase project, drop a ⭐ on GitHub and share it with your team. And if you run into any issues or have feature ideas — open an issue, I'm actively reading them.



Built by Vincent — a developer who got tired of writing the same Firebase boilerplate one too many times.



react-native firebase expo authentication open-source javascript mobile-development rbac

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
Header and Footer not showing in Excel
1 Quelle
Burn Out, Or Fade Away
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten I Was Tired of Writing the Same Firebase Auth Boilerplate in Every React Native App — So I Built FireGuard

Thematisch verwandte Begriffe: Tired, Writing, Same, Firebase · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...