Firestore admin in Node.js Missing or insufficient permissions
Asked Answered
P

4

11

I am trying to access Firestore using Firebase Admin on Node.js v8.4.0 in a Firebase Cloud Function running locally using firebase functions:shell on Windows 10.

firebase -v 4.2.1

  "dependencies": {
    "firebase-admin": "^6.0.0",
    "firebase-functions": "^2.0.5"
  }

After attempting to use firebase admin from my apps code, I attempted to run the quick start example in https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase-firestore/functions/index.js.

This is the actual code run:

'use strict';

// [START all]
// [START import]
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');

admin.initializeApp({})
// [END import]

// [START addMessage]
// Take the text parameter passed to this HTTP endpoint and insert it into the
// Realtime Database under the path /messages/:documentId/original
// [START addMessageTrigger]
exports.addMessage = functions.https.onRequest((req, res) => {
// [END addMessageTrigger]
  // Grab the text parameter.
  const original = req.query.text;
  // [START adminSdkAdd]
  // Push the new message into the Realtime Database using the Firebase Admin SDK.
  return admin.firestore().collection('messages').add({original: original}).then((writeResult) => {
    // Send back a message that we've succesfully written the message
    return res.json({result: `Message with ID: ${writeResult.id} added.`});
  });
  // [END adminSdkAdd]
});
// [END addMessage]

// [START makeUppercase]
// Listens for new messages added to /messages/:documentId/original and creates an
// uppercase version of the message to /messages/:documentId/uppercase
// [START makeUppercaseTrigger]
exports.makeUppercase = functions.firestore.document('/messages/{documentId}')
    .onCreate((snap, context) => {
// [END makeUppercaseTrigger]
      // [START makeUppercaseBody]
      // Grab the current value of what was written to the Realtime Database.
      const original = snap.data().original;
      console.log('Uppercasing', context.params.documentId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an 'uppercase' sibling in the Realtime Database returns a Promise.
      return snap.ref.set({uppercase}, {merge: true});
      // [END makeUppercaseBody]
    });
// [END makeUppercase]
// [END all]

However, I still get the permission denied error.

This is the output I get:

firebase > makeUppercase({original:'alphabets'},{params:{documentId:'mydoc'}})
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: Uppercasing mydoc alphabets
info: Function crashed
info: { Error: 7 PERMISSION_DENIED: Missing or insufficient permissions.
    at Object.exports.createStatusError (C:\projects\myproject\functions\node_modules\grpc\src\common.js:87:15)
    at Object.onReceiveStatus (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:1188:28)
    at InterceptingListener._callNext (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:564:42)
    at InterceptingListener.onReceiveStatus (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:614:8)
    at callback (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:841:24)
  code: 7,
  metadata: Metadata { _internal_repr: {} },
  details: 'Missing or insufficient permissions.' }

My security rules are completely open but that did not resolve the error.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

I also thought that this might be an authentication issue so I have tried the following to initialize the app:

1

admin.initializeApp()

2

admin.initializeApp(functions.config().firebase);

3

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

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});

The last one with my own credentials file and project configuration. All of these attempts still give me the missing permissions error.

Update: I deployed these functions to the cloud and they seem to be working perfectly but when running locally, I'm still getting a Error:7 Permission Denied.

Update 2: Set application default credentials using gcloud auth application-default login as per suggestion by @Doug Stevenson. Ensured environment variable GOOGLE_APPLICATION_CREDENTIALS is not set. Attempted the code in 1,2 and 3 above as well as 4 below with no success. Encountered the same error.

4

admin.initializeApp({
    credential: admin.credential.applicationDefault(), 
    databaseURL: "https://myapp-redacted.firebaseio.com"
  });
Pollard answered 6/9, 2018 at 15:8 Comment(9)
What happens if you try to deploy and invoke the function normally (not using the emulator)?Emmer
The actual deploy works as expected. The error occurs only during emulation.Pollard
Could you edit the question to show the exact code that doesn't work the way you expect? (don't just link to it - show your actual code)Emmer
Updated the question with actual code run. Ensured 'messages' collection exists.Pollard
Do you happen to have gcloud installed or work with other cloud services?Emmer
Yes I do have it installed: Google Cloud SDK 213.0.0 beta 2018.07.16 bq 2.0.34 core 2018.08.17 gcloud gsutil 4.33 Don't really work much with other cloud services but was testing out gcloud for Google Cloud functions and BigQuery.Pollard
Try running gcloud auth application-default login. Does that help?Emmer
Still no luck. I've updated the question to reflect this attempt.Pollard
were you able to fix this? I'm getting the same error now (works when deployed, bugged on localhost)Kristopherkristos
F
6

I hope that by now you've already resolved your issue. The same thing just happened to me and I'm sharing what it was in my case with the hope that it will help others.

We manage several firebase projects - productions, dev, staging etc.

we init the admin sdk with this:

let serviceAccount = require("../serviceAccountKey.json");
const databaseUrl = functions.config().environment.databaseurl
const storageBucket = functions.config().environment.storagebucket
const isDev = "true" === functions.config().environment.dev

// if dev environment
if (isDev) {
  serviceAccount = require("../serviceAccountKey-dev.json");
}

admin.initializeApp({
    projectId: functions.config().environment.projectid,
    credential:    admin.credential.cert(serviceAccount),
    databaseURL:   databaseUrl,
    storageBucket: storageBucket
});

You know how you need to do

firebase functions:config:get > .runtimeconfig.json

for the emulation to work. Well, my runtimeconfig was containing the wrong configuration. I was loading my serviceAccountKey-dev, but I was trying to access a different project. The second I fixed my runtimeconfig - it worked for me.

Fathometer answered 9/1, 2019 at 10:0 Comment(0)
S
4

I had the same issue and was resolved by going to the cloud console then granting the role Firebase Admin SDK admin service agent to the app engine service account which is in the following format {PROJECT_ID}@appspot.gserviceaccount.com.

Salvidor answered 1/10, 2021 at 0:2 Comment(0)
C
3

Ultimately I just had to run gcloud auth application-default login to make sure I was logged in with the correct Google account.

Chandrachandragupta answered 5/10, 2019 at 5:53 Comment(0)
S
1

It is required that the client, the firebase function, to have access to the resource, firebase firestore. Following the least privilege principle you would need to:

  1. Create a role within Google Cloud IAM with the following permissions: datastore.entities.get, datastore.entities.update.
  2. Also in IAM, create a service account and assign it the recently created role.
  3. Update your firebase function selecting the new service account.

I have not found a way to assign the service account while deploying with firebase-cli. Here a guide for configuring permissions of the functions https://cloud.google.com/functions/docs/securing/function-identity.

Sladen answered 4/7, 2022 at 20:7 Comment(1)
Thank you! Worked after adding this ruleHerwin

© 2022 - 2025 — McMap. All rights reserved.