Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Cloud Functions Firestore Trigger produces: Error: 7 PERMISSION_DENIED: Missing or insufficient permissions

I'm trying to use a Firebase Cloud Function to update a document within my Firestore database, when one of my documents has been updated using a trigger. The trigger is called and working fine, but when I'm using the firebase admin instance to get the other document which I want to update, I'm getting the following error.

Error: 7 PERMISSION_DENIED: Missing or insufficient permissions.
    at Object.exports.createStatusError (/user_code/node_modules/firebase-admin/node_modules/grpc/src/common.js:87:15)
    at ClientReadableStream._emitStatusIfDone (/user_code/node_modules/firebase-admin/node_modules/grpc/src/client.js:235:26)
    at ClientReadableStream._receiveStatus (/user_code/node_modules/firebase-admin/node_modules/grpc/src/client.js:213:8)
    at Object.onReceiveStatus (/user_code/node_modules/firebase-admin/node_modules/grpc/src/client_interceptors.js:1256:15)
    at InterceptingListener._callNext (/user_code/node_modules/firebase-admin/node_modules/grpc/src/client_interceptors.js:564:42)
    at InterceptingListener.onReceiveStatus (/user_code/node_modules/firebase-admin/node_modules/grpc/src/client_interceptors.js:614:8)
    at /user_code/node_modules/firebase-admin/node_modules/grpc/src/client_interceptors.js:1019:24

function code:

import * as functions from "firebase-functions";
import * as admin from "firebase-admin";

admin.initializeApp();
const settings = { timestampsInSnapshots: true };
admin.firestore().settings(settings);

export const onDocUpdate = functions.firestore
  .document("documents/{documentId}")
  .onUpdate((snapshot, context) => {
    console.log("onDocUpdate called ", context.params.documentId);
    const document = snapshot.after.data();
    console.log("Document: ", document);
    if (document.screw) {
      console.log("Document screw exists. ", document.screw);
      const docRef = admin
        .firestore()
        .collection("screws")
        .doc(document.screw);
      return docRef
        .get()
        .then(doc => {
          if (doc.exists) {
            console.log("Screw for document exists.");
          } else {
            console.error(
              "Screw for document not found! ",
              document.screw
            );
          }
        })
        .catch(error => {
          // Here I get the permission error :(
          console.error(
            "Screw for document doc load error!! ",
            error
          );
        });
    } else {
      console.error("Document is not bound to a screw! ", document.id);
    }
    return null;
  });

package.json

{
  "name": "functions",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "serve": "npm run build && firebase serve --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "main": "lib/index.js",
  "dependencies": {
    "@google-cloud/firestore": "^0.16.0",
    "firebase-admin": "^6.0.0",
    "firebase-functions": "^2.0.4",
    "protobufjs": "^6.8.8"
  },
  "devDependencies": {
    "tslint": "~5.8.0",
    "typescript": "~2.8.3"
  },
  "private": true
}

I assume that it has something to do with the permission of the admin instance, but no idea what the error could be, I've just followed the steps from the docs and the firebase videos on youtube.

My account is still on a Free Plan and I'm getting a notice in the logs the that I should configure the billing account, but if understand the documentation correct I should be able to access services within the Google Cloud Platform and so reading other nodes within the same database should not be a problem.

I've already found two similar issues here on stackoverflow, but did not find a solution there. Maybe someone else also faced this issue in the meantime and was able to solve it?

PERMISSION_DENIED Firestore CloudFunction TypeScript and Firebase error writing to Firestore via a Function: "7 PERMISSION_DENIED: Missing or insufficient permissions"

Update 1: Had another issue with the new timestampsInSnapshots setting. This has been fixed and the code above updated. The main issue permission denied is still present.

Update 2: Regarding the answer by @RonRoyston below. This is a Cloud Function and its using the Admin SDK from firebase-admin package to read the node. Hence it should not be effected by the firestore security rules. There's already a comment on one of the linked questions by @DougStevenson mentioning this. Based on the Admin SDK documentation it should be enough to initialize it by calling admin.initializeApp(), but unfortunately in my case it isn't. I've read no where that there is any need to apply any special IAM settings within the service accounts or security rules when using Cloud Functions, and so I didn't touch any of these settings.

Cheers, Lars

like image 856
Lars-B Avatar asked Aug 14 '18 22:08

Lars-B


4 Answers

I had the same issue. And like nvitius, I solved it by changing permissions.

When creating a function, the default service account appears to be <project-name>@appspot.gserviceaccount.com.

You can check this by clicking on the Environment variables, networking, timeouts and more link:

To show Service Account

And then you can verify or change the account to the `App Engine default service account':

App Engine default service account

After this, I went to the IAM to verify the permission this service account had.

But the IAM did not have this service account listed/added.

So I add it >> Add >> New members. Start typing the ID of the project and the service account should pop-up in the drop-down.

And then I gave it the following permissions:

  • Project >> Editor (It may have this already)
  • Datastore >> Cloud Datastore Owner
  • Storage >> Storage Admin

Hope this helps.

like image 131
ADW Avatar answered Sep 23 '22 09:09

ADW


My solution was setting up the serviceAccount, check the following code snippet:

var admin = require("firebase-admin");

var serviceAccount = require("path/to/serviceAccountKey.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://your-database-url.firebaseio.com"
});

You can generate the serviceAccountKey on: Firebase dashboard -> Project Settings -> Service Accounts tab

Hope it helps!

like image 40
Matias Seguel Avatar answered Sep 25 '22 09:09

Matias Seguel


I had the same exact problem with Cloud Functions. My issue was not solved by deleting/redeploying the function. It turns out that, when my project was provisioned, the IAM roles for the default service account were not added. I had to add an Editor role in the IAM Admin panel for <project-name>@appspot.gserviceaccount.com.

like image 44
nVitius Avatar answered Sep 23 '22 09:09

nVitius


I've finally got it working. I didn't change any firestore security rules nor any IAM stuff. I deleted the function which was running on us-central1. Created the same Cloud Function project again, copied over my existing code, but this time I deployed it to europe-west1 and it worked out of the box.

I assume that something might failed during the first initial deployment to us-central1 and after that my project stuck with the error even if I had deleted and redeployed the function several times. Not sure what happened exactly, because no obvious error has been displayed. Maybe someone of the firebase team who knows the internal workflows can tell us if something like this can happen and if yes, how to deal with it.

For now the above steps solved my issue.

like image 42
Lars-B Avatar answered Sep 21 '22 09:09

Lars-B