Securely invoking a Cloud Function from client (Flutter Android App, Firebase) [duplicate] - firebase

This question already has an answer here:
Firebase functions: Securing firebase https functions
(1 answer)
Closed 2 years ago.
I am building a Flutter Android app where an user can sign in with a customToken. I have a (Google) Cloud Function that I use to generate a customToken which is sent back to the client (Android App) to sign into Firebase. (the auth provider is not supported by Firebase as of yet - not to my knowledge anyway)
For security and cost-savings purposes, I want to only allow the Cloud Function to be accessed by authorised users, and hence, I removed the allUsers member as a Function Invoker role.
I then created a service account (within the same Firebase project) and gave it the Function Invoker role. The problem now is how do I "authenticate" the client (Android App) to access this function, without being signed in (since you need the customToken to sign in), and also without saving credentials on the client?
Or am I approaching this problem the wrong way? Should that Cloud Function be public? My knowledge of Firebase, Cloud Function and security is also very limited, so any advice/suggestion would be greatly appreciated.

Cloud IAM user permissions (such as allUsers and service accounts) don't have anything to do with Firebase Auth users.
Cloud Functions that need to be directly accessible by web and mobile clients must have IAM allUsers permission. They don't need a service account for permission.
If you need to check the Firebase Auth user account that's invoking the function, the client will need to pass an ID token to the function, and the function will need to verify that token with the Firebase Admin SDK in order to find out who the user is, then decide if the should be able to continue execution. This process is described in more detail in the documentation.
If you aren't using Firebase auth, there's not much else you can do from a security perspective, as you would have to include the credentials in the app itself, or otherwise make those credentials publicly available, which is not secure at all. Or in other words, you can't limit access to your function from just your own app code.

Related

Does "allUsers" in Cloud Functions allow only the users of my app?

I recently started working with firebase functions. My intention is to have a function that "all users" within my app can use. More specifically I want everyone that is signed in to their account to be able to call this function. You can't reach calling the function without being logged in.
Would it be appropriate to use the authentication "allUsers" to this function in google cloud, or does this tag mean more than what I specified?
The Cloud Functions setting "allUsers" allows all users, not just those of your app. If you want to restrict usage to just the users of your app, have a look at Firebase App Check which you can use to enforce this requirement.
As #ESun commented too, if your users are signing in with Firebase Authentication, you can validate that too. See How to protect firebase Cloud Function HTTP endpoint to allow only Firebase authenticated users?

Secure app using firebase auth to stop malicious use of the key which is on the client

I was wondering how to to secure firebase auth. I plan on using firebase JUST for user authentication (not using firestore or realtime db). Since the API key is exposed on the client, my fear is that a malicious user can find the key and start using it inappropriately. So far I've done the following to try to improve security:
Limit key use to a specific domain
Restrict the key to only be able to use "Identity Toolkit API"
Is there anything else I should do here?
My application should be the only one able to use my credentials to access the Firebase API.
For any app where you access a cloud based API directly from within the client-side application code, that is going to be a myth. The closest you can get within Firebase these days is with App Check, but that isn't available for Authentication calls at the moment.
Part of the reason for this is that the authentication API is quite well protected on its own already, and most abuse will actually not affect you as a developer very much. E.g. (ignoring phone auth) there is no charge for account creation, sign in, and any other operations.
I highly recommend checking:
Is it safe to expose Firebase apiKey to the public?
The documentation on API keys in Firebase.
The documentation on Firebase's security rules, which is how you can protect the Firestore and Realtime databases, and files in Cloud Storage.
The documentation on Firebase App Check, which reduces abuse for Realtime Database, Cloud Storage, Cloud Functions, and Firestore at the moment.
More of these previous questions on allowing only you app to access Firebase

Is it possible to only allow authenticated users to use callable function, without getting billed for invalid invocations?

