Habilitación de CORS en funciones en la nube para Firebase

141

Actualmente estoy aprendiendo a usar nuevas funciones de nube para Firebase y el problema que tengo es que no puedo acceder a la función que escribí a través de una solicitud AJAX. Me sale el error "No 'Access-Control-Allow-Origin'". Aquí hay un ejemplo de la función que escribí:

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

La función se encuentra en esta url: https://us-central1-fba-shipper-140ae.cloudfunctions.net/test

Firebase docs sugiere agregar el middleware CORS dentro de la función, lo he probado pero no funciona para mí: https://firebase.google.com/docs/functions/http-events

Así es como lo hice:

var cors = require('cors');    

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

¿Qué estoy haciendo mal? Agradecería cualquier ayuda con esto.

ACTUALIZAR:

La respuesta de Doug Stevenson ayudó. Al agregar ({origin: true}) se solucionó el problema, también tuve que cambiar response.status(500)a lo response.status(200)que me perdí por completo al principio.

Andrey Pokrovskiy
fuente
También una muestra en los documentos aquí
Kato
Tengo algunas funciones que funcionan con la solución proporcionada, pero ahora estoy probando una nueva función que esencialmente agrega gráficos abiertos en la parte superior de mi index.html y devuelve el index.html actualizado y no puedo hacer que funcione :( sigo obteniendo el ACCESO-CONTROL --- error
TheeBen
2
envolver la solicitud entrante en cors () como arriba fue lo único que funcionó para mí
Charles Harring
¿puedes editar tu "actualización" para subrayar que se requiere el middleware cors? Esto ahorrará algo de tiempo a algunas personas
Antoine Weber

Respuestas:

151

Hay dos funciones de muestra proporcionadas por el equipo de Firebase que demuestran el uso de CORS:

La segunda muestra usa una forma diferente de trabajar con cors que la que está usando actualmente.

Además, considere importar de esta manera, como se muestra en las muestras:

const cors = require('cors')({origin: true});
Doug Stevenson
fuente
2
¡Gracias! Agregar ({origin: true}) ayudó.
Andrey Pokrovskiy
2
Bien, definitivamente necesitas, origin: trueya que dejarlo afuera hará que esto no funcione
Scott
44
Parece que aquí es donde se define la lista blanca de dominios para permitir el acceso. ¿Y la configuración origin: truepermite que cualquier dominio acceda? ( npmjs.com/package/cors ) @Doug Stevenson ¿Cree que firebase podría escribir un documento sobre los conceptos básicos necesarios para las funciones https de cliente / servidor? El repositorio de muestras es bueno, pero habíamos perdido este requerimiento adicional.
Alan
9
Para cualquiera que esté dispuesto a agregar soporte CORS a sus back-end: asegúrese de comprender las consecuencias y cómo configurarlo adecuadamente. "origin: true" es genial para probar pero derrota todo el propósito :)
dSebastien
1
las funciones de la nube de Google no permiten el origen de comodines: cloud.google.com/functions/docs/writing/…
Corey Cole
73

Puede configurar el CORS en la función de nube de esta manera

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

No es necesario importar el corspaquete

Deanwilliammills
fuente
2
Esto funciona perfectamente para mi caso, una función en la nube que realiza una llamada XHR a la API de Mailchimp.
elverde
1
Esa es la respuesta necesaria.
Jimmy Kane
1
las funciones de la nube de Google no permiten el origen de comodines: cloud.google.com/functions/docs/writing/…
Corey Cole
44
@CoreyCole Creo que solo si necesita agregar el Authorizationencabezado. Lo anterior parece funcionar bien.
Stuart Memo
¿Dónde colocar esta línea de código? ¿Qué parte de la función de la nube?
Antonio Ooi
41

Para cualquiera que intente hacer esto en Typecript, este es el código:

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
fuente
3
La solución le hará perder el inicio de sesión en las funciones de la nube (muy mal) y una funcionalidad asíncrona / espera adecuada, corre el riesgo de que el contenido de la función finalice prematuramente dentro de la devolución de llamada en llamadas largas.
Oliver Dixon
2
las funciones de la nube de Google no permiten el origen de comodines: cloud.google.com/functions/docs/writing/…
Corey Cole
29

Una información adicional, solo por el bien de aquellos que buscan en Google después de un tiempo: si está utilizando Firebase Hosting, también puede configurar reescrituras, de modo que, por ejemplo, una URL como (firebase_hosting_host) / api / myfunction redirija a ( función firebase_cloudfunctions_host) / doStuff. De esa manera, dado que la redirección es transparente y del lado del servidor, no tiene que lidiar con cors.

Puede configurar eso con una sección de reescrituras en firebase.json:

"rewrites": [
        { "source": "/api/myFunction", "function": "doStuff" }
]
Pablo Urquiza
fuente
1
OMI, esta es la mejor respuesta, ya que resuelve el problema real sin agregar ningún problema de seguridad adicional. De esta manera, las funciones de la nube se sirven desde el mismo dominio que el resto y ni siquiera necesita ningún cors.
koljaTM
3
De hecho, esta es una gran característica, pero actualmente solo funciona si las funciones se encuentran en la región predeterminada (us-central1). Quería implementar mis funciones en europe-west1 por razones de latencia y me encontré con este problema: github.com/firebase/firebase-tools/issues/842
Alex Suzuki
La redirección funciona bien y hace que la URL sea más limpia, pero no he descubierto cómo pasar los parámetros GET. La función (después de reescribir) parece llamarse sin parámetros.
royappa
20

Ninguna solución CORS funcionó para mí ... ¡hasta ahora!

No estoy seguro de si alguien más se encontró con el mismo problema que yo, pero configuré CORS como 5 formas diferentes de los ejemplos que encontré y nada parecía funcionar. Configuré un ejemplo mínimo con Plunker para ver si realmente era un error, pero el ejemplo funcionó muy bien. Decidí verificar los registros de funciones de Firebase (que se encuentran en la consola de Firebase) para ver si eso podría decirme algo. Tuve un par de errores en mi código de servidor de nodo , no relacionados con CORS , que cuando depuré me liberaron de mi mensaje de error CORS . No sé por qué los errores de código no relacionados con CORS devuelven una respuesta de error CORS, pero me llevaron por el agujero de conejo equivocado durante un buen número de horas ...

tl; dr: verifique los registros de funciones de firebase si no hay soluciones CORS que funcionen y depure cualquier error que tenga

tbone849
fuente
1
Esto me volvió loco. en mi caso ni siquiera fue un error en el código! se Error: quota exceeded (Quota exceeded for quota group 'NetworkIngressNonbillable' and limit 'CLIENT_PROJECT-1d' of service 'cloudfunctions.googleapis.com ha superado la cuota así que básicamente libre y funciones devolvió el error coros
Stanislau Buzunko
Ocurre aquí un par de veces, el servidor devuelve el mismo error y los cors: Error: interno es básicamente el error. Este error también ocurrirá si ejecuta la función incorrecta, por ejemplo, al
escribir mal
Cuando intenta solicitar la verificación de Google reCAPTCHA dentro de la función de nube, el navegador también le arroja el error CORS. Cuando reviso el registro de funciones de Firebase Console, dice access to external network resources not allowed if the billing account is not enabled. Después de habilitar la cuenta de facturación, funciona perfectamente. Este es también uno de los ejemplos que no están relacionados con los cors, pero se produce un error de cors.
Antonio Ooi
19

Tengo una pequeña adición a la respuesta de @Andreys a su propia pregunta.

Parece que no tiene que llamar a la devolución de llamada en la cors(req, res, cb)función, por lo que puede llamar al módulo cors en la parte superior de su función, sin incrustar todo su código en la devolución de llamada. Esto es mucho más rápido si desea implementar cors después.

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

No olvides iniciar los cors como se menciona en el post de apertura:

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

Jaap Weijland
fuente
1
esto funcionó cuando otras respuestas de SO con la configuración manual de los encabezados no lo hicieron
Jim Factor
Esto funciona pero puede causar un error de TSlint si lo habilitó y no puede implementarlo en Firebase. Ponga la respuesta dentro del cierre del cors para superarlocors(request, response, () => { return response.send("Hello from Firebase!"); });
Spiral Out
1
2 errores aquí chicos. El primero. Cualquier cosa después de la función cors se ejecutará dos veces (ya que la primera solicitud es antes del vuelo). No está bien. En segundo lugar, @SpiralOut su solución le hará perder el inicio de sesión en las funciones de la nube (muy mal) y una funcionalidad asíncrona / espera adecuada, corre el riesgo de que el contenido de la función finalice prematuramente dentro de la devolución de llamada.
Oliver Dixon
@SpiralOut simplemente puede deshabilitar tslint
Vlad
1
Habiendo aprendido mucho sobre mcd en el último año, ya no recomendaría esta respuesta. Podría ser útil para prototipos rápidos, pero evítelo en casos de producción real
Jaap Weijland
11

Esto puede ser útil. Creé la función de nube HTTP firebase con express (URL personalizada)

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

Asegúrese de agregar secciones de reescritura

"rewrites": [
      {
        "source": "/api/v1/**",
        "function": "functionName"
      }
]
Arenoso
fuente
1
Tu respuesta es demasiado baja, amigo mío, la mejor respuesta con diferencia.
Avram Virgil
Gracias. @AvramVirgil
Sandy
Esto fue lo más rápido y fácil de todo, ¡Gracias!
Gaurav Kakkar
8

Acabo de publicar un pequeño artículo sobre eso:

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

En general, debe usar el paquete Express CORS , que requiere un poco de pirateo para cumplir con los requisitos de las funciones GCF / Firebase.

¡Espero que ayude!

mhaligowski
fuente
44
¿No estás seguro de lo que quieres decir con piratería? ¿Te importaría elaborar un poco? Lee tu publicación pero no veo que lo menciones
TheeBen
1
autor del módulo cors aquí; al "hackear" mhaligowski simplemente significaba que tenía que ajustar la llamada al módulo cors para que coincidiera con la forma en que Express llama al middleware (es decir, proporciona una función como tercer parámetro después de la solicitud y resolución)
Troy
4

Si hay personas como yo: si desea llamar a la función de nube desde el mismo proyecto que la función de nube, puede iniciar el sdk de Firebase y usar el método onCall. Se encargará de todo por ti:

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

Llame a esta función así:

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

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

Si no puede iniciar el SDK, aquí está la esencia de las otras sugerencias:

Chronnie
fuente
3
En realidad, cuando uso onCall func en el navegador, recibí un error de cors. ¿Podría establecer encabezados de Costom en esta solicitud?
Viktor Hardubej
4

Se encontró una manera de habilitar cors sin importar ninguna biblioteca 'cors'. También funciona Typescripty lo probó en la versión 81.0 de Chrome.

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();
});
JerryGoyal
fuente
3

