【问题标题】:Firestore Missing or Insufficient Privileges and React NativeFirestore 缺少或不足的权限和 React Native
【发布时间】:2020-11-24 22:33:12
【问题描述】:

我是 Firebase 和 React Native 的新手。当我将电子邮件身份验证添加到应用程序并更新身份验证规则时,我收到 Missing or Insufficient Privileges 错误。

我在 Screen1 的 componentDidMount 函数中尝试写入 Firestore 时收到错误消息。

我为我的应用添加了以下规则。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
       allow read, write: if request.auth != null;
    }
  }
}

如果我更改为 allow read, write: if true; 一切正常。但是,这不是我计划部署的生产应用程序所需要的。

我通过拉入当前用户检查了我的应用程序,看起来用户已登录并通过身份验证。

当我尝试执行以下操作时,我收到了身份验证错误:[FirebaseError: Missing or enough permissions.]:

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 />
    )
  }
}

提供者 - /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>
  );
}

帮助文件(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>
  );
}

登录屏幕

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;

我什至可以在 console.log 中返回用户的 UID。我还可以看到用户通过 Firebase Web 控制台登录。

我是否缺少在 IAM 中添加一些权限? React Native 库是否存在一些响应头问题或我应该解决的问题。

我安装了以下库和版本:

    "@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",

【问题讨论】:

  • 如果权限拒绝写入,则写入时 auth 为空。所以你有时间问题。在您尝试访问数据库之前,您的身份验证可能不会被处理。因为您包含了摘录而不是可运行的代码示例,所以这是不可操作的,我只能提供猜测。见how to askcreating an mcve
  • 如果我可以在catch的console.log中返回UID,auth可以为null吗? console.log("写入数据库出错", "用户登录为:", currentUser.uid, err);
  • 最简单的故障排除方法通常是在您访问数据库之前记录 UID。所以在你的await session.set({... 之前添加一个console.log(firebase.auth().currentUser) 并检查它的输出。如果您在此故障排除后无法使其正常工作,请编辑您的问题以包含更新的代码及其输出,以便我们进行检查。
  • 我添加了一些额外的代码。我能够在 session.set 之前将日志添加到 UID,并且应用程序能够记录 UID。我仍然收到错误消息。

标签: reactjs firebase react-native google-cloud-firestore firebase-security


【解决方案1】:

我想知道您的 Config.js 文件是否混淆了 SDK。根据react-native-firebase docs

与 Firebase Web SDK 不同,您无需使用项目凭据手动调用 initializeApp 方法。原生 Android 和 iOS SDK 使用在Getting Started 安装步骤中提供的凭据自动连接到您的 Firebase 项目。

此外,您可能还需要考虑:

  • 摆脱AuthProvider.js,只需在需要检查身份验证状态或用户信息时直接调用@react-native-firebase/auth

或:

  • 将您的 onAuthStateChanged 侦听器移动到 AuthProvider.js 并确保您使用 AuthProvider 而不是直接调用 @react-native-firebase/auth

选择其中一个选项而不是两者之一将有助于您跟踪您的身份验证状态的情况。

【讨论】:

  • 杰夫你是个救星!它是 Config.js 文件。我更新了我的 helper.js 文件并从那里调用了firestore().collection,而不是从该 config.js 文件中导入db,一切正常!
  • 很高兴听到这个消息! :)
猜你喜欢
  • 2018-06-11
  • 2019-02-26
  • 2018-04-25
  • 2020-09-01
  • 2022-01-25
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 2018-09-12
相关资源
最近更新 更多