Andrey Pokrovskiy
Andrey Pokrovskiy

Reputation: 2926

Enabling CORS in Cloud Functions for Firebase

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.

Upvotes: 283

Views: 278430

Answers (30)

Doug Stevenson
Doug Stevenson

Reputation: 317948

For Cloud Functions v2:

You can simply define the function to accept CORS requests as shown in the documentation:

const { onRequest } = require("firebase-functions/v2/https");

exports.sayHello = onRequest(
  { cors: true },
  (req, res) => {
    res.status(200).send("Hello world!");
  }
);

For Cloud Functions v1:

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
    })
});

Upvotes: 296

bnitica
bnitica

Reputation: 11

You can set the headers in firebase.json like this:

"hosting": {
  // ...

  // Applies a CORS header for all font files
  "headers": [ {
    "source": "**/*.@(eot|otf|ttf|ttc|woff|font.css)",
    "headers": [ {
      "key": "Access-Control-Allow-Origin",
      "value": "*"
    } ]
  } ]
}

More on Firebase docs.

Upvotes: 0

rbansal
rbansal

Reputation: 1360

I tried the below methods, but none worked.

  1. Installing the cors package.
  2. Adding the cors flag to the firebase.json file.
  3. returning cors as a part of request options.

The solution is to add a cors option to the function.

functions.https.onCall(handler, {
  cors: true
})

Upvotes: 0

Yayo Arellano
Yayo Arellano

Reputation: 3866

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
           });
           
    });

Upvotes: 92

Mobiletainment
Mobiletainment

Reputation: 23311

Cloud Functions for Firebase v2

Cloud Functions for Firebase v2 now allow you to configure cors directly in the HTTP options. It works without the need for any 3rd party package:


import { https } from 'firebase-functions/v2';

export myfunction = https.onRequest({ cors: true }, async (req, res) => {
  // this will be invoked for any request, regardless of its origin
});

Beware:

  • At the time of writing, v2is in public preview.
  • Only a sub-set of regions is currently supported in v2.
  • Function names are restricted to lowercase letters, numbers, and dashes.
  • You can use v1 and v2 functions side-by-side in a single codebase. For improved readability, update your imports to access firebase-functions/v1 or firebase-functions/v2 respectively.

Upvotes: 17

GorvGoyl
GorvGoyl

Reputation: 49680

Updated answer: using cors library with Typescript support:

install cors

npm i -S cors
npm i --save-dev @types/cors

index.ts:

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

// allow cors in http function
export const myFunction = functions.https.onRequest((req, res) => {
corsHandler(req, res, async () => {

// your method body

 });
});

Old answer: (not working anymore)
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();
});

Upvotes: 25

deanwilliammills
deanwilliammills

Reputation: 2787

You can set the CORS in the cloud function like this

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

No need to import the cors package

Upvotes: 152

Jaap Weijland
Jaap Weijland

Reputation: 3357

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.

Upvotes: 22

Pablo Urquiza
Pablo Urquiza

Reputation: 735

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" }
]

Upvotes: 55

bousaque
bousaque

Reputation: 11

With the same access allow control origin error in the devtool console, I found other solutions with also more modern syntax :

My CORS problem was with Storage (and not RTDB neither the browser...), and then I'm not in possession of a credit card (as requested by the aforementioned solutions), my no-credit card solution was to :

  1. install gsutil : https://cloud.google.com/storage/docs/gsutil_install#linux-and-macos

  2. to create a cors.json file to be loaded via terminal with gsutil

gsutil cors set cors.json gs://[ your-bucket ]/-1.appspot.com

https://firebase.google.com/docs/storage/web/download-files#cors_configuration

Upvotes: 1

zqxyz
zqxyz

Reputation: 334

  1. Go into your Google Cloud Functions. You may have not seen this platform before, but it's how you'll fix this Firebase problem.
  2. Find the Firebase function you're searching for and click on the name. If this page is blank, you may need to search for Cloud Functions and select the page from the results.
  3. Find your function, click on the name.
  4. Go to the permissions tab. Click Add (to add user).
  5. Under new principles, type 'allUsers' -- it should autocomplete before you finish typing.
  6. Under select a role, search for Cloud Functions, then choose Invoker.
  7. Save.
  8. Wait a couple minutes.

This should fix it. If it doesn't, do this AND add a CORS solution to your function code, something like:

  exports.sendMail = functions.https.onRequest((request, response) => {
  response.set("Access-Control-Allow-Origin", "*");
  response.send("Hello from Firebase!");
});

Upvotes: 4

David
David

Reputation: 171

Cloud functions logging helps, check that if you are stuck.

My issue turned out to be a type error on my cloud function where I had a number where a string was expected:

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type number (1)

For some reason this gave me the cors error on front-end, and it became a few wasted hours.

Upvotes: 0

BorisD
BorisD

Reputation: 1824

Use cors on your https.onRequest with Typescript like this:

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

export const pingFunctionWithCorsAllowed = functions.https.onRequest((request, response) => {
  corsHandler(request, response, () => {
    response.send(`Ping from Firebase (with CORS handling)! ${new Date().toISOString()}`);
  });
});

From Source

Upvotes: 0

user1689987
user1689987

Reputation: 1566

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

firebase.functions().httpsCallable('makeSureThisStringIsCorrect');

Upvotes: 2

Abraham
Abraham

Reputation: 15830

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.

Upvotes: 1

Guillaume Renoult
Guillaume Renoult

Reputation: 966

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.

Upvotes: 1

Kevin Danikowski
Kevin Danikowski

Reputation: 5206

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))

Upvotes: 1

Joel Walkley
Joel Walkley

Reputation: 21

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'
}));

Upvotes: 1

Noks1
Noks1

Reputation: 11

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

Upvotes: 1

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

Upvotes: 6

dimib
dimib

Reputation: 756

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)

Upvotes: 15

Thomas
Thomas

Reputation: 1354

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

Upvotes: 3

Agilan I
Agilan I

Reputation: 232

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/[email protected],
   type: 'GET'
});

Upvotes: 0

Rob
Rob

Reputation: 2333

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

Upvotes: 4

Kacpero
Kacpero

Reputation: 9

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

Upvotes: -1

KasparTr
KasparTr

Reputation: 2458

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));
}

Upvotes: 10

GorvGoyl
GorvGoyl

Reputation: 49680

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.

Upvotes: 2

zeekrey
zeekrey

Reputation: 451

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:

Upvotes: 8

Sandy
Sandy

Reputation: 520

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"
      }
]

Upvotes: 15

Gleb Dolzikov
Gleb Dolzikov

Reputation: 834

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)
} )};

Upvotes: 5

Related Questions