I have a callable function that gets users auth data and validates it (inside the function), if user is not authenticated or allowed to do a certain action I throw respective error.
However nothing stops malicious actors to run a loop that constantly pings such function and rack up cost in terms of invocations / little bit of time those functions run.
Cloud providers like AWS have systems in place where this auth check is set up on an api gateway / loadbalancer level and users are not billed if someone calls functions with unexpected headers / payload or without authentication.
Does something like this exist for firebase, perhaps via google cloud?
No, it's not possible. The callable function must execute in order for the firebase-functions SDK to verify the user's ID token via the Firebase Admin SDK. It's not a "free" operation in any way. No matter how you write or deploy it, something is going to have to invoke the Firebase Admin SDK to verify the token - it's not part of any unbilled cloud infrastructure.

Cloud Function Error: Forbidden unless I open function to allUsers

This is a web page located on Firebase Hosting, served dynamically through a rewrite to a Cloud Function
When browsing to the url, I receive "Error: Forbidden
Your client does not have permission to get URL [/theURL] from this server.
Everything works as long as I have "allUsers" added as a Cloud Functions Invoker on the function via the GCP console. I have confirmed this with testing just throwing that one switch and getting two different results.
I am logged into my Chrome browser with the same Google account as the owner of the GCP project
I am logged in to my Firebase website (Firebase Authentication and Firestore roles management) using the same Google account, with a Firebase __session cookie and all.
This started when I upgraded my Node.js engine from 8 to 10 with the recent warnings from Google that they're dropping support for 8. (I am assuming, but have no real idea, that the version 8 runtime environment didn't have this feature?)
I wrote these Cloud Functions without any knowledge of GCP authentication, so I am relatively confident that my coding inside the Cloud Function is secure, but if there is another layer of security that I could/should be using, I'm all for it.
I will want this website to be publicly accessible, but I can granulate the functions to public vs. non-public if necessary. (Basically, I was writing them as all publicly accessible and internally authenticated/secured anyway.)
I'm not very knowledgeable at all about the GCP authentication requirements, so it's hard for me to pin this down to one central question, but here are some questions I know I don't know:
Should Firebase Hosting be added as a member to GCP's authentication groups, so that GCP only allows service of a Cloud Function to the rewrite requests? It seems that would be the most secure and straightforward method.
Why is GCP telling my I'm forbidden when I'm the owner of the whole doggone project? Does this have to do with Firebase Rewrite or maybe HTTP requests being anonymous? If so, then how does one authenticate for a Cloud Function anyway?
Sorry for the large scope of the question; any guidance in the right direction is much appreciated.
GCP allUsers is not related to Firebase Authentication. They deal with different sets of users. A function should have GCP allUsers invoker permission in order to allow access from web and mobile apps where users are signed in with Firebase Auth. GCP does not check the Firebase user - that is up to the function to do, if it wants, using the Firebase Admin SDK.

Is it possible to only accept function calls from my app in Firebase?

I am creating a game in Unity where the user can contribute with levels using a Level Creator system.
My application is setup in a way that I just need to call the Cloud Function with the level info, and it handles duplicate entries and saves it to Firestore. All of this works perfectly.
My question, basically, is: can I have my functions only accept calls from my game? (without having my users registered?).
Naturally, I am using functions.https.onCall((data, context) => {}). In the documentation for Firebase, I noticed they use context.auth to check whether the user is authenticated or not. However, I am logging this value to the console and it appears to be undefined.
I am also confused with this line, from the same link:
With callables, Firebase Authentication and FCM tokens, when available, are automatically included in requests.
Maybe context.auth is not defined because my game isn't yet in Google Play / Apple Store? Any ideas?
Thanks to the new feature called Firebase App Check, it is now actually possible to limit calls to Callable Cloud Functions to only those coming from iOS, Android and Web apps that are registered in your Firebase project.
You'll typically want to combine this with the user authentication based security that Doug describes in his answer, so that you have another shield against abusive users that do use your app.
It's not possible to restrict invocations of a callable function to just one app, and it doesn't matter if the app is published to any stores. Once you deploy a function, it's accessible to anyone with an internet connection.
The best you can do is require your users to be authenticated with Firebase Authentication in your app, then check context.auth in the function to determine if the it should do what the user wants. context.auth will be undefined in the case of no authentication. If your code determines that the function should not go any further, you can return early. But the function is still invoked.

Resources