Firebase functions authorize only requests from Firebase hosting app - firebase

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.

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.

How to use Firebase Security Rules to secure Cloud Functions calls with Firebase Authentication?

I'm starting deep on Firebase Security Rules and I'm following the Firebase Security Rules docs, but this document just says about Realtime Database, Cloud Firestore, and Cloud Storage options. There is a way to use Firebase Authentication to protect an invoke of a Google Cloud Function from Client-Side?
I'm trying to use a GC Function as a backend to access the Cloud SQL from a Web Application.
Cloud Functions generally use Admin SDK (or service accounts, application default credentials to access any other services like Cloud SQL) which has complete access to your Firebase project's resources and also bypasses all security rules. That being said you would have to authorize requests yourself. For example, if you are using onCall function:
export const fnName = functions.https.onCall((data, context) => {
const { auth } = context
if (!auth) console.log('User not logged in')
const { uid } = auth;
// UID of user who called the function
// Check if user has access to requested resource
// process request
})
If the caller of function is not authenticated, then context.auth will be undefined.
If your question is if you can prevent the invocation of function at first place, then there's currently no way to do so. You can use Firebase App Check to ensure the function is called from your registered application only.

hide firebase private credentials in client apps

I would like to avoid exposing private credentials in client apps. Doug Stevenson said firebase-authentication-vs-firebase-admin :
The reason why you can't use the Firebase Admin SDK in your app is
because you would have to ship private credentials with your app in
order for the SDK to operate
By saying Admin SDK did he mean when we use for example:
import * as admin from "firebase-admin";
And what about :
import firebase from "firebase/app";
firebase.database().ref ... ?
Is this snippet considered as admin SDK ? To configure firebase we would still need to ship private credential with our client app, which is a security hole. So should we consider NEVER use firebase.database() or firebase.firestore() in client apps and instead use a cloud function ?
If you ship your administrative credentials with your application, anyone can grab those credentials and start calling any API on your project in whatever way they see fit. You have no control whatsoever over this usage, as they'll have the administrative credentials.
For this reason you should indeed not use the Admin SDK in the app, but instead wrap the functionality you need in a custom API (such as in a Cloud Function), where you can ensure its usage is authorized.
This is different from the second snippet in your question, which uses the regular JavaScript SDKs from Firebase. These SDKs don't use administrative credentials to access the project, but instead use the configuration data that is explained here: Is it safe to expose Firebase apiKey to the public?
Access through this configuration data is guarded by the server-side security rules that you've configured for your project. So while the user can still copy the configuration data and call the API on their own, any access has to go through the security rules you configured. And in those security rules you then ensure they can only access data they're authorized to.
But since the Admin SDKs bypass the security rules by design, you won't have that option when you ship the administrative credentials in your app.

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.

How to notify a Firebase Admin SDK user using a cloud function?

I am a student developing an app and I have some back-end python code that utilizes the Firebase admin SDK. The app with provide an interface to an proprietary algorithm that cannot be moved into the a cloud function and so must stay within the back-end server. When a user makes a request to the algorithm, they will do so by uploading a document to Firestore with information the back-end needs to process their request.
Once a user uploads a document, an onCreate() cloud function is triggered, the goal of this function is to simply notify the back-end that there is a pending request, so that it can process it and send back to the user.
This is where I am struggling, I haven't been able to deduce a way to trigger action on the back-end from within the cloud function. I am hoping to find a way to accomplish this through Firebase without the need to implement additional libraries etc.
A way to generalize my issue would be:
How would you notify an Firebase Admin SDK user through a Cloud Function?
FCM is used for sending messages and notifications to mobile clients. It doesn't work for sending messages to backend components.
If you want to notify some backend component, you typically use an HTTP endpoint or pubsub messaging.
Firebase Functions SDK already uses the Firebase Admin SDK under the hood. So if you set up a Cloud Functions trigger in Node.js, you can access Admin SDK from the Function itself. For instance, taking a Cloud Storage trigger as an example:
const admin = require('firebase-admin');
admin.initializeApp();
exports.fn = functions.storage.object().onFinalize((object) => {
// Call Admin SDK APIs
});
Similar integrations should be possible with Python as well: https://medium.com/#hiranya911/firebase-using-the-python-admin-sdk-on-google-cloud-functions-590f50226286

Resources