Activation de CORS dans Cloud Functions pour Firebase


141

J'apprends actuellement à utiliser les nouvelles fonctions Cloud pour Firebase et le problème que je rencontre est que je ne peux pas accéder à la fonction que j'ai écrite via une requête AJAX. J'obtiens l'erreur «No 'Access-Control-Allow-Origin» ». Voici un exemple de la fonction que j'ai écrite:

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

La fonction se trouve dans cette url: https://us-central1-fba-shipper-140ae.cloudfunctions.net/test

La documentation Firebase suggère d'ajouter le middleware CORS à l'intérieur de la fonction, je l'ai essayé mais cela ne fonctionne pas pour moi: https://firebase.google.com/docs/functions/http-events

Voici comment je l'ai fait:

var cors = require('cors');    

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

Qu'est-ce que je fais mal? J'apprécierais n'importe quelle aide avec ceci.

METTRE À JOUR:

La réponse de Doug Stevenson a aidé. Ajout ({origine: true}) fixe la question, j'ai eu aussi au changement response.status(500)à response.status(200)laquelle je complètement raté au début.


Également un échantillon dans la documentation ici
Kato

J'ai quelques fonctions qui fonctionnent avec la solution fournie mais j'essaie maintenant une nouvelle fonction qui ajoute essentiellement des graphiques ouverts en haut de mon index.html et renvoie l'index.html mis à jour et je ne peux pas le faire fonctionner :( continuez à obtenir l'erreur ACCESS-CONTROL ---
TheeBen

2
envelopper la demande entrante dans cors () comme ci-dessus était la seule chose qui a fonctionné pour moi
Charles Harring

pouvez-vous modifier votre "mise à jour" pour souligner que le middleware cors est requis? Cela fera gagner du temps à certaines personnes
Antoine Weber

Réponses:


151

Il existe deux exemples de fonctions fournis par l'équipe Firebase qui illustrent l'utilisation de CORS:

Le deuxième exemple utilise une manière de travailler avec des cors différente de celle que vous utilisez actuellement.

Pensez également à importer comme ceci, comme indiqué dans les exemples:

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

2
Je vous remercie! L'ajout de ({origin: true}) a aidé.
Andrey Pokrovskiy

2
Bien, vous devez absolument origin: truele laisser de côté, cela ne fonctionnera pas
Scott

4
On dirait que c'est là que la liste blanche des domaines pour autoriser l'accès est définie? Et le réglage origin: truepermet à n'importe quel domaine d'accéder? ( npmjs.com/package/cors ) @Doug Stevenson Pensez-vous que Firebase pourrait rédiger un document sur les bases nécessaires pour les fonctions https client / serveur? Le repo d'échantillons est bon, mais nous avions manqué ce petit supplément d'exigence.
Alan

9
À toute personne souhaitant ajouter le support CORS à ses back-ends: assurez-vous de bien comprendre les conséquences et comment le configurer correctement. "origin: true" est cool pour les tests mais il va à l'
encontre de

1
les fonctions google cloud n'autorisent pas l'origine générique: cloud.google.com/functions/docs/writing/…
Corey Cole

73

Vous pouvez définir le CORS dans la fonction cloud comme ceci

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

Pas besoin d'importer le corspackage


2
Cela fonctionne parfaitement pour mon cas, une fonction cloud qui fait un appel XHR à l'API Mailchimp.
elverde

1
Telle est la réponse nécessaire.
Jimmy Kane

1
les fonctions google cloud n'autorisent pas l'origine générique: cloud.google.com/functions/docs/writing/…
Corey Cole

4
@CoreyCole Je pense que ce n'est que si vous devez ajouter l'en- Authorizationtête. Ce qui précède semble fonctionner correctement.
Stuart Memo

Où placer cette ligne de code? Quelle partie de la fonction cloud?
Antonio Ooi

41

Pour tous ceux qui essaient de faire cela dans Typescript, voici le 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
});

3
La solution vous fera perdre la journalisation sur les fonctions cloud (très mauvaise) et la fonctionnalité asynchrone / attente appropriée, vous risquez que le contenu de la fonction se termine prématurément dans le rappel lors de longs appels.
Oliver Dixon

2
les fonctions google cloud n'autorisent pas l'origine générique: cloud.google.com/functions/docs/writing/…
Corey Cole

29

Une information supplémentaire, juste pour le plaisir de ceux qui recherchent ceci après un certain temps: si vous utilisez l'hébergement Firebase, vous pouvez également configurer des réécritures, de sorte que par exemple une URL comme (firebase_hosting_host) / api / myfunction redirige vers le ( firebase_cloudfunctions_host) / doStuff. De cette façon, puisque la redirection est transparente et côté serveur, vous n'avez pas à gérer les cors.

Vous pouvez configurer cela avec une section de réécriture dans firebase.json:

"rewrites": [
        { "source": "/api/myFunction", "function": "doStuff" }
]

