4
votes

I have a Firebase Callable Cloud Function which I call in my javascript app in the browser.

Because the request host is ...cloudfunctions.net and not my app domain this results in a CORS preflight OPTIONS request before the real POST request.

If this was a function with http trigger I'd avoid the extra time that the preflight takes by specifying my function as a rewrite in my hosting config and sending the request to the same domain my app is hosted on.

Is there any way to avoid this preflight with Firebase Callable Cloud Functions? Perhaps there's a way to proxy the request through Firebase Hosting, like you can with http Cloud Functions

4
"Normally I'd avoid the extra time that the preflight takes by specifying my function as a rewrite in my hosting config and sending the request to the same domain my app is hosted on." -> So why not this time? - Daniel Hilgarth
That works for functions.https.onRequest because I can specify the host. This question is about Firebase Callable Cloud Functions - Jake
Why the downvotes? - Jake
I didn't downvote - Daniel Hilgarth
Make sure the function name referenced in the client is correct, see stackoverflow.com/a/62042554/1030246 - tazmaniax

4 Answers

7
votes

After combing through Firebase docs and JS SDK source I've decided this is not possible without using/overriding private APIs.

The solution I've used is to replicate the JS SDK code but specifying a URL that goes via Firebase Hosting so it's on the same domain as my app.

Same Cloud Function, same app code, no CORS preflight 👍🏼


  1. Create a normal Firebase Callable Cloud Function
  2. Add a rewrite to firebase.json
{
 ...
 "hosting": {
   ...
   "rewrites": [
      {
        "source": "myFunction",
        "function": "myFunction"
      }
   ]
 }
}
  1. Instead of calling it with firebase.functions().httpsCallable('myFunction') send a POST request to your own new URL
const token = await firebase.auth().currentUser.getIdToken()
const response = await fetch(
  'https://myapp.web.app/myFunction',
  {
    headers: {
      'Content-Type': 'application/json',
      Authorization: 'Bearer ' + token
    },
    method: 'post',
    body: JSON.stringify({ data })
  }
)

Now the URL is within your domain so no CORS issues

2
votes

For users who have the CORS preflight problem with the firebase local emulator, if you're using Webpack or Vue (vue-cli), you can resolve the situation with a proxy:

my-firebase-initialize-file.js (client side)

firebase.initializeApp(...); // your config
firebase.functions().useFunctionsEmulator('http://localhost:1234/api'); // Client url + /api

webpack.config.js or vue.config.js (client side, at the root, next to package.json)

module.exports = {
  devServer: {
    proxy: {
      "^/api": {
        target: "http://localhost:5001", // Emulator's url
        pathRewrite: {
          "^/api": ""
        },
        ws: true,
        changeOrigin: true
      }
    }
  }
};

Now every http://localhost:1234/api will be proxied to http://localhost:5001, so you don't need CORS preflight anymore.

Of course you need to adapt these local urls to your case.

0
votes

If you are using specific region on server-side, make sure to specify region at client-side as well. Hope this help.

Firebase Functions

exports.status = functions
  .runWith({
    memory: '128MB',
    timeoutSeconds: 15
  })
  .region('asia-east2')
  .https.onCall((data, context) => {
    return { message: 'OK' };
  });

Web client

let functions = firebase.functions('asia-east2');
let status = functions.httpsCallable('status');
status({}).then(res => console.log(res));
-1
votes

Due to security, it is not possible to skip to preflighted request

Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests

Preflighted request would skip when meets all the following conditions:

  • The only allowed methods are:
    • GET
    • HEAD
    • POST
  • Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (but note the additional requirements below)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • The only allowed values for the Content-Type header are:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
  • No ReadableStream object is used in the request.