Home > front end >  Permission Denied when calling a Firebase Cloud Function from Cloud Tasks
Permission Denied when calling a Firebase Cloud Function from Cloud Tasks

Time:11-06

I am trying to run a cloud task that deletes an entry from Firestore after 60 seconds if the name of the entry is "temp". I have no problems creating the queue, and neither do I have problems enqueueing the task. I also have no permission issues running other cloud functions that I have deployed. The problem only lies when the task within the queue is meant to be invoked. I have also checked my IAM policies in GCP and they seem to all checkout, but I am not entirely sure.

Here's my cloud function:

exports.createJob = functions.firestore
    .document("users/{userId}/jobs/{jobId}")
    .onCreate(async (snap) => {
      const job = snap.data();

      /* eslint-disable */
      const token = "drTHilFCSjBLAHBLAHBLAHy5zr84zqNTSjzmewMy7jyeLTAca7ag";
      /* eslint-enable */

      // access a particular field as you would any JS property
      const name = job.name;
      const rate = job.ratePerHour;

      // check if name == "temp"
      if (name == "temp") {
        functions.logger.log("name is temp");
        // Get the project ID from the FIREBASE_CONFIG env var
        const project = JSON.parse(process.env.FIREBASE_CONFIG!).projectId;
        const location = "us-central1";
        // queue created through firebase cli. lives in gcp
        const queue = "firestore-delTempJob";

        // then get a full path to the queue
        const tasksClient = new CloudTasksClient();
        const queuePath: string =
            tasksClient.queuePath(project, location, queue);

        // configure task by passing url to callback function
        // ExpirationTaskPayload is just for type safety
        // the end of the url is whatever you will name the callback
        const url = `https://${location}-${project}.cloudfunctions.net/firestoreTtlCallback`;
        const docPath = snap.ref.path;
        functions.logger.log("docpath is -- "   docPath);
        const deletePayload: ExpirationTaskPayload = {docPath};

        // now configure the cloud task
        const task = {
          httpRequest: {
            httpMethod: "POST",
            url,
            body: Buffer.from(JSON.stringify(deletePayload)).toString("base64"),
            headers: {
              "Content-Type": "application/json",
            },
          },
          scheduleTime: {
            seconds: 60,
          },
        };

        // then enqueue the task
        try {
          await tasksClient.createTask({parent: queuePath, task});
        } catch (error) {
          functions.logger.error("delete request task creation failed:", error);
        }
      }
}

Here's the task callBack function:

interface ExpirationTaskPayload {
    docPath: string
}
export const firestoreTtlCallback =
functions.https.onRequest(async (req, res) => {
  const payload = req.body as ExpirationTaskPayload;
  try {
    await admin.firestore().doc(payload.docPath).delete();
    res.send(200);
  } catch (error) {
    functions.logger.error(error);
    res.status(500).send(error);
  }
});

Here's proof that the queue was created correctly and the task was enqueued, along with the permission denied error:

PERMISSION_DENIED(7): HTTP status code 403

Task Queue in GCP

And then here's what my GCP IAM policies look like (where I suspect the issue is): IAM Policies

CodePudding user response:

You need to authenticate the request using an OIDC token as mentioned in the documentation. Try adding oidcToken object to task.httpRequest as shown below:

const task = {
  httpRequest: {
    httpMethod: "POST",
    url,
    body: Buffer.from(JSON.stringify(deletePayload)).toString("base64"),
    headers: {
      "Content-Type": "application/json",
    },
    oidcToken: {
      serviceAccountEmail: "[email protected]",
    },
  },
  scheduleTime: {
    // Timestamp of when task will be triggered
    seconds: 60   Math.ceil(Date.now() / 1000),
  },
};
  • Related