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
And then here's what my GCP IAM policies look like (where I suspect the issue is):
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),
},
};