How to fix Firebase Cloud Functions Cors Error - firebase

I got on my project at least 12 cloud functions on which most of them are onRequest ones and they work perfectly. However, I just created a new one that I'm getting cors errors. Tried a bunch of things and it doesn't work.
This is what I have:
import * as functions from "firebase-functions";
import fetch from "node-fetch";
export const trkFun = functions.https.onRequest(
async (request, response) => {
const trackingNumber = request.body.trackingNumber;
const responseBody = await fetch(endPoint);
const res = await responseBody.json();
response.send(res.objetos);
});

As you may be aware cors is used to enable cross origin resource sharing. I'm assuming your error is coming from trying to trigger to OnRequest from other origins.
You can do the following just below your imports:
const cors = require('cors')({
origin: true //this will allow all origins, you can limit to to a particular domain etc. but this is a good option for a public api.
});
Then try the following:
export trkFun = functions.https.onRequest(
async (request, response) => {
cors(request, response, async () => {
//... do your things in here
}
});

Related

Why am I getting a CORS error in the browser but not in Postman?

I am using the Firebase emulators to serve Firebase functions on localhost. If I test my functions using Postman, this all works using the following request:
http://localhost:5001/project-XXXX/us-central1/api/users
Then, I fire up my Next.js application on port 3000 and try to use Axios to get the same data as follows:
useEffect(() => {
axios
.get(
"http://localhost:5001/project-XXXX/us-central1/api/users"
)
.then((res) => {
console.log(res);
})
.catch(function (error) {
console.log(error);
});
}, []);
However, now I'm getting a CORS error: "Access to XMLHttpRequest at 'http://localhost:5001/project-XXXX/us-central1/api/appointments/availability' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
In response to similar questions I have tried changing region and starting chrome with web-security disabled, which was both unsuccesful in solving the matter.
Does anyone have an idea as to why I am getting this error when requesting from the browser (and not from Postman)?
For full information, my index.js file of the Firebase cloud functions using Express.js:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const cors = require("cors");
const authMiddleware = require("./authMiddleware");
const { user } = require("firebase-functions/v1/auth");
admin.initializeApp();
const app = express();
const { users, updateUser } = require("./routes/users");
// user-related routes
app.use("/users", users);
const api = functions.https.onRequest(app);
// export as Firebase functions
module.exports = { api: api, updateUser: updateUser };
Thanks jub0bs! My problem was solved by installing cors (npm install cors) and adding the following to my index.js:
const cors = require("cors");
app.use(cors());
This enables CORS for all origins, so only use for development and change before you go to production.

Error when using Spotify access token with API through Firebase callable cloud function

I've been working on this for a while now and feel like I've read everything I can find but still can't get it to work. I'm trying to build a Firebase callable cloud function that uses axios to get a Spotify access token through client credentials auth flow and then uses that token to get data from my own account from the Spotify API. I'm using a chained function starting with axios.post and then axios.get.
The code works when it's getting the access token through axios.post but as soon as I chain an axios.get to use the token with the API something goes wrong. I'm new to Firebase and node.js so am not sure exactly how to catch the errors properly. The most common error is either a null result or a 'Unhandled error RangeError: Maximum call stack size exceeded' in the Firebase log... can't work out what either actually means for my code... With this particular version of my code I get a null result and a mass of around 50 different error logs in Firebase.
I've tried splitting the functions, using async and await and different arrangements of the headers but not a lot really changes. I've found similar questions but nothing that seemed to solve the issue. Any help would be amazing!
const functions = require("firebase-functions");
const axios = require('axios');
const qs = require('qs');
exports.spot = functions.https.onCall( async (data, context) => {
const client_id = //REMOVED;
const client_secret = //REMOVED;
const auth_token = Buffer.from(`${client_id}:${client_secret}`, 'utf-8').toString('base64');
const token_url = 'https://accounts.spotify.com/api/token';
const stringify_data = qs.stringify({'grant_type':'client_credentials'});
const api_url = 'https://api.spotify.com/v1/recommendations'
return axios
.post(token_url, stringify_data, {
headers: {
'Authorization': `Basic ${auth_token}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
grant_type: 'client_credentials'
},
json: true
})
.then(result => {
return axios.get(api_url, {
headers: {
'Authorization': `Bearer ${result.data.access_token}`,
}
})
})
.then(result => {
return result
})
.catch(error => {
console.log(error);
})
});

Firebase functions run in one firebase project but giving internal error in the other

I have two firebase accounts one used for development(D) and the other for production(P). My development(D) firestore and functions run on us-central1. On production(P) firestore location is asia-south1 and functions run on us-central1
My firebase functions run properly in development (D) but are giving me the following error in production. Further, when I check the logs on the firebase functions console, there does not seem to be any activity. It appears as if the function has not been called.
Error returned by firebase function is :
Function call error Fri Apr 09 2021 09:25:32 GMT+0530 (India Standard Time)with{"code":"internal"}
Further the client is also displaying this message :
Access to fetch at 'https://us-central1-xxx.cloudfunctions.net/gpublish' from origin 'https://setmytest.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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. zone-evergreen.js:1052 POST https://us-central1-xxx.cloudfunctions.net/gpublish net::ERR_FAILED
Here is the code from my angular app calling the function -
const process = this.fns.httpsCallable("gpublish");
process(data).subscribe(
(result) => {
console.log("function responded with result: " + JSON.stringify(result));
},
(err) => {
const date1 = new Date();
console.log("Function call error " + date1.toString() + "with" + JSON.stringify(err));
});
Here are the functions -
index.ts
import { gpublish } from "./gpublish/gpublish";
import { sendEmail } from "./sendEmail";
export {gpublish,sendEmail };
gpublish.ts
import * as functions from "firebase-functions";
const fs = require("fs");
const { google } = require("googleapis");
const script = google.script("v1");
const scriptId = "SCRIPT_ID";
const googleAuth = require("google-auth-library");
import { admin } from "../admin";
const db = admin.firestore();
export const gpublish = functions.https.onCall(async (data: any, res: any) => {
try {
const googleTest = data.test;
console.log("Publishing to google test of name " + googleTest.testName);
// read the credentials and construct the oauth client
const content = await fs.readFileSync("gapi_credentials.json");
const credentials = JSON.parse(content); // load the credentials
const { client_secret, client_id, redirect_uris } = credentials.web;
const functionsOauth2Client = new googleAuth.OAuth2Client(client_id,client_secret, redirect_uris); // Constuct an auth client
functionsOauth2Client.setCredentials({refresh_token: credentials.refresh_token}); // Authorize a client with credentials
// run the script
return runScript(functionsOauth2Client,scriptId,JSON.stringify(googleTest)
).then((scriptData: any) => {
console.log("Script data is" + JSON.stringify(scriptData));
sendEmail(googleTest, scriptData);
return JSON.stringify(scriptData);
});
} catch (err) {
return JSON.stringify(err);
}
});
function runScript(auth: any, scriptid: string, test: any) {
return new Promise(function (resolve, reject) {
script.scripts
.run({auth: auth,scriptId: scriptid, resource: {function: "doGet", devMode: true,parameters: test }
})
.then((respons: any) => { resolve(respons.data);})
.catch((error: any) => {reject(error);});
});
}
I have changed the service account key and google credentials correctly when deploying the functions in development and in production.
I have tried many things including the following:
Enabling CORS in Cloud Functions for Firebase
Google Cloud Functions enable CORS?
The function is running perfectly in Development firebase project but not in Production firebase project. Please help!
You need to check that your function has been deployed correctly.
A function that doesn't exist (404 Not Found) or a function that can't be accessed (403 Forbidden) will both give that error as the Firebase Function is never executed, which means the correct CORS headers are never sent back to the client.

CORS error when calling Firebase cloud function with httpsCallable()

I am trying to call my Firebase cloud functions from my React client.
I am able to successfully call these functions using HTTP requests (as described here). This requires setting up a full Express app in the cloud function.
Now I am trying to call the cloud functions directly from my client using httpsCallable() (as described here). It appears that this method has a couple of advantages over calling over HTTP requests. However using this approach I am getting the following CORS error:
Access to fetch at 'https://us-central1-myapp.cloudfunctions.net/helloWorld' from origin 'http://localhost:3000' has been blocked by CORS policy
How do I make this work? Is it worth the trouble? Is it really the preferred way?
Here's my cloud function:
import * as functions from 'firebase-functions';
export const helloWorld = functions.https.onRequest((request, response) => {
response.send('Hello from Firebase!');
});
Here's how I am calling it from my client:
const sayHello = async (): Promise<string> => {
const helloWorld = firebase.functions().httpsCallable('helloWorld');
const result = await helloWorld();
return result.data;
};
By doing
const helloWorld = firebase.functions().httpsCallable('helloWorld');
const result = await helloWorld();
you are indeed calling a Callable Cloud Function, but by defining the called Function as follows
functions.https.onRequest((request, response) => {})
you are defining an HTTPS Cloud Function which is different.
You should define your Cloud Function as a Callable one, as follows:
export const helloWorld = = functions.https.onCall((data, context) => {
return { response: 'Hello from Firebase!' };
});

Trying to connect to firebase function from React app - cors issue?

I'm creating a react application. I have code like this
async componentDidMount() {
const questions = await axios.get('getQuestions');
console.log(questions);
}
(I have a baseURL set up for axios and all, so the URL is correct)
I created a firebase function as follows (typescript)
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
admin.firestore().settings({ timestampsInSnapshots: true });
const db = admin.firestore();
exports.getQuestions = functions.https.onRequest(async (request, response) => {
const questions = [];
const querySnapshot = await db.collection('questions').get();
const documents = querySnapshot.docs;
documents.forEach(doc => {
questions.push(doc.data());
});
response.json({ questions: questions });
});
Now when I build and run firebase deploy --only functions, and then visit the url directly, everything works. I see my questions.
But in the react app, I get the following error
Access to XMLHttpRequest at '.../getQuestions' from origin
'http://localhost:3000' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
After some googling, I tried
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
admin.firestore().settings({ timestampsInSnapshots: true });
const db = admin.firestore();
const cors = require('cors')({ origin: true });
exports.getQuestions = functions.https.onRequest(
cors(async (request, response) => {
const questions = [];
const querySnapshot = await db.collection('questions').get();
const documents = querySnapshot.docs;
documents.forEach(doc => {
questions.push(doc.data());
});
response.json({ questions: questions });
})
);
But that just gave me an error when I ran firebase deploy --only functions
✔ functions: Finished running predeploy script. i functions:
ensuring necessary APIs are enabled... ✔ functions: all necessary
APIs are enabled i functions: preparing functions directory for
uploading...
Error: Error occurred while parsing your function triggers.
TypeError: Cannot read property 'origin' of undefined
at ...
And tbh, even if this command worked, I don't know if it is the correct solution
Got it :) I was doing something silly
import * as cors from 'cors';
const corsHandler = cors({ origin: true });
exports.getQuestions = functions.https.onRequest(async (request, response) => {
corsHandler(request, response, async () => {
const questions = [];
const querySnapshot = await db.collection('questions').get();
const documents = querySnapshot.docs;
documents.forEach(doc => {
questions.push(doc.data());
});
response.status(200).json({ questions: questions });
});
});
This answer will help someone who facing cors error.
01 - Create Firebase Function Called BtnTrigger (You can name whatever you like)
// Include Firebase Function
const functions = require('firebase-functions');
// Include Firebase Admin SDK
const admin = require('firebase-admin');
admin.initializeApp();
//cors setup include it before you do this
//run npm install cors if its not in package.json file
const cors = require('cors');
//set origin true
const corsHandler = cors({ origin: true });
//firebase function
export const BtnTrigger = functions.https.onRequest((request, response) => {
corsHandler(request, response, async () => {
//response.send("test");
response.status(200).json({ data: request.body });
});
});
Then Run firebase deploy --only functions this will create your firebase function. if you need you can check it from your Firebase Console.
02 - Create Function Trigger from your Application from your application code
i used same BtnTrigger name to understand it properly you can change variable here but httpsCallable params should same as your Firebase Function Name you created.
var BtnTrigger =firebase.functions().httpsCallable('BtnTrigger');
BtnTrigger({ key: value }).then(function(result) {
// Read result of the Cloud Function.
console.log(result.data)
// this will log what you have sent from Application BtnTrigger
// { key: value}
});
Don't Forget to import 'firebase/functions' from your Application Code

Resources