1
imo, c'est la meilleure réponse, car elle résout le problème réel sans ajouter de problèmes de sécurité supplémentaires. De cette façon, les fonctions cloud sont servies à partir du même domaine que les autres et vous n'avez même pas besoin de cors.
koljaTM

3
C'est une fonctionnalité intéressante, mais elle ne fonctionne actuellement que si les fonctions résident dans la région par défaut (us-central1). Je voulais déployer mes fonctions sur europe-west1 pour des raisons de latence et j'ai rencontré ce problème: github.com/firebase/firebase-tools/issues/842
Alex Suzuki

La redirection fonctionne bien et rend l'URL plus propre, mais je n'ai pas compris comment passer les paramètres GET. La fonction (après réécriture) semble être appelée sans paramètres.
royappa

20

Aucune solution CORS n'a fonctionné pour moi ... jusqu'à présent!

Je ne sais pas si quelqu'un d'autre a rencontré le même problème que moi, mais j'ai configuré CORS comme 5 manières différentes à partir d'exemples que j'ai trouvés et rien ne semblait fonctionner. J'ai mis en place un exemple minimal avec Plunker pour voir s'il s'agissait vraiment d'un bogue, mais l'exemple a fonctionné à merveille. J'ai décidé de vérifier les journaux des fonctions de Firebase (trouvés dans la console Firebase) pour voir si cela pouvait me dire quelque chose. J'ai eu quelques erreurs dans le code de mon serveur de nœud , non liées à CORS , qui, lorsque j'ai débogué, m'ont libéré de mon message d'erreur CORS . Je ne sais pas pourquoi les erreurs de code non liées à CORS renvoient une réponse d'erreur CORS, mais cela m'a conduit dans le mauvais terrier pendant un bon nombre d'heures ...

tl; dr - vérifiez vos journaux de fonctions Firebase si aucune solution CORS ne fonctionne et déboguez toutes les erreurs que vous avez


1
cela m'a rendu fou. dans mon cas, ce n'était même pas une erreur de code! c'était Error: quota exceeded (Quota exceeded for quota group 'NetworkIngressNonbillable' and limit 'CLIENT_PROJECT-1d' of service 'cloudfunctions.googleapis.com tellement fondamentalement le quota gratuit a été dépassé et les fonctions ont renvoyé une erreur de cors
Stanislau Buzunko

Cela se produit plusieurs fois ici, la même erreur est renvoyée par le serveur ainsi que par cors: Erreur: interne est fondamentalement l'erreur. Cette erreur se produira également si vous exécutez la mauvaise fonction, par exemple en tapant mal un nom de fonction
Henrik Bøgelund Lavstsen

Lorsque vous essayez de demander la vérification de Google reCAPTCHA dans la fonction cloud, le navigateur vous renvoie également l'erreur CORS. Lorsque je vérifie le journal des fonctions de la console Firebase, il dit access to external network resources not allowed if the billing account is not enabled. Après avoir activé le compte de facturation, cela fonctionne parfaitement. C'est également l'un des exemples non liés aux cors, mais une erreur cors est générée.
Antonio Ooi

19

J'ai un petit ajout à la réponse de @Andreys à sa propre question.

Il semble que vous n'ayez pas à appeler le rappel dans la cors(req, res, cb)fonction, vous pouvez donc simplement appeler le module cors en haut de votre fonction, sans incorporer tout votre code dans le rappel. C'est beaucoup plus rapide si vous souhaitez implémenter des cors par la suite.

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

N'oubliez pas d'initier les cors comme mentionné dans le post d'ouverture:

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


1
cela a fonctionné lorsque d'autres réponses SO avec la définition des en-têtes manuellement n'ont pas
Jim Factor

Cela fonctionne mais cela peut provoquer une erreur TSlint si vous l'aviez activé et que vous ne pouvez pas déployer sur Firebase. Mettez la réponse à l'intérieur de la fermeture des cors pour la surmontercors(request, response, () => { return response.send("Hello from Firebase!"); });
Spiral Out

1
2 erreurs ici les gars. Premier. Tout ce qui se trouve après la fonction cors s'exécutera deux fois (puisque la première requête est le contrôle en amont). Pas bon. Deuxièmement, @SpiralOut votre solution vous fera perdre la journalisation sur les fonctions du cloud (très mauvaise) et la fonctionnalité async / attente appropriée, vous risquez que le contenu de la fonction se termine prématurément dans le rappel.
Oliver Dixon

@SpiralOut vous pouvez simplement désactiver tslint
Vlad

1
Ayant beaucoup appris sur gcf l'année dernière, je ne recommanderais plus cette réponse. Cela pourrait être pratique pour les prototypes rapides, mais évitez cela dans les cas de production réels
Jaap Weijland

11

Cela pourrait être utile. J'ai créé la fonction cloud HTTP Firebase avec express (URL personnalisée)

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

Veuillez vous assurer d'avoir ajouté des sections de réécriture

"rewrites": [
      {
        "source": "/api/v1/**",
        "function": "functionName"
      }
]

1
Votre réponse est trop basse mon ami, la meilleure réponse de loin.
Avram Virgil

Merci. @AvramVirgil
Sandy

