Building cross-platform mobile apps means handling user authentication reliably across iOS and Android. When developers pair React Native with Supabase for backend data storage, they often want to leverage Firebase Authentication—specifically using getAuth—for identity management, social logins, and multi-factor security.
Merging these two tools requires clear configuration because they operate as distinct services. If you configure them incorrectly, session tokens drift, state changes fail to sync across navigation trees, and backend row-level security rejects valid users.
This guide walks through configuring Firebase Auth alongside Supabase in a React Native application, setting up proper session persistence, and enforcing secure database access without common pitfalls.
Understanding the Architecture
Supabase provides an open-source Postgres database, real-time channels, and an independent built-in auth system. Firebase Authentication offers a mature identity provider ecosystem supporting anonymous logins, phone authentication, and federated identity providers like Google and Apple.
When you combine them, Firebase handles the user identity lifecycle (signup, password reset, token generation), while Supabase handles application data, business logic, and database queries.
To make this work smoothly, your React Native app must capture the Firebase ID token upon authentication and pass it to Supabase requests, or use custom JWT claims. Without this link, Supabase has no way of knowing which user is making a query under Row Level Security (RLS) policies.
Project Setup and Initializing SDKs
Before writing code, install the required packages for both ecosystems. Open your terminal in your React Native project root and run:
npm install @supabase/supabase-js firebase
Make sure you configure your package bundler or environment variables correctly using a library like react-native-dotenv to keep your API keys out of source control.
Configuring Firebase Auth
Initialize Firebase using your project configuration constants. Create an authentication service file, such as firebaseAuth.js, to manage your auth instance:
import { initializeApp } from 'firebase/app';
import {
initializeAuth,
getReactNativePersistence,
signInWithEmailAndPassword,
signOut,
getAuth
} from 'firebase/auth';
import AsyncStorage from '@react-native-async-storage/async-storage';
const firebaseConfig = {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.FIREBASE_APP_ID,
};
const app = initializeApp(firebaseConfig);
// Initialize Auth with AsyncStorage persistence for React Native
export const auth = initializeAuth(app, {
persistence: getReactNativePersistence(AsyncStorage)
});
Using getReactNativePersistence with AsyncStorage is vital. Without explicit mobile persistence, users will find themselves logged out every time they close and reopen the app.
Initializing Supabase Client
Next, create your Supabase client instance in a separate file, such as supabaseClient.js:
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
persistSession: false, // Managed by Firebase instead
autoRefreshToken: false,
},
});
Disabling session persistence and token refreshing on the Supabase client prevents it from competing with Firebase Auth for local storage tokens.
Synchronizing Auth States in React Native
To keep your app UI responsive to login and logout events, wrap your authentication logic in a React Context. This provider listens to Firebase's onAuthStateChanged observer and exposes the current user state to your screens.
import React, { createContext, useState, useEffect, useContext } from 'react';
import { onAuthStateChanged, signInWithEmailAndPassword, signOut as fbSignOut } from 'firebase/auth';
import { auth } from './firebaseAuth';
import { supabase } from './supabaseClient';
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
if (firebaseUser) {
setUser(firebaseUser);
// Retrieve the Firebase ID token to pass to Supabase requests
const token = await firebaseUser.getIdToken();
// Update Supabase client headers with the Firebase token
supabase.rest.headers['Authorization'] = `Bearer ${token}`;
} else {
setUser(null);
delete supabase.rest.headers['Authorization'];
}
setLoading(false);
});
return unsubscribe;
}, []);
const login = async (email, password) => {
setLoading(true);
try {
await signInWithEmailAndPassword(auth, email, password);
} catch (error) {
console.error('Login error:', error.message);
throw error;
} finally {
setLoading(false);
}
};
const logout = async () => {
await fbSignOut(auth);
};
return (
<AuthContext.Provider value={{ user, login, logout, loading }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
Securing Supabase Data with Row Level Security
When your React Native client queries Supabase tables, the database needs to verify who is asking. Because you are passing the Firebase token in the request header, you can configure PostgreSQL custom functions to extract the Firebase user UID.
In your Supabase SQL editor, create a helper function that reads the JWT claims:
CREATE OR REPLACE FUNCTION auth.firebase_uid()
RETURNS text AS $$
SELECT nullif(current_setting('request.jwt.claim.sub', true), '')::text;
$$ LANGUAGE sql STABLE;
With this function active, you can write Row Level Security (RLS) policies on your tables. For example, to ensure users can only read and write their own profile data:
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow individual user access" ON profiles
FOR ALL
USING (auth.firebase_uid() = user_id)
WITH CHECK (auth.firebase_uid() = user_id);
This setup ensures that even if a client-side API call is intercepted or modified, the Supabase database rejects any unauthorized read or write operations at the storage layer.
Handling Common Mistakes and Edge Cases
Developers merging Firebase and Supabase often run into specific stumbling blocks. Review these common issues to save hours of debugging:
- Token Expiration: Firebase ID tokens expire every hour. Ensure your app calls
getIdToken(true)to force a token refresh when making sensitive network requests if your custom middleware doesn't handle background rotation automatically. - Network Latency on Startup: When the app cold starts,
onAuthStateChangedtakes a split second to fire. Always check yourloadingstate before rendering protected navigators to prevent flashing login screens. - AsyncStorage Conflicts: If you attempt to let both Firebase and Supabase manage local session persistence simultaneously, they may overwrite each other's storage keys. Keep Supabase persistence disabled when using Firebase as your primary auth controller.
For official documentation on mobile security best practices, consult the Firebase Auth Guide and the Supabase Security Documentation.
Frequently Asked Questions
Can I use social login providers like Google and Apple with this setup?
Yes. Firebase Auth handles the OAuth flow and token exchange for Google, Apple, Facebook, and Twitter. Once Firebase returns the authenticated user object, you extract the ID token and pass it to Supabase just like an email and password login.
What happens if the user loses internet connection?
Firebase Auth caches user credentials locally using AsyncStorage. If a user opens your React Native app offline, onAuthStateChanged still recognizes the cached session, allowing offline navigation. However, Supabase queries requiring live network connectivity will fail until connection is restored.
Do I need to create user records in Supabase manually after signup?
You can automate this. Set up a Firebase Cloud Function that triggers on user creation (functions.auth.user().onCreate), which automatically executes an API call to insert a corresponding user row into your Supabase database table.
Is it secure to pass Firebase tokens to Supabase REST headers?
Yes, provided your Supabase project is configured to validate the JWT signature issued by your specific Firebase project. Ensure your Supabase JWT secret matches or accepts the public keys provided by Google's Firebase auth endpoint.