I am building a react native app, using expo as my build tool and Firebase as my Backend for Auth and Database.
So far, everything has gone fine. Auth Loggin and Logout works, Fetching from Firestore with enforced security rules works. The only issue I have is that the persistence of the authentication is not working. BUT: this issue is not unknown and there is a simple solution for it. In order for this to work, one has to customize the auth object deviant from the default way.
By default, I would do this:
import { initializeApp } from "firebase/app";
import {getAuth } from "firebase/auth";
// Initialize Firebase
const firebaseConfig = {...};
export const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
This will work for react native, but it will lack persistent auth - thus where my initial problem comes from. To fix it I followed Expo docs instructions on how to set up Firebase. They explicitly guided me to this solution. In short, I need to switch the default in-memory persistence manager to the react native persistence manager like this article also suggested:
import { initializeApp } from "firebase/app";
import {
initializeAuth,
getReactNativePersistence,
} from "firebase/auth/react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
// Initialize Firebase
const firebaseConfig = {};
export const app = initializeApp(firebaseConfig);
export const auth = initializeAuth(app, {
persistence: getReactNativePersistence(AsyncStorage),
});
The only thing it seems I have to addiotionally install is this package, the rest of the imports should already work.
npx expo install @react-native-async-storage/async-storage
But when I try to import the react native persistence manager I get this error:
// I try this -->
import { initializeAuth, getReactNativePersistence } from "firebase/auth/react-native";
--> I get this Error: "Cannot find module 'firebase/auth/react-native' or its corresponding type declarations.ts(2307)"
THIS is my issue, I just dont know why I cant import "getReactNativePersistence", and thus I cannot configure my firebase app as desired.
First, I tried to figure out, if there even is a "getReactNativePersistence" object in my node_modules/firebase/auth directory. In order to find it out I cloned this repository from the article mentioned above and looked into it if the import works locally on my machine and if there are differences in the package.json file. What I found:
--> The import works on my machine
--> I found no notable differences in the dependencies
--> I found where "getReactNativePersisentence" is supposed to be located. It is at:
\node_modules\@firebase\auth\dist\rn\src\platform_react_native\persistence\react_native.d.ts
Then I tried to find this object in my Project. I found it... It is in the very same place as in the repo above.
After that, I also tried reinstalling Firebase, which also changed nothing.
Last but not least I cloned my repo and tried a clean reinitialization, but that didn't work as well.
I have no clue what the problem could be... The import should work, the object is where it is supposed to be.
My Repo is here maybe someone has any suggestions on what is going on here?
The issue was that Firebase doesn't include getReactNativePersistence in its NPM module by default. So, I had to implement it manually.
Here’s a step-by-step of how I made it work:
Created firebaseAuth/reactNativeAsyncStorageTypes.ts with the necessary types:
// Define the base Persistence interface to match Firebase auth expectations
export interface Persistence {
readonly type: "SESSION" | "LOCAL" | "NONE" | "COOKIE";
}
export const enum PersistenceType {
SESSION = 'SESSION',
LOCAL = 'LOCAL',
NONE = 'NONE',
COOKIE = 'COOKIE'
}
export type PersistedBlob = Record<string, unknown>;
export type PersistenceValue = PersistedBlob | string;
export const STORAGE_AVAILABLE_KEY = '__sak';
export interface StorageEventListener {
(value: PersistenceValue | null): void;
}
export interface PersistenceInternal extends Persistence {
type: PersistenceType;
_isAvailable(): Promise<boolean>;
_set(key: string, value: PersistenceValue): Promise<void>;
_get<T extends PersistenceValue>(key: string): Promise<T | null>;
_remove(key: string): Promise<void>;
_addListener(key: string, listener: StorageEventListener): void;
_removeListener(key: string, listener: StorageEventListener): void;
_shouldAllowMigration?: boolean;
}
Created firebaseAuth/reactNativeAsyncStorage.ts with the getReactNativePersistence function:
// Define ReactNativeAsyncStorage interface locally
export interface ReactNativeAsyncStorage {
getItem(key: string): Promise<string | null>;
setItem(key: string, value: string): Promise<void>;
removeItem(key: string): Promise<void>;
}
import {
Persistence,
PersistenceInternal,
PersistenceType,
PersistenceValue,
STORAGE_AVAILABLE_KEY,
StorageEventListener
} from './reactNativeAsyncStorageTypes';
export function getReactNativePersistence(
storage: ReactNativeAsyncStorage
): Persistence {
return class implements PersistenceInternal {
static type: 'LOCAL' = 'LOCAL';
readonly type: PersistenceType = PersistenceType.LOCAL;
async _isAvailable(): Promise<boolean> {
try {
if (!storage) return false;
await storage.setItem(STORAGE_AVAILABLE_KEY, '1');
await storage.removeItem(STORAGE_AVAILABLE_KEY);
return true;
} catch {
return false;
}
}
_set(key: string, value: PersistenceValue): Promise<void> {
return storage.setItem(key, JSON.stringify(value));
}
async _get<T extends PersistenceValue>(key: string): Promise<T | null> {
const json = await storage.getItem(key);
return json ? JSON.parse(json) : null;
}
_remove(key: string): Promise<void> {
return storage.removeItem(key);
}
_addListener(_key: string, _listener: StorageEventListener): void {
// Listeners not supported for React Native storage
}
_removeListener(_key: string, _listener: StorageEventListener): void {
// Listeners not supported for React Native storage
}
};
}
Updated src/services/firebase.ts to use the local implementation:
import { initializeApp } from "firebase/app";
import { initializeAuth } from "firebase/auth";
import { getReactNativePersistence } from "../../firebaseAuth/reactNativeAsyncStorage";
import ReactNativeAsyncStorage from "@react-native-async-storage/async-storage";
// Rest of your Firebase configuration...
export const auth = initializeAuth(app, {
persistence: getReactNativePersistence(ReactNativeAsyncStorage)
});
My response is based on this response: https://stackoverflow.com/a/76943639/10500723
I hope it works :)