Habilitando o CORS nas funções de nuvem para Firebase

141

Atualmente, estou aprendendo a usar as novas funções de nuvem para Firebase e o problema que tenho é que não consigo acessar a função que escrevi por meio de uma solicitação AJAX. Eu recebo o erro "Não 'Acesso-controle-permitir-origem'". Aqui está um exemplo da função que escrevi:

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

A função fica neste URL: https://us-central1-fba-shipper-140ae.cloudfunctions.net/test

Os documentos do Firebase sugerem adicionar o middleware CORS dentro da função, eu tentei, mas não está funcionando para mim: https://firebase.google.com/docs/functions/http-events

Foi assim que eu fiz:

var cors = require('cors');    

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

O que estou fazendo de errado? Eu apreciaria qualquer ajuda com isso.

ATUALIZAR:

A resposta de Doug Stevenson ajudou. A adição ({origin: true}) corrigiu o problema. Também tive que mudar response.status(500)para o response.status(200)qual perdi completamente.

Andrey Pokrovskiy
fonte
Também uma amostra nos documentos aqui
Kato
Eu tenho algumas funções que funcionam com a solução fornecida, mas agora estou tentando uma nova função que essencialmente adiciona gráficos abertos à parte superior do meu index.html e retorna o index.html atualizado e não consigo fazê-lo funcionar :( continue obtendo o Access-Control --- erro
TheeBen
2
agrupar a solicitação recebida em cors () como acima foi a única coisa que funcionou para mim
Charles Harring
você pode editar sua "atualização" para sublinhar que o middleware cors é necessário? Isso poupará tempo a algumas pessoas
Antoine Weber

Respostas:

151

Existem duas funções de exemplo fornecidas pela equipe do Firebase que demonstram o uso do CORS:

O segundo exemplo usa uma maneira diferente de trabalhar com cors do que você está usando atualmente.

Além disso, considere importar desta forma, conforme mostrado nas amostras:

const cors = require('cors')({origin: true});
Doug Stevenson
fonte
2
Obrigado! A adição ({origin: true}) ajudou.
precisa
2
Bom, você definitivamente precisa origin: truede deixá-lo fora fará com que isso não trabalho
Scott
4
Parece que é aqui que a lista de permissões de domínios para permitir o acesso é definida? E a configuração origin: truepermite que qualquer domínio acesse? ( npmjs.com/package/cors ) @Doug Stevenson Você acha que o firebase poderia escrever um documento sobre o básico necessário para as funções https do cliente / servidor? O repositório de amostras é bom, mas perdemos esse requisito extra.
Alan Alan
9
Para qualquer pessoa que queira adicionar suporte ao CORS em seus back-ends: certifique-se de entender as conseqüências e como configurá-lo corretamente. "origem: true" é legal para o teste, mas derrota o propósito :)
dSebastien
1
as funções do Google Cloud não permitem a origem do curinga: cloud.google.com/functions/docs/writing/…
Corey Cole
73

Você pode definir o CORS na função de nuvem como esta

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

Não há necessidade de importar o corspacote

deanwilliammills
fonte
2
Isso funciona perfeitamente para o meu caso, uma função de nuvem que faz uma chamada XHR para a API do Mailchimp.
elverde 3/01/19
1
Essa é a resposta necessária.
Jimmy Kane
1
as funções do Google Cloud não permitem a origem do curinga: cloud.google.com/functions/docs/writing/…
Corey Cole
4
@CoreyCole Acho que é apenas se você precisar adicionar o Authorizationcabeçalho. O texto acima parece funcionar bem.
Stuart Memo
Onde colocar essa linha de código? Qual parte da nuvem funciona?
Antonio Ooi
41

Para qualquer pessoa que tente fazer isso no TypeScript, este é o 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
fonte
3
A solução fará com que você perca o logon nas funções da nuvem (muito ruim) e a funcionalidade assíncrona / aguardada adequada, o risco de que o conteúdo da função seja prematuramente encerrado no retorno de chamada em chamadas longas.
Oliver Dixon
2
as funções do Google Cloud não permitem a origem do curinga: cloud.google.com/functions/docs/writing/…
Corey Cole
29

Uma informação adicional, apenas para aqueles que pesquisam isso depois de algum tempo: Se você estiver usando hospedagem firebase, também poderá configurar reescritas, de modo que, por exemplo, um URL como (firebase_hosting_host) / api / myfunction seja redirecionado para o ( firebase_cloudfunctions_host) / função doStuff. Dessa forma, como o redirecionamento é transparente e do lado do servidor, você não precisa lidar com cors.

Você pode configurar isso com uma seção de reescritas no firebase.json:

"rewrites": [
        { "source": "/api/myFunction", "function": "doStuff" }
]
Pablo Urquiza
fonte
1
imo, esta é a melhor resposta, pois resolve o problema real sem adicionar nenhum problema de segurança adicional. Dessa forma, as funções da nuvem são atendidas do mesmo domínio que o restante e você nem precisa de cors.
precisa saber é o seguinte
3
Esse é um ótimo recurso, mas atualmente só funciona se as funções estiverem na região padrão (us-central1). Eu queria implantar minhas funções para a Europa-West1 por motivos de latência e correu para esse problema: github.com/firebase/firebase-tools/issues/842
Alex Suzuki
O redirecionamento funciona bem e torna o URL mais limpo, mas ainda não descobri como passar os parâmetros GET. A função (após reescrita) parece ser chamada sem parâmetros.
Rogereteu #
20

Nenhuma solução CORS funcionou para mim ... até agora!

Não tenho certeza se mais alguém encontrou o mesmo problema que eu, mas configurei o CORS como 5 maneiras diferentes dos exemplos que encontrei e nada parecia funcionar. Eu configurei um exemplo mínimo com o Plunker para ver se era realmente um bug, mas o exemplo correu lindamente. Decidi verificar os logs de funções do firebase (encontrados no console do firebase) para ver se isso poderia me dizer alguma coisa. Eu tive alguns erros no código do servidor do nó , não relacionados ao CORS , que quando depurei, liberaram minha mensagem de erro do CORS . Eu não sei por que erros de código não relacionados ao CORS retornam uma resposta de erro do CORS, mas isso me levou à toca do coelho errada por um bom número de horas ...

tl; dr - verifique seus logs de função do firebase se nenhuma solução CORS funcionar e depure quaisquer erros que você tiver

