Using locally emulated https.onCall Firebase functions in locally hosted Firebase application - firebase

I am writing a Firebase application using the node SDK and vanilla JavaScript on the client side. I am using Firebase Cloud Functions to implement a server that receives requests for my page routes and returns rendered HTML with the https.onRequest method. I am also using Cloud Functions to handle client-server interaction with the https.onCall method.
I develop locally using the firebase serve command. When developing locally, my client seems ignores my local onCall functions, instead calling the route for the deployed onCall functions. I am forced to deploy my onCall functions in order to see changes locally. If I do not deploy each change, my local app will not show any changes to onCall functions. This happens whether I run firebase serve or firebase serve --only=hosting,functions.
When I run my app locally with firebase serve, the pages are generally hosted at localhost:5000. The functions are hosted at localhost:5001. If I call a cloud function on one of these locally hosted pages, like firebase.functions().httpsCallable('functionName') and check the Network panel in my developer tools, I can see the Request URL is https://us-central1-<app-name>.cloudfunctions.net/<functionName>, instead of localhost:5001/<app-name>/us-central1/<functionName>.
This is frustrating me because it means I have to deploy my functions to test each change, rather than testing my local functions through my locally hosted web application.
Have I configured something incorrectly? How can I get my locally hosted app to use my locally emulated onCall cloud functions?
I am not making a single page application or using any view frameworks.

