How to solve CORS in firebase functions enviroment?

18,613

Solution 1

For someone who will meet this issue in the future (or my future self):

If you've already configured CORS using cors package, and you think you configured it correctly, and still have CORS error in the browser console, check this article:

https://haha.world/firebase-cors/

Basically, it's a misleading error returns from Google Cloud Functions, when actually the error is inside your code logic (which is totally not related to CORS at all)

So the first step to fix this bug is to check Google Cloud Functions logs (or Firebase Cloud Functions logs) to see if your function crashed because of any error in your code. Then fix it.


Note: For someone that doesn't have the issue that I described above, you can see other answers, or check these resources:

  1. https://expressjs.com/en/resources/middleware/cors.html
  2. https://firebase.google.com/docs/functions/http-events#using_existing_express_apps

Solution 2

Within your Firebase Function response header you could explicitly allow all origins:

exports.handler = ((req, res) => {
    res.set({ 'Access-Control-Allow-Origin': '*' }).sendStatus(200)
})

Or you could modify this to allow only specific origins. This is generally how I have worked around CORS issues with Firebase funcitons in the past.

Solution 3

Check out https://cloud.google.com/functions/docs/writing/http#handling_cors_requests . From that document -

exports.corsEnabledFunction = (req, res) => {
  // Set CORS headers
  // e.g. allow GETs from any origin with the Content-Type header
  // and cache preflight response for an 3600s
  res.set("Access-Control-Allow-Origin", "*");
  res.set("Access-Control-Allow-Methods", "GET");
  res.set("Access-Control-Allow-Headers", "Content-Type");
  res.set("Access-Control-Max-Age", "3600");
  // Send response to OPTIONS requests and terminate the function execution
  if (req.method == 'OPTIONS') {
    res.status(204).send('');
  }
  // Continue with function code
  ...
}
Share:
18,613
mrsalitre
Author by

mrsalitre

Updated on July 26, 2022

