Reputation: 437
I have used firebase functions with node js before and consistently run similar functions as below. However, I am now getting this error all of a sudden in my console:
Access to fetch at 'https://us-central1-myapp.cloudfunctions.net/test2' from origin 'http://localhost:8383' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
POST https://us-central1-myapp.cloudfunctions.net/test2 net::ERR_FAILED
This is my code I don't know what I am doing wrong:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const nodemailer = require('nodemailer');
const {Storage} = require('@google-cloud/storage');
var PizZip = require('pizzip');
var Docxtemplater = require('docxtemplater');
admin.initializeApp();
const BUCKET = 'gs://myapp.appspot.com';
const cors = require('cors');
const https = require('https');
const express = require('express');
const app = express();
app.use(cors({origin: true}));
const storage = new Storage({
projectId: 'myapp' });
// The error object contains additional information when logged with JSON.stringify (it contains a properties object containing all suberrors).
function replaceErrors(key, value) {
if (value instanceof Error) {
return Object.getOwnPropertyNames(value).reduce(function(error, key) {
error[key] = value[key];
return error;
}, {});
}
return value;
}
function errorHandler(error) {
console.log(JSON.stringify({error: error}, replaceErrors));
if (error.properties && error.properties.errors instanceof Array) {
const errorMessages = error.properties.errors.map(function (error) {
return error.properties.explanation;
}).join("\n");
console.log('errorMessages', errorMessages);
// errorMessages is a humanly readable message looking like this :
// 'The tag beginning with "foobar" is unopened'
}
throw error;
}
function readStreamToBuffer(stream) {
return new Promise((resolve, reject) => {
const chunkArray = [];
stream
.on('data', (chunk) => chunkArray.push(chunk))
.on('error', (err) => {
chunkArray = [];
reject(err);
})
.on('end', () => {
const buffer = Buffer.concat(chunkArray);
chunkArray = [];
resolve(buffer);
});
});
}
exports.test2 = functions.https.onCall(async(data, context) => {
const file_name = 'tag-example.docx';
const storageRef = storage.bucket(BUCKET).file(file_name);
const buffer = await readStreamToBuffer(storageRef.createReadStream());
const zip = new PizZip(buffer);
let doc;
try {
doc = new Docxtemplater(zip);
} catch(error) {
// Catch compilation errors (errors caused by the compilation of the template : misplaced tags)
errorHandler(error);
}
doc.setData({
first_name: 'Fred',
last_name: 'Flinstone',
phone: '0652455478',
description: 'Web app'
});
try {
doc.render();
} catch (error) {
errorHandler(error);
}
const contentBuffer = doc.getZip()
.generate({type: 'nodebuffer'});
console.log(doc);
const targetStorageRef = storage.bucket(BUCKET).file("compiled.docx");
await targetStorageRef.put(contentBuffer);
});
Is there someway to get that error that shows up in my client side console to go away and my cloud function to run? any help will be appreciated
Upvotes: 1
Views: 5397
Reputation: 217
I had the same problem and the issue was actually related to "user permissions". The documentation says
As of January 15, 2020, all HTTP functions by default require most invokers >to be authenticated. https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_http_function_invocation
Solution
allUsers
To make your function public, you can either deploy it with the --allow-unauthenticated flag, or use the Console to grant the Cloud Functions Invoker role to allUsers. Then handle CORS and authentication in the function code. https://cloud.google.com/functions/docs/writing/http#limitations
Service Accounts
tab.For both solutions, the problem was that by following the docs I could not get to the list of functions. So make sure you go to Cloud Functions
in order to change/add permissions
Click on the function you want, then Add
to insert a new Principal
Principal
Cloud functions invoker
roleUpvotes: 2
Reputation: 50900
First thing that I notice is you are using callable cloud functions but the express app is using the cors middleware here: app.use(cors({origin: true}))
and there no routes in that app. I don't think you need to explicitly import express and CORS when using callable functions so try removing them. Quoting from here, "CORS should be automatically handled by the onCall handler".
If you want to use Express and CORS middleware like that, you should try onRequest functions as shown below:
const express = require('express');
const cors = require('cors');
const app = express();
// Automatically allow cross-origin requests
app.use(cors({ origin: true }));
// build multiple CRUD interfaces:
app.get('/test2', (req, res) => {
//Handle your test2 methods here
return res.send('your response')
});
// Expose Express API as a single Cloud Function:
exports.api = functions.https.onRequest(app);
Now in this case the CORS middleware you wrote will be running. Your function's URL may look something like - https://us-central1-myapp.cloudfunctions.net/api
Then to use the /test2
route it becomes https://us-central1-myapp.cloudfunctions.net/api/test2
. CORS shouldn't be an issue anymore here but do note it's an express app now so the parameters in the function are Request, Response
instead of data, context
.
Additionally, I see your are calling the API from localhost so using Firebase Functions Emulator may be a good idea. To use specify to use the emulator like this: firebase.functions().useFunctionsEmulator('http://localhost:5001')
Upvotes: 2