182

I'm currently learning how to use new Cloud Functions for Firebase and the problem I'm having is that I can't access the function I wrote through an AJAX request. I get the "No 'Access-Control-Allow-Origin'" error. Here's an example of the function I wrote:

exports.test = functions.https.onRequest((request, response) => {
  response.status(500).send({test: 'Testing functions'});
})

The function sits in this url: https://us-central1-fba-shipper-140ae.cloudfunctions.net/test

Firebase docs suggests to add CORS middleware inside the function, I've tried it but it's not working for me: https://firebase.google.com/docs/functions/http-events

This is how I did it:

var cors = require('cors');    

exports.test = functions.https.onRequest((request, response) => {
   cors(request, response, () => {
     response.status(500).send({test: 'Testing functions'});
   })
})

What am I doing wrong? I would appreciate any help with this.

UPDATE:

Doug Stevenson's answer helped. Adding ({origin: true}) fixed the issue, I also had to change response.status(500) to response.status(200) which I completely missed at first.

chainicko
  • 461
  • 2
  • 13
Andrey Pokrovskiy
  • 1,916
  • 2
  • 10
  • 10
  • Also a sample in the docs [here](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-headers) – Kato Apr 24 '17 at 21:12
  • I have some functions that work with the solution provided but now am trying a new function which essentially adds open graphs to the top of my index.html and returns the updated index.html and I can't get it to work :( keep getting the ACCESS-CONTROL--- error – TheeBen Apr 26 '18 at 22:59
  • 2
    wrapping the incoming request in cors() like above was the only thing that worked for me – Charles Harring Apr 19 '20 at 01:34
  • can you edit your "update" to underline that the cors middleware is required? This will save some people some time – Antoine Weber Jul 16 '20 at 15:36

27 Answers27

190

There are two sample functions provided by the Firebase team that demonstrate the use of CORS:

The second sample uses a different way of working with cors than you're currently using.

Consider importing like this, as shown in the samples:

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

And the general form of your function will be like this:

exports.fn = functions.https.onRequest((req, res) => {
    cors(req, res, () => {
        // your function body here - use the provided req and res from cors
    })
});
Doug Stevenson
  • 236,239
  • 27
  • 275
  • 302
  • 4
    It looks like this is where the whitelist of domains to allow access is defined? And setting `origin: true` allows any domain to access? (https://www.npmjs.com/package/cors) @Doug Stevenson Do you think firebase could write up a doc on the basics needed for client/server https functions? The samples repo is good, but we had missed this extra bit of require. – Alan Aug 25 '17 at 09:40
  • 12
    To anyone willing to add CORS support to their back-ends: please make sure you understand the consequences and how to properly configure it. "origin: true" is cool for testing but it defeats the whole purpose :) – dSebastien Apr 17 '18 at 19:30
  • @dSebastien, could you give an example of cors with specific origin which I assume that's what you mean it should be used? I have problem getting it to work – TheeBen Apr 26 '18 at 23:01
  • Hey Doug, I've posted a question on Firebase, it's been awhile with no answer and am kind of desperate for it otherwise wouldn't bug you here. Appreciate any comment/reply. https://stackoverflow.com/q/50053798/5601401 – TheeBen May 10 '18 at 15:07
  • 1
    google cloud functions do not allow the wildcard origin: https://cloud.google.com/functions/docs/writing/http#authentication_and_cors – Corey Cole Sep 19 '19 at 21:00
  • 1
    Just a reminder for everyone still importing cors like Doug brilliantly mentioned, don't forget to wrap the response like Andrey did, otherwise it won't work! – Gustavo Garcia Apr 21 '20 at 19:24
  • TypeError: 'origin' is undefined. – Antonio Ooi May 06 '20 at 10:20
  • 1
    As others have mentioned, can you update your answer to point out that cors middleware is indeed required? If we skim through your answer, we think that the only step needed is to write `const cors = require('cors')({origin: true});`, but it's not the case – Antoine Weber Jul 16 '20 at 15:37
  • response.set('Access-Control-Allow-Origin', '*'); this works fine for me. Do check this solution by @deanwilliammills – Alok Prusty Sep 23 '20 at 13:21
  • Doug, you forgot the `return` keyword in your snippet. It should be: `return cors(req, res, () => {..` instead. – Antoine Weber Oct 05 '20 at 01:05
  • @AntoineWeber So, what happens if you don't add it? HTTP functions don't require a return value. They just require a response be sent via the `res` parameter. – Doug Stevenson Oct 05 '20 at 01:10
  • Ok it works without the `return`. But the sample function uses `return` for some reason. I guess I got confused by that inconsistency – Antoine Weber Oct 05 '20 at 01:20
  • 1
    @AntoineWeber Yes, I know the author of those samples, and I disagreed with him over the use of unnecessary and misleading returns throughout, but nothing changed. ¯\\_(ツ)_/¯ – Doug Stevenson Oct 05 '20 at 01:27
  • ANd how would this same problem be solved for "onCall" Cloud functions? Because these functions are not quite like express endpoints and I don't have to define middleware supposedly – ppicom Oct 23 '20 at 06:39
  • I needed to `npm i cors` and `@types/cors` for this work. Thank you! – Cooper Scott Dec 08 '20 at 18:14
