6

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

Jake
  • 12,713
  • 18
  • 66
  • 96
  • "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 Mar 26 '19 at 08:34
  • 1
    That works for `functions.https.onRequest` because I can specify the host. This question is about [Firebase Callable Cloud Functions](https://firebase.google.com/docs/functions/callable) – Jake Mar 26 '19 at 09:12
  • 2
    Why the downvotes? – Jake Mar 26 '19 at 09:18
  • 2
    I didn't downvote – Daniel Hilgarth Mar 26 '19 at 09:45
  • Make sure the function name referenced in the client is correct, see https://stackoverflow.com/a/62042554/1030246 – tazmaniax May 14 '21 at 15:37

5 Answers5

9

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

Jake
  • 12,713
  • 18
  • 66
  • 96
  • But then we lose the auth token (and stuff) that callable functions take care of automatically. How to have the comfort of callable's and no CORS issue? Or asked differently, how exactly does your solution work? – imran Jun 24 '19 at 08:17
  • @imran, I just edited my answer with details of what I did. Hope it helps! – Jake Jun 25 '19 at 10:14
  • 1
    I'm also pushing for this to be available in the JS SDK: https://github.com/firebase/firebase-js-sdk/issues/1800 – Jake Jun 25 '19 at 10:45
  • @Jake Awesome you documented this! I am having the same issue at the moment and almost regret not sticking to the normal http functions, I thought the callable ones would be more seamless. Anyhow! I think your solution is clever, but is it not a symptom fix? Is it perhaps an idea to ask the firebase team to enable us to customise CORS headers on firebase callable functions instead? – realappie Apr 21 '20 at 10:15
2

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.

mlb
  • 661
  • 7
  • 11
  • I wasn't facing the CORS problem but this comment gave me the solution to my problem ! (which was changing the base path of the callable functions when running the app locally). – websilone Nov 17 '19 at 22:11
-1

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.
Tony Yip
  • 705
  • 5
  • 14
  • 4
    I understand CORS and preflight requests. I'm asking if there is a way to avoid them for Firebase Callable Cloud Functions. – Jake Mar 26 '19 at 09:14
  • 3
    To be clear, [you can avoid preflights for http Cloud Functions](https://firebase.google.com/docs/hosting/functions). Is there a way to do it for **Callable** Cloud Functions? – Jake Mar 26 '19 at 09:21
-1

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));
micksatana
  • 183
  • 1
  • 6
-1

I had the exact same issue: I had a custom domain and I was getting a CORS error when using a callable function.

I was looking through Firebase and saw a dropdown that was unfamiliar to me. I selected my hosting application, deleted and redeployed the function, and it was working fine without any CORS problems.

Firebase hosting setup

Yafim
  • 1
  • 2