How to fix Firebase CORS errors in callable functions? [duplicate] - firebase

This question already has answers here:
Firebase Callable Function + CORS
(21 answers)
Closed 1 year ago.
I have a problem with Firebase and CORs, apparently it cannot reach the endpoint with errors like:
Access to fetch at
'https://europe-west2-XXX.cloudfunctions.net/fetchChatToken'
from origin 'https://trato.app' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
service.ts:203
POST
https://europe-west2-XXX.cloudfunctions.net/fetchChatToken
net::ERR_FAILED (anonymous) # service.ts:203 ... ...
error.ts:66 Uncaught (in promise) Error: internal
at new t (error.ts:66)
at error.ts:175
at e. (service.ts:276)
at tslib.es6.js:100
at Object.next (tslib.es6.js:81)
at a (tslib.es6.js:71)
I also checked the network tab on dev inspector (chrome) to check if the CORS header is there, i dont see it.
Also, I have been checking firebase functions logs and apparently is not being even invoked, the last line showing is the deployment.
the way that Im using it is this:
Front End side:
const functions = firebaseApp.functions('europe-west2');
export const fetchChatToken = async () => (await functions.httpsCallable('fetchChatToken')()).data;
Functions (Backend) side:
const ensureAuthentication = auth => { if (!auth) throw new HttpsError("unauthenticated", "authentication required"); };
exports.fetchChatToken = functions.region("europe-west2").https.onCall((data, context) => {
ensureAuthentication(context.auth);
try {
const { AccessToken } = twilio.jwt;
const { ChatGrant } = AccessToken;
const grant = new ChatGrant({
serviceSid: conversationsid
});
const token = new AccessToken(accountsid, apikey, apisecret);
token.addGrant(grant);
token.identity = context.auth.uid;
return token.toJwt();
} catch (error) {
console.error(error);
throw new HttpsError("internal", "internal error");
} });

Unfortunately there many reasons possible for this CORS error. If the cloud function returns an "internal" error message it might be due to inconsistent Regions or errors in your cloud function code. My checklist for this error when creating a new cloud function:
Not matched Regions of Firestore-Project, Functions and Client side init cause a CORS Error
internal code errors inside the cloud functions cause this error
new function must be included in cloud function index file (if used)
cloud function name must match the string on client side invocation
delete cloud function in firebase dashboard before deploying new one after error

Make sure the function name referenced in the client is correct, see https://stackoverflow.com/a/62042554/1030246

I got it solved changing it to us, basically removing the region, taking out the 'europe-wes2' region from the function declaration and from the function call it works fine again.
I assume there is some error on the firebase side.

Related

Why I can not connect to Firebase?

I'm trying to connect my app to firebase but the only response I get is not the response json I need from firebase. I included my call to firebase below. Is the url not correct? The response I'm getting back is not the json object made with firebase that I created.
``
<script>
(async function call () {
console.log("hello")
const endpoint = url
console.log(endpoint)
async function initiation () {
const result = await fetch(endpoint, {mode: "no-cors"})
const data = await result
console.log(data)
}
initiation()
})()
</script>
``
is your database in us-central1?
according to documentation [1] "the form https://<"databaseName">.firebaseio.com (for us-central1 databases) or https://<"databaseName"><"region">.firebasedatabase.app (for databases in all other locations)."
If its in another region you should try with https://<"databaseName"><"region">.firebasedatabase.app
[1]https://firebase.google.com/docs/database/web/start#initialize_the_javascript_sdk
The structure within your code seems odd, if you are implementing the CDN you need to initiate your app with your project credentials, right now you are only accessing a real-time database as a public request and does not provide any additional validators as the database is most likely to have Security Rules enabled.
To request data from the endpoint, you need to also include a .json at the end of the URL https://[PROJECT_ID].firebaseio.com/users/jack/name.json
Source: https://firebase.google.com/docs/reference/rest/database#section-get

How to use multiple cookies in Firebase hosting + Cloud Run? [duplicate]