It seems like Firebase has yet to implement a solution to point to the local server, so I came up with a little hack. Include the below snippet where you initialize your Firebase project. Hope it helps!
if (process.env.NODE_ENV === 'development') {
firebase.functions()._url = function (name) {
return `${process.env.API_URL}/${name}`
}
}
EDIT: Like goker.cebeci commented below, the correct way to connect to your local emulator is to use functions.useFunctionsEmulator('http://localhost:5001') (change http://localhost:5001 to whatever address your local emulator is running on)
The updated code looks like this:
if (process.env.NODE_ENV === 'development') {
const functions = firebase.functions()
functions.useFunctionsEmulator('http://localhost:5001')
}

Updated version for Firebase v9.x.x:
import { getApp } from "firebase/app";
import { getFunctions, connectFunctionsEmulator, httpsCallable } from "firebase/functions";
const functions = getFunctions(getApp());
connectFunctionsEmulator(functions, "localhost", 5001);
httpsCallable(functions, 'helloWorld')({foo: "bar"}).then((res) => ...)

Related

understanding how server-time is calculated when integrating next.js with firebase-hosting?

I want to make a general refactor to my app that will mover from react.js (client-side)
to next js with SSR, where I will use the following props to pre-render data... and my app pages,
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
how to does firebase charge for the SSR ? since it is not a cloud function, technically for next.js app to be hosted on firebase there should be a server time, as I understood the idea, how is this server time goin to be calculated in the context of the hosting and out of the context of cloud functions, please elaborate if I misunderstand the concept behind ssr with next.js and firebase.
Firebase Hosting on its own will never execute any of your code on its servers.
If you run a web app that performs Server-Side Rendering on Firebase, you are using Cloud Functions or Cloud Run to execute that code for which Firebase Hosting provides convenient connectors.
So if you get the server-side timestamp in SSR, that's also where any billing for such server-side code comes from. Also see this line from the Firebase documentation on using web frameworks:
Prerequisites
...
Optional: Billing enabled on your Firebase project (required if you plan to use SSR).
For a good primer on hosting server-side dynamic content on Firebase Hosting, see the documentation on serving dynamic content and host microservices using Firebase Hosting.

Localhost Firebase Cloud Functions are working with the emulator but not live

I have a Firebase app with an App Check debug token implemented.
When I run my Cloud Functions using the Emulator they work fine.
But when I turn off the emulator and try to access the live deployed version from http://localhost:8080/ I get this console error:
POST http://localhost:5001/my-app/us-central1/getZohoDeskTicketsLoggedInUser net::ERR_CONNECTION_REFUSED
Why does this happen when calling the live version and not with the emulator?
There are no Firebase Cloud Function logs to provide because the functions never even fire.
Perhaps I need to white list the locahost domain somewhere?
When you deploy your web app to productions, you should use URL of deployed Cloud function to make your API requests instead of localhost which will lead to connection errors. You can additionally call connectFunctionsEmulator only when you are on localhost:
if (window.location.hostname === "localhost") {
connectFunctionsEmulator(...)
}

Firebase emulators blocking firebase functions?

So in my web app, I am calling an https callable cloud function like so
const createStripeCheckout = functions.httpsCallable('createStripeCheckout')
//Call function
And up until I started using the firebase emulator, everything was fine. It was making a request to the https endpoint, and the checkout was made successfully. But after I used functions in the emulator, it makes a request to the local dev server (localhost:5000/firebase-project/name) instead of the correct https endpoint hosted by firebase (firebase/name) and the function can't work unless I start the emulator, and will only work on my computer. I don't recall changing any settings.
Found the problem, in my code there was a line that read:
functions.useEmulators("localhost", 5001)
I guess that directed all functions to that adress

Firebase functions authorize only requests from Firebase hosting app

I have a simple Firebase Hosting web application (based on a Vue app) which invokes Firebase Function (Google cloud function):
import firebase from "firebase/app";
import "firebase/functions";
firebase.initializeApp(firebaseConfig);
let functions = firebase.app().functions("us-west4");
let testFunction = functions.httpsCallable("testFunction");
and corresponding functions index.js file:
const functions = require("firebase-functions");
exports.testFunction = functions.region("us-west4").https.onCall(async (data, context) => {
console.log("Very important things here");
return {"response": "data"};
});
From security perspective is it possible to
Allow this invocation only from my domain name (Firebase hosting) myhostedapp.web.app
Check for any kind of authentication (e.g. token) that my JS app provides during the request?
I've tried accessing context.auth property (see docs) buth seems like some kind of service account is required and this cannot be used when called from Firebase hosting web application.
Basically I don't want my function to be publicly accessible (simple invocation via trigger url), so any advice or best practice for securing Firebase Hosting + Functions would be appreciated.
Firebase just released a new feature called App Check that does precisely this: it allows the Cloud Functions in your project to only be invoked from apps that are registered in that project.
For web apps this happens through reCAPTCHA v3, which . Then once you enable enforcement of the check on Cloud Functions, it will reject any requests coming from other sources.
You'll typically want to combine App Check with your current user-based approach, so that you can easily block calls from outside your web app, but also still ensure authenticated users only can make calls that they're authorized for.

How to get the firebase functions URL in a firebase hosted webapp?

I am working on a project in which we have pre-existing cloud functions in use with Firebase. We are adding a small React SPA using firebase cloud hosting, and this SPA will interact with some of the existing public cloud functions.
The way we have been doing things so far, we have a dev project, and a production project in Firebase. For cloud functions, this works fine, we have environment specific config set up with firebase functions:config:set for differentiations between prod and dev servers.
The problem comes with the hosted SPA contacting the cloud functions. I've seen a lot of questions on how to access the environment config in the hosted code, eg this one: How to reference Firebase Functions config variables from a Firebase-hosted application? where the answer seems to be to have firebase functions that return the values of the environment variables, but for me this just moves the problem further back on step. I fully understand that having the environment variables accessible to this code would be a massive security problem as the SPA is run in the browser.
The only environment specific config I really need for the hosted SPA is the base address for the cloud functions.
eg if in my cloud functions I have
const functions = require('firebase-functions');
const express = require('express');
const test = express();
test.on('/hello/:target', (req, res) => {
res.send(`Hello ${req.params.target}`);
})
exports.test = functions.https.onRequest(test);
then having deployed, this cloud function is available both at https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net/test/hello/world and https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net/test/hello/world . How would I best get the appropriate root url (https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net or https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net) for the project that the SPA is deployed to?
eg. is there some global I can access in the frontend js code where I could do something like:
const url = `${__FIREBASE_GLOBALS__.cloudFunctions.baseUrl}/test/hello/${input}`;
And have the url be correctly defined based on which project the hosted app is deployed to?
I'm assuming here that you're not using Firebase in any other way in your SPA other than to call Cloud Functions (since you didn't say otherwise).
Read the Firebase web setup docs for Firebase Hosting, especially the section on SDK imports and implicit initialization. When you host a site with Firebase Hosting, there are some special URLs that give you the configurations for that project. There are some special script includes that give you access to Firebase products. In particular, note the relative path URI /__/firebase/init.js will yield JavaScript that initializes the Firebase JavaScript SDK with the default settings for your project. Go ahead and access that in a browser pointing to your web app. You're probably interested in the projectId property of the config.
If you want to get a hold of that value, you can use the Firebase SDK, which would be initialized by the script includes from the first link above. Minimally, you could add:
<script src="/__/firebase/5.8.2/firebase-app.js"></script>
<script src="/__/firebase/init.js"></script>
Then later on (see API docs):
firebase.app().options.projectId
to get the ID of the project where Firebase Hosting is serving the content. You can use that to build the URL to your functions.
It might also be convenient for you to port your HTTP functions to callable functions and invoke them from the web site with the Firebase SDK to invoke kthem. Or not.
I was able to get the region and appId from the environment variables.
eg:
console.log(process.env);
Check your firebase logs
{ ...
ENTRY_POINT: 'server',
X_GOOGLE_FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
FIREBASE_CONFIG: '{"projectId":"pid","databaseURL":"https://pid.firebaseio.com","storageBucket":"pid.appspot.com","locationId":"europe-west"}',
X_GOOGLE_FUNCTION_NAME: 'server',
FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
X_GOOGLE_GCLOUD_PROJECT: 'pid',
FUNCTION_NAME: 'server',
X_GOOGLE_GCP_PROJECT: 'pid',
X_GOOGLE_FUNCTION_REGION: 'us-central1',
FUNCTION_REGION: 'us-central1',
X_GOOGLE_ENTRY_POINT: 'server',
GCLOUD_PROJECT: 'pid',
GCP_PROJECT: 'pid',
... ommited
}
Out of these GCP_PROJECT, GCLOUD_PROJECT, FUNCTION_REGION, FUNCTION_NAME should work. So for eg. process.env.FUNCTION_REGION
Not sure how reliable this will be.

Resources