skip to Main Content

Client side Firebase seems to believe i have an authenticated user when I refresh the page, but the firestore firebase rules imply i don’t have an authenticated user.

I have a firebase application where the user logs in using email and password via signInWithEmailAndPassword. I have the persistence set to browserLocalPersistence. normally when a user logs in the method onAuthStateChanged is called with the authenticated user object, and the app functions properly. when the user refreshes the page my onAuthStateChanged method fires again with a non null user, but all firebase queries fail because of my security rules. what am i doing wrong there.

This is a reactjs app. Let me know if I can offer any other answers that might give you something to go off of.

my firebase rules:

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

My login code:

setPersistence(sharedFirebaseAuth, browserLocalPersistence)
    .then(() => {
        signInWithEmailAndPassword(sharedFirebaseAuth, email, password)
            .then((userCredential) => {
            })
            .catch((error) => {
            });
})

My onAuthStateChanged code


onAuthStateChanged(sharedFirebaseAuth, (user) => {
  if(user){
    user.reload().then(()=>{
      requestDocs();
    });
  }
})

2

Answers


  1. Chosen as BEST ANSWER

    I ended up having to implement an Auth provider in order for it to keep my user info.

    new file AuthContext.tsx

    import React from "react"
    import { User } from "firebase/auth";
    
    export const AuthContext = React.createContext<User | null>(null)
    

    new file AuthProvider.tsx

    import { FC, useEffect, useState } from "react";
    import { User } from "firebase/auth";
    import { sharedFirebaseAuth } from "../Utils/SharedFirebase";
    import { AuthContext } from "./AuthContext";
    
    export interface AuthProviderProps {
        children: JSX.Element;
    }
    
    export const AuthProvider: FC<AuthProviderProps> = ({ children }: AuthProviderProps) => {
      const [user, setUser] = useState<User | null>(null);
      
      useEffect(() => {
        const unsubscribe = sharedFirebaseAuth.onAuthStateChanged((firebaseUser) => {
          setUser(firebaseUser);
        });
        return unsubscribe
      }, []);
    
      return (
        <AuthContext.Provider value={user}>{children}</AuthContext.Provider>
      );
    };
    

    and then i updated my index.tsx like so

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import App from './App';
    import { AuthProvider } from './provider/AuthProvider';
    import "bootstrap/dist/css/bootstrap.min.css";
    
    const root = ReactDOM.createRoot(
      document.getElementById('root') as HTMLElement
    );
    root.render(
      <React.StrictMode>
        <AuthProvider>
          <App />
        </AuthProvider>
      </React.StrictMode>
    );
    
    

    and then updated my app.tsx section where i was calling the onAuthStateChanged to the following

        const user = useContext(AuthContext);
    
        useEffect(() => {
            if(user){
                requestDocs();
            }
        },[user]);
    
    

    upon refresh it seems to keep all of the necessary auth info to pull in documents.


  2. I think you forget the comma between read and write, Should look like allow read, write: if isAuthenticated();

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search