Search code examples
javascriptreactjsgoogle-cloud-firestorefirebase-authenticationfirebase-security

Firestore app gets missing or insufficient permissions even when not trying to access the data


I've been following along with a demo video on Youtube for learning Firebase auth that also uses firestore for the database. I've modified mine a bit to show a list of recipes when the user is logged in and only allows the user to create a new recipe if they are logged in. I hide the create button and the list of existing recipes if they are logged out, all they see when logged out is a message to log in. I also modified the rules in firestore to only allow access to the data if there is a user signed in.

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

Thats just the basic code provided in their documentation. It was working well until I tried to create a recipe while logged out by showing the button through the dev tools and trying to submit a new recipe in order to test the rules and make sure it would not allow it. Now the page errors before fully loading once unless I remove the rules in firestore. If I allow access everything works as intended.

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const querySnapshot = await getDocs(collection(db, "Recipes"));
//listen for auth status
onAuthStateChanged(auth, (user) => {
  console.log(user);
  if (user) {
    setupRecipes(querySnapshot);
    setupUI(user);
  } else {
    setupRecipes([]);
    setupUI();
  }
});

const recipeList = document.querySelector(".recipes");
const loggedOutLinks = document.querySelectorAll(".logged-out");
const loggedInLinks = document.querySelectorAll(".logged-in");

const setupUI = (user) => {
  if (user) {
    loggedInLinks.forEach((item) => (item.style.display = "block"));
    loggedOutLinks.forEach((item) => (item.style.display = "none"));
  } else {
    loggedInLinks.forEach((item) => (item.style.display = "none"));
    loggedOutLinks.forEach((item) => (item.style.display = "block"));
  }
};

const setupRecipes = (data) => {
  let html = "";
  let ingredientList = "";
  data.forEach((doc) => {
    const recipe = doc.data();
    ingredientList = "";
    recipe.ingredients.forEach((ingredient) => {
      ingredientList += `<li>${ingredient}</li>`;
    });
    const li = `
      <li>
        <div class="collapsible-header grey lighten-4">${recipe.title}</div>
        <div class="collapsible-body white">
          <h4 style="margin: 0 0 10px 0;">Ingredients</h4>
          <ul class="ingredientList">
            ${ingredientList}
          </ul>
        </div>
      </li>
    `;
    html += li;
  });
  if (html.length !== 0) {
    recipeList.innerHTML = html;
  } else {
    recipeList.innerHTML =
      '<h4 class="center-align">Login to view recipes</h4>';
  }
};

So I guess my question is: Is that bad request being held onto somehow or am I trying to access the data somewhere without realizing it? I have tried clearing my cache and restarting the local server I have the app running on but the only way to even get the page to load with the message telling the user to log in is to take away to the rule in firestore and allow access for all. When the rule is there I get the following immediately on page load: 127.0.0.1/:1 Uncaught FirebaseError: Missing or insufficient permissions. Any help would be much appreciated, I'm happy to provide more of the code if needed.


Solution

  • As pointed out in the comments above there was a query to the database that was happening before the user was signed in. I moved the getDocs call into the onAuthStateChanged function and only call it when a user exists which now allows the page to load while signed out since there is no attempt to retrieve anything unless an authorized user is signed in

    const app = initializeApp(firebaseConfig);
    const auth = getAuth(app);
    const db = getFirestore(app);
    
    //listen for auth status
    onAuthStateChanged(auth, async(user) => {
      console.log(user);
      if (user) {
        const querySnapshot = await getDocs(collection(db, "Recipes"));
        setupRecipes(querySnapshot);
        setupUI(user);
      } else {
        setupRecipes([]);
        setupUI();
      }
    });