Securely setting the first custom claim on a Firebase user - firebase

What is the standard, secure way to set the first custom claim on all Firebase users?
Firebase provides some great documentation and examples for understanding and using custom claims -- e.g. this great video example -- but most examples use an existing custom claim to authorize the creation of other custom claims; and as of this post the Firebase console provides no way to set/edit/view custom claims, nor can custom claims be set via the CLI.
Here are some options I am considering:
Create a distinct admin project, which can be used by a service account to create custom claims via the Firebase Admin SDK.
Use a Cloud Function to perform custom claim creation iff a certain Firebase console action is taken, e.g. creating a Firestore Document in collection inaccessible via security rules.
Ignore security for the creation of the first custom claim; only add security after this is already a custom claim on a Firebase user.
Have you encountered this problem and solved it more-elegantly?

There is no real standard way to set Custom Claims. The only constraint, as you know, is that they can only be set from a privileged server environment by the Firebase Admin SDK, i.e. from one of your servers, or, easier and more serverless-oriented, via a Cloud Function.
So, within this constraint, you can do whatever you want. The first two options in your question are totally valid and good ones, IMO. I've wrote an article about a year ago (How to create an Admin module for managing Firebase users access and roles) in which we use a Callable Cloud Function to do the job. Today, in most of my projects, I prefer to use a Firestore collection which triggers the Cloud Function, but it is more or less equivalent (the Callable Cloud function in the article actually creates a Firestore doc).
In this article, I share a simple approach for creating the first Claim (which I call the Admin user Claim): use a temporary Cloud Function that you trigger by creating a doc in a temporary, secured, Firestore collection. Not a very elaborated and elegant method, but it does the job...
About your third option ("Ignore security for the creation of the first custom claim") I don't think you need and should do that.
You can do as described in the article and above. In a nutshell:
Set up your system with access rights restricted to the user with the Admin Custom claim (e.g. a security rule to create a doc in the dedicated Firestore collection, or a check in a Callable Cloud Function that the caller has the Admin Claim)
Create the Admin user in the Auth service
Assign him the Admin user Claim via the method detailed above.
You are done and no security hole.
Finally, it's worth noting that a new experimental Extension dedicated to setting claims with Firestore was launched in January this year. See here and here.

Related

Protect Firebase Http Cloud Functions Using JWT Tokens or any other mechanism without using firebase user authentication

Edited
I am building a spa app to make some administrative tasks on the app/firebase easier. I don't have a separate backend for this app so I use firebase cloud functions directly from the spa client. I am also not using Admin Sdk , just Http triggers. I am using functions.https.onCall on functions invoked by regular app users. Since i didn't want to mix up regular users with the admin login , I implemented a separate login for the spa app. Since the admin is already logged in , I am looking for some kind of JWT key / API key based authentication to secure firebase functions implemented for these tasks.
One possible solution , as Frank van Puffelen mentioned in his answer , is to add firebase auth , store the UIDs of the admin users and check if the request came from them using getIdToken() in the cloud function. Is this the best practice or is there an alternate to approach this.
Any input is appreciated.
The common approaches here are to:
Either keep a list of the UIDs of the admin users in a place that your Cloud Function can access, for example in the code itself or (more likely) in a cloud-hosted database.
Or add a custom claim to the ID token of admin users.
In either case, your Cloud Functions code can then ensure the user is authorized to call the end point.

Flutter - Understanding Firebase Admin and how to get a user's information from email/uid/name