tbone849
fonte
1
isso me deixou louco. no meu caso, nem foi um erro no código! foi Error: quota exceeded (Quota exceeded for quota group 'NetworkIngressNonbillable' and limit 'CLIENT_PROJECT-1d' of service 'cloudfunctions.googleapis.com quota então basicamente livre foi excedido e funções retornou erro coros
Stanislau Buzunko
Aconteceu algumas vezes aqui, o mesmo erro é retornado do servidor e também o cors: Erro: interno é basicamente o erro. Este erro também ocorrerá se você executar a função errada, por exemplo,
digitando incorretamente o
Quando você tenta solicitar a verificação do Google reCAPTCHA na função de nuvem, o navegador também gera o erro CORS. Quando eu verifico o log de funções do Firebase Console, ele diz access to external network resources not allowed if the billing account is not enabled. Depois de ativar a conta de cobrança, ela funciona perfeitamente. Este também é um dos exemplos não relacionados a cors, mas é gerado um erro de cors.
Antonio Ooi
19

Tenho um pequeno acréscimo à resposta do @Andreys à sua própria pergunta.

Parece que você não precisa chamar o retorno de chamada na cors(req, res, cb)função, portanto, basta chamar o módulo cors na parte superior da sua função, sem incorporar todo o seu código no retorno de chamada. Isso é muito mais rápido se você quiser implementar cors posteriormente.

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

Não esqueça de iniciar cors como mencionado no post de abertura:

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

Jaap Weijland
fonte
1
isso funcionou quando outras respostas Assim, com a criação dos cabeçalhos manualmente não
Jim Fator
Isso funciona, mas pode causar erro do TSlint se você o tiver ativado e não puder implantar na base do fogo. Coloque a resposta dentro do fecho de coros para superá-locors(request, response, () => { return response.send("Hello from Firebase!"); });
Spiral Out
1
2 erros aqui pessoal. Primeiro. Qualquer coisa após a função cors será executada duas vezes (já que a primeira solicitação é preflight). Não é bom. Segundo, @SpiralOut, sua solução fará com que você perca o logon nas funções da nuvem (muito ruim) e a funcionalidade assíncrona / aguardada adequada, corre o risco de o conteúdo da função ser prematuramente encerrado no retorno de chamada.
Oliver Dixon
@SpiralOut você pode simplesmente desativar tslint
Vlad
1
Tendo aprendido muito sobre o gcf no ano passado, eu não recomendaria mais esta resposta. Poderia ser útil para protótipos rápidos, mas evite isso em casos reais de produção #
Jaap Weijland 17/03/19
11

Isso pode ser útil. Criei a função de nuvem HTTP do firebase com express (URL personalizado)

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

Certifique-se de adicionar seções de reescrita

"rewrites": [
      {
        "source": "/api/v1/**",
        "function": "functionName"
      }
]
Sandy
fonte
1
Sua resposta é muito baixa, meu amigo, melhor resposta de longe.
Avram Virgil
Obrigado. @AvramVirgil
Sandy
Isso foi o mais rápido e fácil de todos, obrigado!
Gaurav Kakkar
8

Acabei de publicar um pequeno artigo sobre isso:

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

Geralmente, você deve usar o pacote Express CORS , que requer um pouco de hacking para atender aos requisitos do GCF / Firebase Functions.

Espero que ajude!

mhaligowski
fonte
4
Não sabe o que você quer dizer com hacking? Gostaria de elaborar um pouco? Ler o seu post, mas eu não vejo você mencioná-lo
TheeBen
1
autor do módulo cors aqui; por "hacking" mhaligowski significava simplesmente que ele tinha que encerrar a chamada para o módulo cors para fazer com que ela correspondesse à maneira como o Express chama o middleware (ou seja, fornecer uma função como terceiro parâmetro após solicitações e res)
Troy
4

Se houver pessoas como eu por aí: Se você quiser chamar a função de nuvem do mesmo projeto que a função de nuvem, pode iniciar o firebase sdk e usar o método onCall. Ele vai lidar com tudo para você:

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

Chame esta função assim:

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

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

Se você não pode iniciar o SDK, aqui está a essência das outras sugestões:

Chronnie
fonte
3
na verdade, quando eu uso o onCall func no navegador, recebo um erro de cors. Posso definir cabeçalhos de custo nesta solicitação?
Viktor Hardubej 23/10/19
4

Foi encontrada uma maneira de habilitar cors sem importar nenhuma biblioteca 'cors'. Ele também funciona Typescripte o testou na versão 81.0 do 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
fonte
3

Pelo que vale a pena, eu estava tendo o mesmo problema ao passar apppara o onRequest. Percebi que o problema era uma barra no URL de solicitação para a função firebase. O Express estava procurando, '/'mas eu não tinha a barra à direita na função [project-id].cloudfunctions.net/[function-name]. O erro do CORS foi um falso negativo. Quando adicionei a barra final, recebi a resposta que estava esperando.

Shadyhill
fonte
Também certifique-se de adicionar o seu [project-id]como este foi o problema que enfrentamos
unplugged
3

Só assim funciona para mim, pois tenho autorização no meu pedido:

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
fonte
as funções do Google Cloud não permitem a origem do curinga: cloud.google.com/functions/docs/writing/…
Corey Cole
3

Se você não pode / não pode usar o plugin cors, chamando o setCorsHeaders() função logo na função manipulador também funcionará.

Use também as funções respondSuccess / Error ao responder novamente.

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

Se você estiver testando o aplicativo firebase localmente, precisará apontar funções para em localhostvez de nuvem. Por padrão, firebase serveoufirebase emulators:start aponta as funções para o servidor, em vez do host local, quando você o usa no aplicativo da web.

Adicione o script abaixo no cabeçalho html após o script init do firebase:

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

Certifique-se de remover esse trecho ao implantar o código no servidor.

JerryGoyal
fonte
2

Alterar truepor "*"fez o truque para mim, então é assim que parece:

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

Eu tentei essa abordagem porque, em geral, é assim que esse cabeçalho de resposta é definido:

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

Esteja ciente de que isso permitirá que qualquer domínio chame seus endpoints, portanto, NÃO é seguro.

Além disso, você pode ler mais sobre os documentos: https://github.com/expressjs/cors

Roubar
fonte
1

Se você não estiver usando o Express ou simplesmente desejar usar o CORS. O código a seguir ajudará a resolver

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

No meu caso, o erro foi causado pelo acesso limitado ao invocador da função de nuvem. Adicione todos os usuários ao invocador da função de nuvem. Por favor, pegue o link . Consulte o artigo para obter mais informações

Kacpero
fonte
Por favor, forneça uma explicação do material vinculado em sua resposta, por que é relevante e tal
Firefly
0

Se nenhuma das outras soluções funcionar, tente adicionar o endereço abaixo no início da chamada para ativar o redirecionamento CORS:

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

Código de amostra com solicitação JQuery AJAX:

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

Adicionando minha experiência. Passei horas tentando descobrir por que tive um erro de CORS.

Acontece que renomeei minha função de nuvem (a primeira vez que tentei após uma grande atualização).

Portanto, quando meu aplicativo firebase estava chamando a função de nuvem com um nome incorreto, ele deveria ter gerado um erro 404, não um erro CORS.

A correção do nome da função de nuvem no meu aplicativo firebase corrigiu o problema.

Preenchi um relatório de bug sobre isso aqui https://firebase.google.com/support/troubleshooter/report/bugs

Thomas
fonte