I’m attempting to call a callable cloud function (which is already deployed) from a client app and getting this error on the GCP logs:
{
httpRequest: {9}
insertId: "647865c20002422d2d32b259"
labels: {1}
logName: "projects/faker-app-flutter-firebase-dev/logs/run.googleapis.com%2Frequests"
receiveTimestamp: "2023-06-01T09:32:50.154902339Z"
resource: {2}
severity: "WARNING"
spanId: "11982344486849947204"
textPayload: "The request was not authorized to invoke this service. Read more at https://cloud.google.com/run/docs/securing/authenticating Additional troubleshooting documentation can be found at: https://cloud.google.com/run/docs/troubleshooting#401"
timestamp: "2023-06-01T09:32:50.138090Z"
trace: "projects/faker-app-flutter-firebase-dev/traces/ddcb5a4df500af085b7a7f6f89a72ace"
traceSampled: true
}
The same function works correctly from the Firebase Local Emulator, so I assume this is a permissions issue related to IAM and service accounts (I still don’t understand too well how IAM works).
Here is my code:
import * as admin from "firebase-admin"
import * as functions from "firebase-functions/v2"
import * as logger from "firebase-functions/logger";
// https://github.com/firebase/firebase-tools/issues/1532
if (admin.apps.length === 0) {
admin.initializeApp()
}
export const deleteAllUserJobs = functions.https.onCall(async (context: functions.https.CallableRequest) => {
const uid = context.auth?.uid
if (uid === undefined) {
throw new functions.https.HttpsError("unauthenticated", "You need to be authenticated to perform this action")
}
const firestore = admin.firestore()
const collectionRef = firestore.collection(`/users/${uid}/jobs`)
const collection = await collectionRef.get()
logger.debug(`Deleting ${collection.docs.length} docs at "/users/${uid}/jobs"`)
// transaction version
await firestore.runTransaction(async (transaction) => {
for (const doc of collection.docs) {
transaction.delete(firestore.doc(`/users/${uid}/jobs/${doc.id}`))
}
})
logger.debug(`Deleted ${collection.docs.length} docs at "/users/${uid}/jobs"`)
return {"success": true}
})
The function was deployed with firebase deploy --only functions
, and I made sure the client app calls this function when the user is already authorized.
According to the docs:
If you encounter permissions errors when deploying functions, make sure that the appropriate IAM roles are assigned to the user running the deployment commands.
The docs also link to this page, which says:
Cloud Functions for Firebase permissions
For a list and descriptions of Cloud Functions permissions, refer to
the IAM documentation.Be aware that the deployment of functions requires a specific
configuration of permissions that aren’t included in the standard
Firebase predefined roles. To deploy functions, use one of the
following options:Delegate the deployment of functions to a project Owner. If you're deploying only non-HTTP functions, then a project Editor can deploy your functions. Delegate deployment of functions to a project member who has the following two roles: Cloud Functions Admin role (roles/cloudfunctions.admin) Service Account User role (roles/iam.serviceAccountUser) A project Owner can assign these roles to a project member using the Google Cloud Console or gcloud CLI. For detailed steps and
security implications for this role configuration, refer to the IAM
documentation.
But like I said, I can successfully deploy the function. It’s when I try to execute it that I get an error log.
In summary, what I’m trying to do is quite basic:
- write a callable cloud function
- deploy it
- call it from the client app
When the function runs, it fails with the error above.
Any advice? Do I need to set a specific IAM role?
2
Answers
After much trial and error, I managed to get it working by adding the "Cloud Functions Invoker" role to the
firebase-service-account@firebase-sa-management.iam.gserviceaccount.com
principal in the IAM & Admin page:Open
https://console.cloud.google.com/iam-admin/<project_name>
, find the service account you are using in your firebase project and add the Rol "Cloud Functions Invoker".Is like Admin, Editor or Viewer roles are about manipulating the function on GCP (don’t allow you to use it) and Invoker allows that account to invoke the function.