I'm making a little Snapchat clone, and a part of this app I'm trying to build is the ability to add a friend and start a conversation with them. I'm using Firebase to manage my users and I'm a little stuck now trying to figure out what works and why I'm getting problems trying to use some methods or functions.
What I want is this simple line of code to work:
var userByEmail = await _admin.app().auth().getUserByEmail("b#gmail.com");
print(userByEmail.toString());
However this has been giving my some problems, most recently, the following error message:
Unhandled Exception: FirebaseAuthError(auth/invalid-credential): Must initialize app with a cert credential or set your Firebase project ID as the GOOGLE_CLOUD_PROJECT environment variable to call verifyIdToken().
Getting to this point made me want to first ask a question about FirebaseAdmin and Auth before continuing and potentially screwing up my app settings.
Is there a simple way to do what I'm trying to do?
I have a Firebase.instance.initializeApp() in my Main function, do I only ever call that once or should I start initilizeApp in the initState of each Stateful Widget where needed?
What does this error message actually mean?
You are trying to use the Firebase Admin SDK in your Flutter code, which is not possible. The Admin SDKs give full administrative access to your Firebase project, which would be a serious security concern if you allow that in your Flutter app.
If you want to allow certain administrative functionality in your application, you will have to make that functionality available yourself. For example, to look up a user by their email address, there are two common approaches:
Store the minimal information about each user in a cloud-accessible database (such as Firebase's Realtime Database or Cloud Firestore) when each user registers with your app, and then look it up from there.
Wrap the getUserByEmail from the Admin SDK in a custom API that you make for yourself, on a server you control or in Cloud Functions. In that API you validate that the user making the call is authorized to do so, then call Firebase through the API you were trying to use, and return the minimal result back to the caller.
Both of these are feasible and can work to solve a variety of use-cases. But if you've never built backend code before, you might find the first approach easier to get started with.
Also see:
How to get Firebase UID knowing email user?
Flutter get User Data from Firebase
The right way to do what you want is using Firebase auth, authenticating your user and using a collection to store and retrieve users information. That auth information provided by firebase should only be used for authentication and security purposes.
The Firebase admin must have a user logged in to work properly, but its purpose is to provide a more administration environment and should not be used inside a clients app, unless its an admin app.
With all that said, lets go for the rescue:
Authenticate your user (using firebase auth);
After auth, save all the user information you want to share with other user inside its own collection (you will need to create one);
When an authenticated user (this is important) 'request any other users data, you query for the data in the previous created collection.

Pass context value into firestore document add/set/update from Admin SDK

I am looking to use the https.onCall to accept some input from a user (such as data about another user). I'd then like to do some advanced processing on that data including retrieving sensitive data from other entries on my firestore that should not be exposed. Depending on the outcome of that analysis, I will update other locations in the database. However, I am concerned about the security of the original call and its source. I know that I have the context parameter on the onCall to verify the source was logged in, but I'd like to apply security rules to the final write based on the context.auth provided to the cloud function.
The security rules are straight forward for normal database operations but not if I'm doing an operation (seeded by a normal user) routed through the Admin SDK.
Thoughts?
but I'd like to apply security rules to the final write based on the context.auth provided to the cloud function
As you are aware that you can identify which user made a call to functions as well as that Admin SDK has super-access to database, the general flow should be to write functions in a way that they only edit documents that should be editable by the user.
If you had still like to narrow down access, you can do that for firebase database by passing databaseAuthVariableOverride when initializing admin app.
Read more on authenticating with limited privileges
When you use the admin SDK, or any of the server SDKs, it always bypasses all security rules. Rules only apply to access coming directly from web and mobile clients using the client SDKs.
If you need to apply some sort or restricts to data written from your backend, you will need to code that into the logic of your backend code. Security rules will be of no help.

Flutter/Firebase: Admin features in-app or cloud functions?

I'm writing an app with Flutter and Firebase (using both Firestore, Storage and Authentication so far).
Currently the app shows content from Firebase, but now I'm trying to figure out how the best way is to implement writing/editing/removing stuff in Firebase.
The goal is to have users with admin privileges.
My question is if I can build an Admin Panel inside the client app (which would be ideal), or if that's considered bad practice and I should build an Admin Panel in another app and using Cloud Functions.
For example, currently I perform Authentication (signup/register) in the Flutter/Dart code and when registering it creates a field in Firestore isAdmin = false, which I then can manually set to true (if I want) in the Firestore console. Could this somehow be an "unsafe" way of doing this?
The goal is to have users with admin privileges
Since you are using the Authentication service you already have half of the solution: with authentication you can identify each user who is using your app.
The other part is Authorization: this is normally done with Security Rules in Firebase, both for Firestore and Cloud Storage.
To be able to authorize certain users (identified through authentication) with Admin privileges, you need to know which users have the admin role in such a way you authorized them to execute the admin functions.
One possible way to identify the admin users is to have an isAdmin flag in some user documents in Firestore, as you mention in your question. There is an example of Firestore Security Rule using this approach in the documentation.
HOWEVER, you will encounter some problem if you want to use this flag (stored in Firestore) with Security Rules for Cloud Storage. At the time of writing, it is not possible to read the value of a Firestore document in Security Rules for Cloud Storage.
The solution is to use Custom Claims. You will find all the details in the doc on how to implement it in such a way it fulfill your needs.
Can I build an Admin Panel inside the client app?
Yes, you can very well do that. As soon as your security is correctly implemented (through Authentication and Security Rules, as explained above), there is nothing that prevents you to develop an Admin panel. If a user that is not admin can access the Admin panel, he/she will not be able to perform the admin actions (i.e. writing/editing/removing Firestore or Cloud Storage data).
Moreover, with Custom Claims, you can access them in the front-end to modify the client UI based on the user's role or access level (i.e. showing the pages, buttons and menu items of the Admin module only to admin users -note however that this does not prevent someone to reverse engineer your app and execute the queries dedicated to admin users: this is why it is key to correctly implement the Authentication and Security Rules parts-). See this section in the Custom Claims doc.
Should I build an Admin Panel in another app and using Cloud
Functions?
If you don't want to over-complexify your app with some logic to hide/show the Admin panel elements (based on Custom Claims, see above) you can very well build the Admin Panel in another app.
If you have specific needs/access restrictions that cannot be implemented through standard Security Rules you could very well use some Cloud Functions to check the user is an admin and to execute the writing/editing/removing admin actions (note however that while it is quite easy to interact with Firestore from a Cloud Function, it can be a bit more tricky with Storage: using the Cloud Storage Client SDKs is much easier than interacting with Cloud Storage through Cloud Functions).
You would preferably use Callable Cloud Functions, since "with callables, Firebase Authentication and FCM tokens, when available, are automatically included in requests". (See https://firebase.google.com/docs/functions/callable).
Side Note: You may be interested by this article, which details how to to create an Admin module for managing users access and roles. (Disclaimer: I'm the author).
the idea of ​​creating an admin panel for any flutter app
The idea is for two applications with different names and they will be linked to each other with Firebase
for more details see the video from the link
https://youtu.be/d7qoff-I8BU

firebase firestore audit log in functions

I have a couple of http functions in my firebase project, because I prefer to hydrate, validate and update the data on the backend. I would like to use the automatic stackdriver logs, but I need to associate the log events with the authenticated user (the requests are authenticated). Is there any way to add some context to database updates? Or commit the changes in the name of the user (not the service account)?
Firestore triggers don't currently associate any information about the end user (not even if you're using Firebase Authentication and security rules), so you will have to write user information into each document in order to track who performed an action on it. It will not just appear in the environment or context.
If you go this route, I strongly suggest adding security rules that require the user to provide their Firebase Auth UID correctly in a document field, so you can be 100% sure it's correct.
Read this for more details: https://medium.com/firebase-developers/patterns-for-security-with-firebase-per-user-permissions-for-cloud-firestore-be67ee8edc4a

Resources