101

You can set the CORS in the cloud function like this

response.set('Access-Control-Allow-Origin', '*');

No need to import the cors package

deanwilliammills
  • 1,763
  • 1
  • 14
  • 27
57

For anyone trying to do this in Typescript this is the code:

import * as cors from 'cors';
const corsHandler = cors({origin: true});

export const exampleFunction= functions.https.onRequest(async (request, response) => {
       corsHandler(request, response, () => {});
       //Your code here
});
Yayo Arellano
  • 2,626
  • 18
  • 19
  • 5
    Solution will make you lose logging on cloud functions (very bad) and proper async / await functionality, you risk the function content being prematurely ended inside the callback on long calls. – Oliver Dixon Dec 16 '18 at 21:30
  • 2
    google cloud functions do not allow the wildcard origin: https://cloud.google.com/functions/docs/writing/http#authentication_and_cors – Corey Cole Sep 19 '19 at 21:00
  • 2
    @YayoArellano , thanks. Your answer helped me, however I made a slight change: `corsHandler(request, response, () => { YOUR CODE HERE });` – CloudWindMoonSun Dec 17 '20 at 11:53
  • @OliverDixon do you have a better solution that does not have the downside of possibly risking the function content to being prematurely ended during long calls? Probably wrapping it in a Promise that only resolves once the callback "completed"? – sceee May 06 '21 at 11:59
37

One additional piece of info, just for the sake of those googling this after some time: If you are using firebase hosting, you can also set up rewrites, so that for example a url like (firebase_hosting_host)/api/myfunction redirects to the (firebase_cloudfunctions_host)/doStuff function. That way, since the redirection is transparent and server-side, you don't have to deal with cors.

You can set that up with a rewrites section in firebase.json:

"rewrites": [
        { "source": "/api/myFunction", "function": "doStuff" }
]
Pablo Urquiza
  • 481
  • 5
  • 5
  • 2
    imo, this is the best answer, since it solves the actual problem without adding any additional security problems. This way the cloud functions are served from the same domain as the rest and you dont even need any cors. – koljaTM Feb 03 '19 at 15:29
  • 4
    This is a great feature indeed, but it currently only works if the functions live in the default region (us-central1). I wanted to deploy my functions to europe-west1 for latency reasons and ran into this issue: https://github.com/firebase/firebase-tools/issues/842 – Alex Suzuki Jun 26 '19 at 14:21
  • 1
    The redirect works fine and makes the URL cleaner, but I haven't figured out how to pass GET parameters. The function (after rewrite) seems to be called without parameters. – royappa Jul 23 '19 at 13:57
23

No CORS solutions worked for me... till now!