Por si sirve de algo que estaba teniendo el mismo problema cuando se pasa appa onRequest. Me di cuenta de que el problema era una barra diagonal en la url de solicitud para la función firebase. Express estaba buscando '/'pero no tenía la barra inclinada final en la función [project-id].cloudfunctions.net/[function-name]. El error CORS fue un falso negativo. Cuando agregué la barra inclinada final, obtuve la respuesta que esperaba.

Shadyhill
fuente
también asegúrese de agregar su, [project-id]ya que este fue el problema que enfrenté
desconectado
3

Solo así funciona para mí, ya que tengo autorización en mi solicitud:

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
fuente
las funciones de la nube de Google no permiten el origen de comodines: cloud.google.com/functions/docs/writing/…
Corey Cole
3

Si no puede / no puede usar el complemento cors, llame al setCorsHeaders() función primero en la función del controlador también funcionará.

Utilice también las funciones respondSuccess / Error al responder de nuevo.

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
fuente
2

Si está probando la aplicación Firebase localmente, entonces necesita señalar funciones en localhostlugar de la nube. Por defecto, firebase serveofirebase emulators:start señala las funciones al servidor en lugar de localhost cuando lo usa en su aplicación web.

Agregue el script a continuación en la cabeza html después del script de inicio de Firebase:

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

Asegúrese de eliminar este fragmento al implementar código en el servidor.

JerryGoyal
fuente
2

Cambiar truepor "*"hizo el truco para mí, así es como se ve:

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

Intenté este enfoque porque, en general, así es como se configura este encabezado de respuesta:

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

Tenga en cuenta que esto permitirá que cualquier dominio llame a sus puntos finales, por lo tanto, NO es seguro.

Además, puede leer más sobre los documentos: https://github.com/expressjs/cors

Robar
fuente
1

Si no está utilizando Express o simplemente desea utilizar CORS. El siguiente código ayudará a resolver

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

En mi caso, el error fue causado por la función de nube que invoca el acceso limitado. Agregue allUsers al invocador de funciones en la nube. Por favor, coge el enlace . Consulte el artículo para obtener más información.

Kacpero
fuente
Proporcione alguna explicación del material vinculado en su respuesta, por qué es relevante y tal
Firefly
0

Si ninguna de las otras soluciones funciona, puede intentar agregar la siguiente dirección al comienzo de la llamada para habilitar CORS - redirigir:

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

Código de muestra con solicitud JQuery AJAX:

$.ajax({
   url: 'https://cors-anywhere.herokuapp.com/https://fir-agilan.web.app/[email protected],
   type: 'GET'
});
Agilan I
fuente
0

Agregando mi experiencia. Pasé horas tratando de encontrar por qué tuve un error CORS.

Sucede que he cambiado el nombre de mi función de nube (la primera que estaba intentando después de una gran actualización).

Entonces, cuando mi aplicación firebase estaba llamando a la función de la nube con un nombre incorrecto, debería haber arrojado un error 404, no un error CORS.

Arreglar el nombre de la función en la nube en mi aplicación firebase solucionó el problema.

He completado un informe de error sobre esto aquí https://firebase.google.com/support/troubleshooter/report/bugs

Thomas
fuente