i followed the sample of authorized-https-endpoint and only added console.log to print the req.cookies, the problem is the cookies are always empty {} I set the cookies using client JS calls and they do save but from some reason, I can't get them on the server side.
here is the full code of index.js, it's exactly the same as the sample:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
const app = express();
const validateFirebaseIdToken = (req, res, next) => {
console.log(req.cookies); //// <----- issue this is empty {} why??
next();
};
app.use(cors);
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.get('/hello', (req, res) => {
res.send(`Hello!!`);
});
exports.app = functions.https.onRequest(app);
store cookie:
curl http://FUNCTION_URL/hello --cookie "__session=bar" // req.cookies =
{__session: bar}
doesn't store:
curl http://FUNCTION_URL/hello --cookie "foo=bar" // req.cookies =
{}
If you are using Firebase Hosting + Cloud Functions, __session is the only cookie you can store, by design. This is necessary for us to be able to efficiently cache content on the CDN -- we strip all cookies from the request other than __session. This should be documented but doesn't appear to be (oops!). We'll update documentation to reflect this limitation.
Also, you need to set Cache-Control Header as private
res.setHeader('Cache-Control', 'private');
Wow this cost me 2 days of debugging. It is documented (under Hosting > Serve dynamic content and host microservices > Manage cache behavior, but not in a place that I found to be useful -- it is at the very bottom "Using Cookies"). The sample code on Manage Session Cookies they provide uses the cookie name session instead of __session which, in my case, is what caused this problem for me.
Not sure if this is specific to Express.js served via cloud functions only, but that was my use case. The most frustrating part was that when testing locally using firebase serve caching doesn't factor in so it worked just fine.
Instead of trying req.cookies, use req.headers.cookie. You will have to handle the cookie string manually, but at least you don't need to implement express cookie parser, if that's a problem to you.
Is the above answer and naming convention still valid? I can't seem to pass any cookie, to include a session cookie named "__session", to a cloud function.
I setup a simple test function, with the proper firebase rewrite rules:
export const test = functions.https.onRequest((request, response) => {
if (request.cookies) {
response.status(200).send(`cookies: ${request.cookies}`);
} else {
response.status(200).send('no cookies');
}
});
The function gets called every time I access https://www.xxxcustomdomainxxx.com/test, but request.cookies is always undefined and thus 'no cookies' is returned.
For example, the following always returns 'no cookies':
curl https://www.xxxcustomdomainxxx.com/test --cookie "__session=testing"
I get the same behavior using the browser, even after verifying a session cookie named __session was properly set via my authentication endpoint. Further, the link cited above (https://firebase.google.com/docs/hosting/functions#using_cookies) no longer specifies anything about cookies or naming conventions.

Unit test Actions on Google Dialogflow locally

I'm trying to unit test a DialogflowApp locally by using the firebase shell environment. (in a cli do firebase experimental:functions:shell and then call my methods)
I have followed this guide by google https://firebase.google.com/docs/functions/local-emulator but they don't use the DialogflowApp where the invoked function tries to bind a request object containing intents and parameters like this ->
exports.myFunction = functions.https.onRequest((request, response) => {
const app = new App({ request, response });
function myMethod(app) {
let myArgument = app.getArgument(MY_ARGUMENT);
app.tell('Here we are responding');
}
let actionMap = new Map();
actionMap.set(MYMETHOD_ACTION, myMethod);
app.handleRequest(actionMap);
});
Regardless of what request object I send in the CLI, like this myFunction(require("../test/testdata.json")), the request body object is empty, like this body: {} which means I can't do app.handleRequest() or app.getArgument(). The error message I get is
RESPONSE RECEIVED FROM FUNCTION: 400, Action Error: no matching intent
handler for: null
I thought that if I populated testdata.json with the json request data shown in Actions on Google -> console.actions.google.com -> Simulator it would be valid data but no.
My question is, how can i mock my request data so that I can start unit testing my fullfillment methods locally?
EDIT 1:
firebase > myMethod.post("/").form(require("../test/testdata.json"))
Sent request to function.
firebase > info: User function triggered, starting execution
info: Function crashed
info: TypeError: Cannot destructure property `parameters` of 'undefined' or 'null'.
if we look in dialogflow_app.js we can see this code for fetching an argument value
getArgument (argName) {
debug('getArgument: argName=%s', argName);
if (!argName) {
error('Invalid argument name');
return null;
}
const { parameters } = this.body_.result;
if (parameters && parameters[argName]) {
return parameters[argName];
}
return this.getArgumentCommon(argName);
}
this.body_ is always just empty {}, regardless of how and what I send into the method when running locally.
EDIT 3
firebase > myMethod({method: "post",json: true, body: require("../test/testdata.json")})
Sent request to function.
firebase > info: User function triggered, starting execution
info: Function crashed
info: TypeError: Cannot destructure property parameters of 'undefined' or 'null'.
Invoking a Firebase HTTPS function using the shell requires a different form. It takes the parameters that the request module does, so in order to emulate a webhook, it will be something like this:
myfunction({
method: 'POST',
json: true,
body: require("../test/testdata.json")
});
These three parameters are important:
You need to specify that this is a POST operation
You need to indicate that the body will be JSON. This will send the correct header and won't try to send the body as x-www-form-urlencoded
You need to include the body. As an object is ok because you've set the json parameter to true.

Cloud Functions for Firebase Error: Forbidden

I am trying to send multipart/form-data through URLRequest on my app to Cloud Functions for Firebase. And to test if my cloud function and my app are connected, I created a test function and deployed it:
function test(data, callback) {
console.log("Test begin:");
console.log(data);
console.log("Test finish...");
callback(null, null);
}
exports.test = functions.https.onRequest((request, respond) => {
console.log("test called");
test(request.body.data, function(data, error) {
respond.json({
data: data,
error: error
});
});
});
However, after sending the URLRequest, nothing was printed on the console, and instead, I got a html as data. By opening the html, I get Error: Forbidden. Your client does not have permission to get URL / from this server. How can I fix this?
Thanks to #Doug Stevenson, the problem is that I used the wrong URL instead of the provided one. And the URL can be found on the console when you deploy your cloud function.
Cloud Functions has special ways of dealing with different types of input. It's documented here.
For multipart/form-data, you can access the content as request.rawBody.

Firebase cloud function always fails - onerror

I am trying to call firebase cloud function using xmlhttprequest through serve feature(testing locally), the following code is xmlHttpRequest
var req = new XMLHttpRequest();
req.onload = function() {
//success
};
req.onerror = function() {
//always fall to this function
};
var url = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/helloWorld';
req.open('GET', url);
//Chrome does not allow 'Access-Control-Allow-Origin' from
//localhost:5000 (non-google server)
//I am using mozilla firefox with 'serve' feature, hope
//this solve 'Access-Control-Allow-Origin' problem
req.setRequestHeader('Access-Control-Allow-Origin', '*');
req.send();
I can see that cloud function is executed successfully in firebase console. But i am always fall into onerror function.
I also enabled CORS in cloud functions by
const cors = require('cors')({origin: true});
While hosting functions locally i get following error
After research of two day of research ,
Updated node and firebase to compatible version.
Cleared cache
Updated firebase-tools, firebase functions through npm
. Still not working.
Delete the project, re-initialize the project with firebase init function. hope this helps.

Resources