Not sure if anyone else ran into the same issue I did, but I set up CORS like 5 different ways from examples I found and nothing seemed to work. I set up a minimal example with Plunker to see if it was really a bug, but the example ran beautifully. I decided to check the firebase functions logs (found in the firebase console) to see if that could tell me anything. I had a couple errors in my node server code, not CORS related, that when I debugged released me of my CORS error message. I don't know why code errors unrelated to CORS returns a CORS error response, but it led me down the wrong rabbit hole for a good number of hours...

tl;dr - check your firebase function logs if no CORS solutions work and debug any errros you have

tbone849
  • 707
  • 5
  • 15
  • 2
    this drove me crazy. in my case it wasn't even error in code! it was ```Error: quota exceeded (Quota exceeded for quota group 'NetworkIngressNonbillable' and limit 'CLIENT_PROJECT-1d' of service 'cloudfunctions.googleapis.com ``` so basically free quota was exceeded and functions returned cors error – Stanislau Buzunko Oct 17 '19 at 19:16
  • 1
    Happend a couple of times here, same error is returned from the server aswell as cors: Error: internal is basically the error. This error will also happen if you run the wrong function, for example mistyping a function name – Henrik Bøgelund Lavstsen May 13 '20 at 02:13
  • When you try to request for Google reCAPTCHA verification within cloud function, the browser throws you the CORS error too. When I check the Firebase Console function log, it says `access to external network resources not allowed if the billing account is not enabled`. After enabling the billing account, it works perfectly. This is also one of the non-cors related examples but a cors error is thrown. – Antonio Ooi May 27 '20 at 09:52
  • In my case, the issue was that I wasn't logged in under the correct Firebase project (`firebase use ` on the command-line) before running the emulator. – Jaromír Adamec Feb 04 '21 at 19:24
20

I have a little addition to @Andreys answer to his own question.

It seems that you do not have to call the callback in the cors(req, res, cb) function, so you can just call the cors module at the top of your function, without embedding all your code in the callback. This is much quicker if you want to implement cors afterwards.

exports.exampleFunction = functions.https.onRequest((request, response) => {
    cors(request, response, () => {});
    return response.send("Hello from Firebase!");
});

Do not forget to init cors as mentioned in the opening post:

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

Update: Any response function that takes time risk a CORS error with this implementation because this doesn't have the appropriate async/await. Don't use outside of quick prototyping endpoints that return static data.

Kevin Danikowski
  • 2,444
  • 1
  • 23
  • 42
Jaap Weijland
  • 2,367
  • 4
  • 18
  • 28
  • 1
    this worked when other SO answers with setting the headers manually did not – Jim Factor May 28 '18 at 21:48
  • This works but it can cause TSlint error if you had it enabled and you cannot deploy to firebase. Put the response inside the cors closure to overcome it `cors(request, response, () => { return response.send("Hello from Firebase!"); });` – Spiral Out Sep 15 '18 at 15:01
  • 2
    2 errors here guys. First one. Anything after the cors function will run twice (since the first request is preflight). Not good. Second, @SpiralOut your solution will make you lose logging on cloud functions (very bad) and proper async / await functionality, you risk the function content being prematurely ended inside the callback. – Oliver Dixon Dec 16 '18 at 20:53
  • @SpiralOut you can simply disable tslint – Vlad Mar 17 '19 at 05:06
  • 2
    Having learned a lot about gcf in the last year, I wouldn’t recommend this answer anymore. It could be handy for quick prototypes, but avoid this in real production cases – Jaap Weijland Mar 17 '19 at 16:37
  • @SpiralOut i mean remove tslint command from firebase build script only – Vlad Mar 22 '19 at 18:58
  • 2
    @JaapWeijland please update your answer with any info why this approach is bad for production code. As eventually all (seemingly working) examples from stackoverflow sooner or later end up in production... – Simon B. Aug 11 '20 at 11:48
12

This might be helpful. I created firebase HTTP cloud function with express(custom URL)

const express = require('express');
const bodyParser = require('body-parser');
const cors = require("cors");
const app = express();
const main = express();

app.post('/endpoint', (req, res) => {
    // code here
})

app.use(cors({ origin: true }));
main.use(cors({ origin: true }));
main.use('/api/v1', app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({ extended: false }));

module.exports.functionName = functions.https.onRequest(main);

Please make sure you added rewrite sections

"rewrites": [
      {
        "source": "/api/v1/**",
        "function": "functionName"
      }
]
Sandy
  • 459
  • 4
  • 13
  • 1
    To anyone reading this considering putting express inside their cloud function - just don't. Spinning up express takes up extra resources, etc, and you already have all that functionality implemented. Firebase functions !== node. – David May 14 '21 at 06:43
  • It may literally spin up a new express server *per request*. I'm really not sure why they have this stuff in the official docs. – David May 14 '21 at 06:49
10

Found a way to enable cors without importing any 'cors' library. It also works with Typescript and tested it in chrome version 81.0.

exports.createOrder = functions.https.onRequest((req, res) => {
// browsers like chrome need these headers to be present in response if the api is called from other than its base domain
  res.set("Access-Control-Allow-Origin", "*"); // you can also whitelist a specific domain like "http://127.0.0.1:4000"
  res.set("Access-Control-Allow-Headers", "Content-Type");

  // your code starts here

  //send response
  res.status(200).send();
});
GorvGoyl
  • 27,835
  • 20
  • 141
  • 143
  • This worked for me adding `res.set("Access-Control-Allow-Origin", "*")`; only did not work `res.set("Access-Control-Allow-Headers", "Content-Type");` solved my issue – Amina Darwish Aug 27 '20 at 07:24
  • This did not work for me, because it didn't support the OPTIONS method "preflight check" before the GET/POST request. I had to switch to the `cors` package (or recreate the `OPTIONS` special response, which ended up being more trouble than it's worth) – zeroasterisk Sep 29 '20 at 04:22
8

If you don't/can't use cors plugin, calling the setCorsHeaders() function first thing in the handler function will also work.

Also use the respondSuccess/Error functions when replying back.

const ALLOWED_ORIGINS = ["http://localhost:9090", "https://sub.example.com", "https://example.com"]


// Set CORS headers for preflight requests
function setCorsHeaders (req, res) {
  var originUrl = "http://localhost:9090"


  if(ALLOWED_ORIGINS.includes(req.headers.origin)){
    originUrl = req.headers.origin
  }

  res.set('Access-Control-Allow-Origin', originUrl);
  res.set('Access-Control-Allow-Credentials', 'true');

  if (req.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    res.set('Access-Control-Allow-Methods', 'GET,POST','PUT','DELETE');
    res.set('Access-Control-Allow-Headers', 'Bearer, Content-Type');
    res.set('Access-Control-Max-Age', '3600');
    res.status(204).send('');
  }
}

function respondError (message, error, code, res) {
  var response = {
    message: message,
    error: error
  }
  res.status(code).end(JSON.stringify(response));
}


function respondSuccess (result, res) {
  var response = {
    message: "OK",
    result: result
  }
  res.status(200).end(JSON.stringify(response));
}
KasparTr
  • 1,796
  • 2
  • 20
  • 43
5

I have just published a little piece on that:

https://mhaligowski.github.io/blog/2017/03/10/cors-in-cloud-functions.html

Generally, you should use Express CORS package, which requires a little hacking around to meet the requirements in GCF/Firebase Functions.

Hope that helps!

mhaligowski
  • 2,080
  • 18
  • 23
  • 4
    Not sure what you mean by hacking ? Care to elaborate a bit? Read your post but I don't see you mentioning it – TheeBen Apr 26 '18 at 23:03
  • 1
    author of the cors module here; by "hacking" mhaligowski simply meant that he had to wrap the call to the cors module to make it match the way Express calls middleware (i.e. supply a function as third parameter after req & res) – Troy Mar 19 '20 at 18:40
  • That link is broken https://mhaligowski.github.io/blog/2017/03/10/cors-in-cloud-functions.html this is why its better to put the content(summary) of external resources rather than external links – rahulserver Sep 11 '20 at 04:54
5

Only this way works for me as i have authorization in my request:

exports.hello = functions.https.onRequest((request, response) => {
response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true'); // vital
if (request.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    response.set('Access-Control-Allow-Methods', 'GET');
    response.set('Access-Control-Allow-Headers', 'Content-Type');
    response.set('Access-Control-Max-Age', '3600');
    response.status(204).send('');
} else {
    const params = request.body;
    const html = 'some html';
    response.send(html)
} )};
Gleb Dolzikov
  • 556
  • 5
  • 10
5

If there are people like me out there: If you want to call the cloud function from the same project as the cloud function it self, you can init the firebase sdk and use onCall method. It will handle everything for you:

exports.newRequest = functions.https.onCall((data, context) => {
    console.log(`This is the received data: ${data}.`);
    return data;
})

Call this function like this:

// Init the firebase SDK first    
const functions = firebase.functions();
const addMessage = functions.httpsCallable(`newRequest`);

Firebase docs: https://firebase.google.com/docs/functions/callable

If you can't init the SDK here is the essence from the other suggestions:

Chronnie
  • 155
  • 4
  • 12
4

Simple solution using the Google Cloud Console Dashboard:

  1. Go to your GCP console dashboard:

https://console.cloud.google.com/home/dashboard

  1. Go to menu

"Cloud Functions" ("Compute" section)

  1. Select your cloud function, e.g. "MyFunction", a side menu should appear on the right showing you the access control settings for it

  2. Click on "Add Member", type in "allUsers" and select the role "Cloud Function Invoker"

  3. Save it -> now, you should see a remark "Allow unauthenticated" in the list of your cloud functions

Access is now available to everybody from the internet with the correct config to your GCP or Firebase project. (Be careful)

dimib
  • 499
  • 4
  • 6
3

For what it's worth I was having the same issue when passing app into onRequest. I realized the issue was a trailing slash on the request url for the firebase function. Express was looking for '/' but I didn't have the trailing slash on the function [project-id].cloudfunctions.net/[function-name]. The CORS error was a false negative. When I added the trailing slash, I got the response I was expecting.

shadyhill
  • 465
  • 4
  • 12
3

A cors error can occur if you don't catch an error in a function. My suggestion is to implement a try catch in your corsHandler

const corsHandler = (request, response, handler) => {
    cors({ origin: true })(request, response, async () => {
        try {
            await handler();
        }
        catch (e) {
            functions.logger.error('Error: ' + e);
            response.statusCode = 500;
            response.send({
                'status': 'ERROR' //Optional: customize your error message here
            });
        }
    });
};

Usage:

exports.helloWorld = functions.https.onRequest((request, response) => {
    corsHandler(request, response, () => {
        functions.logger.info("Hello logs!");
        response.send({
            "data": "Hello from Firebase!"
        });
    });
});

Thanks to stackoverflow users: Hoang Trinh, Yayo Arellano and Doug Stevenson

WiseTap
  • 3,383
  • 1
  • 16
  • 24
  • Good answer but one nit: As far as the status code of the error, rather than a 500, I think that it’s probably better to response with a 403. That’s (roughly) what the spec recommends at https://fetch.spec.whatwg.org/#http-responses. And in general, IMHO, it seems not optimal to send a 500 from application code — because that’s gonna make it harder for you to distinguish cases of your application code throwing an intended error from cases of the server/runtime code hitting some other kind of internal failure (maybe even before it ever executes your application code). – sideshowbarker Dec 03 '20 at 16:44
  • Thanks for the comment :) The 500 response code is because is not an error related to cors, because of that I think is more appropriate to use a status code >= 500 rather than 403. What do you think? – WiseTap Dec 03 '20 at 17:12
2

If You are not using Express or simply want to use CORS. The following code will help resolve

const cors = require('cors')({ origin: true, });   
exports.yourfunction = functions.https.onRequest((request, response) => {  
   return cors(request, response, () => {  
        // *Your code*
    });
});
Suraj Rao
  • 28,186
  • 10
  • 88
  • 94
krishnazden
  • 859
  • 8
  • 14
2

If you're testing firebase app locally then you need to point functions to localhost instead of cloud. By default, firebase serve or firebase emulators:start points the functions to server instead of localhost when you use it on your web app.

Add below script in html head after firebase init script:

 <script>
      firebase.functions().useFunctionsEmulator('http://localhost:5001')
 </script> 

Make sure to remove this snippet when deploying code to server.

GorvGoyl
  • 27,835
  • 20
  • 141
  • 143
2

Changing true by "*" did the trick for me, so this is how it looks like:

const cors = require('cors')({ origin: "*" })

I tried this approach because in general, this is how this response header is set:

'Access-Control-Allow-Origin', '*'

Be aware that this will allow any domain to call your endpoints therefore it's NOT secure.

Additionally, you can read more on the docs: https://github.com/expressjs/cors

Rob
  • 1,919
  • 3
  • 26
  • 37
1

Adding my piece of experience. I spent hours trying to find why I had CORS error.

It happens that I've renamed my cloud function (the very first I was trying after a big upgrade).

So when my firebase app was calling the cloud function with an incorrect name, it should have thrown a 404 error, not a CORS error.

Fixing the cloud function name in my firebase app fixed the issue.

I've filled a bug report about this here https://firebase.google.com/support/troubleshooter/report/bugs

Thomas
  • 813
  • 6
  • 20
1

From so much searching, I could find this solution in the same firebase documentation, just implement the cors in the path:

import * as express from "express";
import * as cors from "cors";


const api = express();
api.use(cors({ origin: true }));
api.get("/url", function);

Link firebase doc: https://firebase.google.com/docs/functions/http-events

Noks1
  • 11
  • 1
1

I'm a very beginner with Firebase (signed up 30 minutes ago). My issue is that I called my endpoint

https://xxxx-default-rtdb.firebaseio.com/myendpoint

Instead of

https://xxxx-default-rtdb.firebaseio.com/myendpoint.json

If you just started with Firebase, make sure you don't forget the .json extension.

0

In my case the error was caused by cloud function invoker limit access. Please add allUsers to cloud function invoker. Please catch link. Please refer to article for more info

Kacpero
  • 1
  • 3
  • 1
    Please provide some explanation of linked material in your answer, why is it relevant and such – Firefly Apr 14 '20 at 18:45
0

If none of the other solutions work, you could try adding the below address at the beginning of the call to enable CORS - redirect:

https://cors-anywhere.herokuapp.com/

Sample code with JQuery AJAX request:

$.ajax({
   url: 'https://cors-anywhere.herokuapp.com/https://fir-agilan.web.app/gmail?mail=asd@gmail.com,
   type: 'GET'
});
Agilan I
  • 29
  • 1
  • 5
0

See below for how I set up my Express with CORS.

The 'https://pericope.app' is my custom domain for my Firebase project.

It looks like all other answers recommend origin:true or *.

I'm hesitant to allow all origins since it would allow anyone else access to the api. That's fine if you are creating a public service, but if you're doing anything with your data it is risky since it is a privileged environment. For example, this admin SDK bypasses any security rules you have setup for Firestore or Storage.

//Express
const express = require('express');
const app = express();

const cors = require('cors');
app.use(cors({
  origin: 'https://pericope.app'
}));
0

If you prefer to make a single handler function (reference answer)

const applyMiddleware = handler => (req, res) => {
  return cors(req, res, () => {
    return handler(req, res)
  })
}
exports.handler = functions.https.onRequest(applyMiddleware(handler))
Kevin Danikowski
  • 2,444
  • 1
  • 23
  • 42
0

I have been trying this for a long time.

It finally finally worked when I made this change.

app.get('/create-customer', (req, res) => {
  return cors()(req, res, () => {
    ... your code ...

The Big difference is that I used cors()(req, res... instead of directly cors(req, res...

It Now works perfectly.

Abraham
  • 451
  • 11
0

I got the error because I was calling a function that didn't exist on the client side. For example:

firebase.functions().httpsCallable('makeSureThisStringIsCorrect');
user1689987
  • 411
  • 1
  • 5
  • 12