I'm new to Firebase and React Native. I am getting a Missing or Insufficient Privileges error when I add email authentication to the app and update the Authentication Rules.
I receive the error when trying to write to Firestore in the componentDidMount function in Screen1.
I have added the following rules for my app.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
If I change to allow read, write: if true;
everything works fine. However, that is not what I need for a production app that I plan on deploying.
I checked my app by pulling in the current User and it looks like the user is logged in and authenticated.
And I get the authentication error: [FirebaseError: Missing or insufficient permissions.] when I try to do the following:
Config.js
import Firebase from "firebase";
// Web App Firebase Configuration
var firebaseConfig = {
apiKey: "pretendapikeytoprotect",
authDomain: "pretendapp.firebaseapp.com",
databaseURL: "https://pretendapp.firebaseio.com",
projectId: "pretendapp",
storageBucket: "pretendapp.appspot.com",
messagingSenderId: "messengerSenderIDnumbers",
appId: "appIDNumber",
measurementId: "measureIDnumberiD"
};
const app = Firebase.initializeApp(firebaseConfig);
// Initialize Cloud Firestore through firebase
export const db = app.firestore();
db.settings({experimentalForceLongPolling: true});
App.js
/**
* @format
* @flow strict-local
*/
import 'react-native-gesture-handler';
import React, {Component} from 'react';
import Providers from './src/navigation';
import {
Alert,
I18nManager,
YellowBox
} from 'react-native';
//Ignore Warning about Setting a Timer/No known fix at this time.
YellowBox.ignoreWarnings(['Setting a timer']);
type Props = {};
export default class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
}
componentWillUnmount() {
//willMountFunctions
}
componentDidMount() {
//DidMountFunctions
}
render() {
return (
<Providers />
)
}
}
Providers - /src/navigation/index.js
import React from 'react';
import { AuthProvider } from './AuthProvider';
import Routes from './Routes';
export default function Providers() {
return (
<AuthProvider>
<Routes />
</AuthProvider>
);
}
Routes.js
import React, { useContext, useState, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import auth from '@react-native-firebase/auth';
import AuthStack from './AuthStack';
import HomeStack from './HomeStack';
import { AuthContext } from './AuthProvider';
import Loading from '../components/Loading';
export default function Routes() {
const { user, setUser } = useContext(AuthContext);
const [loading, setLoading] = useState(true);
const [initializing, setInitializing] = useState(true);
// Handle user state changes
function onAuthStateChanged(user) {
setUser(user);
if (initializing) setInitializing(false);
setLoading(false);
if (user) {
console.log('user is logged');
}
}
useEffect(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
if (loading) {
return <Loading />;
}
return (
<NavigationContainer>
{user ? <HomeStack /> : <AuthStack />}
</NavigationContainer>
);
}
HomeStack.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
//Import Components
import HomeScreen from '../screens/HomeScreen';
import Screen1 from '../screens/Screen1';
const Stack = createStackNavigator();
export default function HomeStack() {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home"
component = {HomeScreen}
options={{ title: 'Home Screen' }} />
<Stack.Screen name="Screen1"
component={Screen1}
options={{ title: 'Screen 1' }} />
</Stack.Navigator>
);
}
Helper file (helpers.js)
const RNFS = require('react-native-fs');
import storage from '@react-native-firebase/storage';
import firestore from '@react-native-firebase/firestore';
import { db } from '../config';
import { Platform } from 'react-native';
import { currentUser } from '../navigation/AuthProvider';
import auth from '@react-native-firebase/auth';
// removed other functions not relevant to question //
export const WriteSessionData = async (sessionID) => {
console.log("Write Session current User Logged in ", currentUser.uid);
console.log("Current Session User in Write Session ", auth().currentUser)
const session = db.collection('sessions').doc(sessionID);
await session.set({
name: '',
email: '',
timedate : Date.now(),})
.then(() => {
console.log("Session Data Added to Database");
}).catch((err) => {
console.log("Error in Writing to Database ", "User Logged in as: ", currentUser.uid, err);
});
}
}
AuthStack.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import SignupScreen from '../screens/SignupScreen';
import LoginScreen from '../screens/LoginScreen';
const Stack = createStackNavigator();
export default function AuthStack() {
return (
<Stack.Navigator initialRouteName='Login'>
<Stack.Screen
name='Login'
component={LoginScreen}
options={{ header: () => null }}
/>
<Stack.Screen name='Signup' component={SignupScreen} />
</Stack.Navigator>
);
}
Login Screen
import React, { useState, useContext } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image } from 'react-native';
import FormButton from '../components/FormButton';
import FormInput from '../components/FormInput';
import { AuthContext } from '../navigation/AuthProvider';
export default function LoginScreen({ navigation }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { login } = useContext(AuthContext);
return (
<View style={styles.container}>
<FormInput
value={email}
placeholderText='Email'
onChangeText={userEmail => setEmail(userEmail)}
autoCapitalize='none'
keyboardType='email-address'
autoCorrect={false}
/>
<FormInput
value={password}
placeholderText='Password'
onChangeText={userPassword => setPassword(userPassword)}
secureTextEntry={true}
/>
<FormButton buttonTitle='Login' onPress={() => login(email, password)} />
<TouchableOpacity
style={styles.navButton}
onPress={() => navigation.navigate('Signup')}
>
<Text style={styles.navButtonText}>New user? Join here</Text>
</TouchableOpacity>
</View>
);
}
AuthProvider.js
import React, { createContext, useState } from 'react';
import auth from '@react-native-firebase/auth';
export const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<AuthContext.Provider
value={{
user,
setUser,
login: async (email, password) => {
try {
await auth().signInWithEmailAndPassword(email, password);
} catch (e) {
console.log(e);
}
},
register: async (email, password) => {
try {
await auth().createUserWithEmailAndPassword(email, password);
} catch (e) {
console.log(e);
}
},
logout: async () => {
try {
await auth().signOut();
} catch (e) {
console.error(e);
}
}
}}
>
{children}
</AuthContext.Provider>
);
};
export const currentUser = auth().currentUser;
HomeScreen.js
import React, { Component, useContext, useState} from 'react';
import { Alert, Button, View, Text, Image, PermissionsAndroid, TouchableHighlight, TextInput } from 'react-native';
import { styles, buttons } from './styles'
import auth from '@react-native-firebase/auth';
import FormButton from '../components/FormButton';
import { AuthContext } from '../navigation/AuthProvider';
export default function HomeScreen(props) {
const { route, navigation } = props
const { user, logout } = useContext(AuthContext);
function navigate() {
console.log("User Logged In", auth().currentUser.uid);
console.log("Navigating to Screen 1 ....");
navigation.navigate('Screen1');
}
return (
<View>
<View style={styles.container}>
<Text style={styles.description}>You are logged in as {user.email}</Text>
<FormButton buttonTitle="Get Started" onPress={()=> navigate()} />
<FormButton buttonTitle='Logout' onPress={() => logout()} />
</View>
</View>
);
}
Screen1.js
import React, { Component } from 'react';
import { Alert, Button, View, Text, Image, PermissionsAndroid, TouchableHighlight } from 'react-native';
import { styles } from './styles'
import { db } from '../config';
import { WriteSessionData} from '../utils/helpers';
import storage from '@react-native-firebase/storage';
import { currentUser } from '../navigation/AuthProvider';
import FormButton from '../components/FormButton';
class Screen1 extends Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
sessionID: '12345' //random id that will be passed down
};
}
componentWillUnmount() {
clearInterval(this._progressInterval);
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
console.log("User ID in Screen 1 componentDidMount", currentUser.uid);
WriteSessionData(this.state.sessionID);
if(this._isMounted){
//do some other commands
}
}
render() {
return (
<View>
<Text style={styles.title}>Screen 1</Text>
<Text style={styles.description}>User Email: {currentUser.email}
</Text>
</View>
);
}
}
export default Screen1;
I can even return the user's UID in console.log. I can also see the user is logged in through the Firebase Web Console.
Am I missing adding some privileges in IAM? Is there some response header issue with the React Native libraries or something I should resolve.
I have the following libraries and versions installed:
"@react-native-firebase/app": "^8.2.0",
"@react-native-firebase/auth": "^8.2.0",
"@react-native-firebase/firestore": "^7.4.3",
"@react-native-firebase/storage": "^7.2.2",
"@react-navigation/native": "^5.6.1",
"@react-navigation/stack": "^5.6.2",
"firebase": "^7.16.0",
"react": "16.11.0",
"react-native": "0.62.2",
await session.set({...
add aconsole.log(firebase.auth().currentUser)
and check its output. If you're having trouble getting it to work after this troubleshooting, please edit your question to include the updated code and its output, so that we can check. – Frank van Puffelen