Comments

  • mrsalitre
    mrsalitre almost 2 years

    I´m runnig into CORS issues. In my functions/index.js I have:

    const cors = require('cors')({
      origin: true
    });
    

    My endpoint is https://sis-t.redsys.es:25443/sis/realizarPago. I need to do a POST to this url with apropiate parameters from the client, and this is outside firebase enviroment. I also have the correct plan to allow external network, but making a request to a different address than the origin one, triggers a CORS problem:

    I have read that you only need to modify the headers, but that only applies if you are making a request to your own server. When you do the http.onRequest(), you can use a middleware inside the function, but what happens when you make a POST to an external server?

    This is the axios functions that do the POST:

    cardPay: function () {
      this.cardProcess = true
        axios({
          method: 'post',
          url: 'https://us-central1-cutre-windrider.cloudfunctions.net/cardPay',
          data: {
            verifiedUserLogged: this.userLogged.uid,
            cart: this.cartItemList,
            finalPrice: this.serverPrice,
            deliveryInfo: this.userLogged.deliveryAdress,
            name: this.userLogged.displayName || false,
            email: this.userLogged.email
          }
        })
        .then(response => {
          this.requestData = response
          this.redsysRedirect(response.data.data)
        })
        .catch(console.log)
        },
    redsysRedirect: function (data) {
      axios.post('https://sis-t.redsys.es:25443/sis/realizarPago', {
        'Ds_SignatureVersion': 'HMAC_SHA256_V1',
        'Ds_MerchantParameters': data.merchantParameters,
        'Ds_Signature': data.signature
      }).then(console.log).catch(console.log)
    

    These are the server side functions:

    exports.cardPay = functions.https.onRequest((req, res) => {
      return cors(req, res, () => {
        const cart = req.body.cart
        const user = req.body.verifiedUserLogged
        const key = admin.database().ref(`sales/${user}`).push().key
        processCart(cart).then(result => {
          console.info(createPayment(result, key))
          return res.json({ "data": createPayment(result, key) }).end()
        }).catch(console.log)
      })
    })
    
    function processCart(cart) {
      return new Promise((resolve, reject) => {
        Promise.all(cart.map(i => switcher(i)))
          .then(prices => resolve(
            prices.reduce(
              (finalPrice, price) => price + finalPrice, 0)
          )).catch(console.log)
      });
    }
    
    function switcher(item) {
      switch (item.destiny) {
        case 'bookedLessons':
            return lessonPrice(item.name, item.index)
          case 'bookedRentals':
            return rentalPrice(item.id, item.index, item.insurancePurchased, item.insuranceId)
          case 'bookedLodgins':
            return item.reservationData ? roomPriceWithReservation(item.id, item.quantity, item.persons, item.reservationData) : roomPriceNoReservation(item.id, item.quantity, item.persons)
          case 'deliveries':
            return productPrice(item.id, item.quantity)
          case 'bookedCar':
            return carPrice(item.id, item.index)
          case 'bookedStorage':
            return storagePrice(item.index)
          case 'bookedTransportation':
            return transportationPrice(item.id, item.index, item.persons, item.roundTrip)
          case 'bookedDoublePack':
            return doublePack(item.id, item.persons)
          case 'bookedTriplePack':
            return triplePack(item.id, item.persons)
          default:
            break
      }
    }
    
    function createPayment(total, orderId) {
      let redsys = new Redsys();
      let mParams = {
          "DS_MERCHANT_AMOUNT":total.toString(),
          "DS_MERCHANT_ORDER":orderId,
          "DS_MERCHANT_MERCHANTCODE":   "025988262",
          // "DS_MERCHANT_MERCHANTCODE":tpvInfo.fucCode,
          "DS_MERCHANT_CURRENCY":"978",
          // "DS_MERCHANT_CURRENCY":tpvInfo.currency,
          "DS_MERCHANT_TRANSACTIONTYPE":"0",
          // "DS_MERCHANT_TRANSACTIONTYPE":tpvInfo.transaction_type,
          "DS_MERCHANT_TERMINAL":   "001",
          // "DS_MERCHANT_TERMINAL":tpvInfo.terminal,
          "DS_MERCHANT_MERCHANTURL":'http://localhost:8080',
          "DS_MERCHANT_URLOK":'http://localhost:8080/home?foo=true',
          "DS_MERCHANT_URLKO":'http://localhost:8080/home?foo=false'
      };
      return  {signature: redsys.createMerchantSignature(/* tpvInfo.secret */   "sq7HjrUOBfKmC576ILgskD5srU870gJ7", mParams) , merchantParameters: redsys.createMerchantParameters(mParams), raw: mParams};
    }
    
  • mrsalitre
    mrsalitre over 5 years
    In the documentation you attached, specify how to handle CORS when using firebase functions: yourdomain.com make requests to region-project.cloudfunctions.net/yourfunction, but this is not the case, In that case you can access yourfunction, but I have to make a POST to a site that is not a function. Unless you're referring to configuring them globally, by the way, I'm not using Express.
  • R. Wright
    R. Wright over 5 years
    Any way you can add these same headers to the response from sis-t.redsys.es:25443/sis/realizarPago? If so, this should work even if it isn't cloud functions.
  • mrsalitre
    mrsalitre over 5 years
    and then the response res.json({foo: bar})? because this overwrites the headers, and cause a Can't set headers after they are sent
  • mrsalitre
    mrsalitre over 5 years
    sis-t.redsys.es:25443/sis/realizarPago is just the url to where I need to POST, I won't get a response until the request is made to that url. Using the anwser of @Jason Dark: res.set({ 'Access-Control-Allow-Origin': '*' }).sendStatus(200) return res.send(200, { "data": createPayment(result, key) }).end(); this cause a Can't set headers after they are sent.
  • R. Wright
    R. Wright over 5 years
    What happens when you try what I mentioned above? The answer from @jasonDark has a flaw in that it sends a response code all the time which is why you're getting the error. Also, it doesn't send a 204 status code in response to the preflight core options. It might be worth reading through developer.mozilla.org/en-US/docs/Web/HTTP/CORS for more info on CORS.
  • Jason Dark
    Jason Dark over 5 years
    Yes, if you wanted to send a json response you could do this: res.set({ 'Access-Control-Allow-Origin': '*' }).status(200).json({foo: bar})
  • SuperUberDuper
    SuperUberDuper over 4 years
    Access-Control-Allow-Origin': '*' is pretty dangerous, is there not a safer way to test your functions from a local app?
  • idophir
    idophir about 4 years
    For me, the issue was that I needed to re-export the Google credentials file. If CORS worked for you before, ignore this error and look for the issue elsewhere.
  • Roland
    Roland about 4 years
    watch out for "Unhadled rejection" in your firebase logs
  • Gino
    Gino almost 3 years
    I've spent way too much time looking for this! Well done
  • William
    William almost 3 years
    wow. that was it. error in function, returning cors error.
  • Claus
    Claus almost 3 years
    Absolutely true! In my case I was calling the "test" function using "Test" string and I was getting a CORS related error.