C'était le plus rapide et le plus simple de tous, merci!
Gaurav Kakkar le

8

Je viens de publier un petit article à ce sujet:

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

En règle générale, vous devez utiliser le package Express CORS , qui nécessite un peu de piratage pour répondre aux exigences des fonctions GCF / Firebase.

J'espère que cela pourra aider!


4
Vous ne savez pas ce que vous entendez par piratage? Voulez-vous élaborer un peu? Lisez votre message mais je ne vous vois pas en parler
TheeBen

1
auteur du module cors ici; par "piratage", mhaligowski signifiait simplement qu'il devait encapsuler l'appel au module cors pour qu'il corresponde à la façon dont Express appelle le middleware (c.-à-d. fournir une fonction comme troisième paramètre après req & res)
Troy

4

S'il y a des gens comme moi là-bas: Si vous voulez appeler la fonction cloud à partir du même projet que la fonction cloud elle-même, vous pouvez lancer le sdk firebase et utiliser la méthode onCall. Il s'occupera de tout pour vous:

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

Appelez cette fonction comme ceci:

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

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

Si vous ne pouvez pas lancer le SDK, voici l'essence des autres suggestions:


3
en fait, lorsque j'utilise onCall func sur le navigateur, j'ai une erreur cors. Puis-je définir des en-têtes personnalisés dans cette demande?
Viktor Hardubej

4

Nous avons trouvé un moyen d'activer les cors sans importer de bibliothèque «cors». Il fonctionne également avec Typescriptet l'a testé dans la version chrome 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();
});

3

Pour ce que ça vaut la peine que je faisais le même problème lors du passage appdans onRequest. J'ai réalisé que le problème était une barre oblique à la fin de l'URL de la requête pour la fonction Firebase. Express cherchait '/'mais je n'avais pas la barre oblique finale sur la fonction [project-id].cloudfunctions.net/[function-name]. L'erreur CORS était un faux négatif. Lorsque j'ai ajouté la barre oblique finale, j'ai obtenu la réponse que j'attendais.


assurez-vous également d'ajouter votre [project-id]car c'était le problème auquel j'ai été confronté
débranché

3

Seulement cette façon fonctionne pour moi car j'ai une autorisation dans ma demande:

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

les fonctions google cloud n'autorisent pas l'origine générique: cloud.google.com/functions/docs/writing/…
Corey Cole

3

Si vous n'utilisez pas / ne pouvez pas utiliser le plugin cors, appeler la setCorsHeaders()fonction en premier dans la fonction handler fonctionnera également.

Utilisez également les fonctions respondSuccess / Error lorsque vous répondez.

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

2

Si vous testez l'application Firebase localement, vous devez pointer les fonctions vers localhostplutôt que vers le cloud. Par défaut, firebase serveou firebase emulators:startpointe les fonctions vers le serveur au lieu de l'hôte local lorsque vous l'utilisez sur votre application Web.

Ajoutez le script ci-dessous dans la tête html après le script d'initialisation de firebase:

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

Assurez-vous de supprimer cet extrait de code lors du déploiement de code sur le serveur.


2

Changer truepar a "*"fait l'affaire pour moi, voici à quoi ça ressemble:

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

J'ai essayé cette approche car en général, voici comment cet en-tête de réponse est défini:

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

Sachez que cela permettra à n'importe quel domaine d'appeler vos points de terminaison, donc ce n'est PAS sécurisé.

De plus, vous pouvez en savoir plus sur la documentation: https://github.com/expressjs/cors


1

Si vous n'utilisez pas Express ou souhaitez simplement utiliser CORS. Le code suivant aidera à résoudre

const cors = require('cors')({ origin: true, });   
exports.yourfunction = functions.https.onRequest((request, response) => {  
   return cors(request, response, () => {  
        // *Your code*
    });
});

0

Dans mon cas, l'erreur a été causée par l'accès à la limite de l'invocateur de la fonction cloud. Veuillez ajouter allUsers à l'invocateur de la fonction cloud. Veuillez attraper le lien . Veuillez vous référer à l' article pour plus d'informations


Veuillez fournir une explication du matériel lié dans votre réponse, pourquoi est-il pertinent et tel
Firefly

0

Si aucune des autres solutions ne fonctionne, vous pouvez essayer d'ajouter l'adresse ci-dessous au début de l'appel pour activer CORS - redirection:

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

Exemple de code avec la requête JQuery AJAX:

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

0

Ajout de mon expérience. J'ai passé des heures à essayer de trouver pourquoi j'avais une erreur CORS.

Il arrive que j'ai renommé ma fonction cloud (la toute première que j'essayais après une grosse mise à jour).

Ainsi, lorsque mon application Firebase appelait la fonction cloud avec un nom incorrect, elle aurait dû générer une erreur 404, pas une erreur CORS.

La correction du nom de la fonction cloud dans mon application Firebase a résolu le problème.

J'ai rempli un rapport de bogue à ce sujet ici https://firebase.google.com/support/troubleshooter/report/bugs

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.