how do I implement role based access control in firebase - firebase

This is my first foray into Firebase & nosql, I come from a SQL background.
Using Simple Login Security Email/Password, how do I limit access to data in Firebase? For example, some user will have access to create a business object (users, customers, categories, etc), others won't. Is there a way to attach a list of permissions to the "auth" variable?

There isn't a way to attach permissions directly to the auth variable (or at least that doesn't seem to be an intended strategy). I'd recommend creating a collection of users organized by auth.uid and you can keep whatever kind of permission attributes you want in there, such that your security rules might something look like this (untested):
{
"rules": {
".read": true,
"users": {
".write": "root.child('users').child(auth.uid).child('role').val() == 'admin'"
}
}
}
Where role is an attribute belonging to all objects in your users collection.
UPDATE
See comment below:
"There isn't a way to attach permissions directly to the auth variable" This changed in 2017. You can now attach custom claims to an auth profile, which are available in security rules. See bojeil's answer and the Firebase documentation for custom claims. – Frank van Puffelen

Firebase launched support for role based access on any user via custom user claims on the ID token: https://firebase.google.com/docs/auth/admin/custom-claims
You would define the admin access rule:
{
"rules": {
"adminContent": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
}
}
Set the user role with the Firebase Admin SDK:
// Set admin privilege on the user corresponding to uid.
admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
This will propagate to the corresponding user's ID token claims.
You can force token refresh immediately after: user.getIdToken(true)
To parse it from the token on the client, you need to base64 decode the ID token's payload: https://firebase.google.com/docs/auth/admin/custom-claims#access_custom_claims_on_the_client
You can upgrade/downgrade users as needed. They also provided a programmatic way to list all users if you have recurring scripts to change a users' access levels: https://firebase.google.com/docs/auth/admin/manage-users#list_all_users

Looking at this again a year later "Custom Tokens" may be a better option.
https://www.firebase.com/docs/security/guide/user-security.html#section-custom

Related

Login as an admin to a firebase Realtime database through postman

I have these rules set for a firebase realtime database
{
"rules": {
"adminContent": {
".read": true,
".write": "auth.token.admin === true",
}
}
}
I have created a user and can sign in
following this URL
https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[API_KEY]
{
"email":"MY EMAIL",
"password":"MYPASSWORD",
"returnSecureToken":true
}
I get the idToken and all the data.
I input in Params: auth | "THE ID TOKEN RECEIVED"
I am still getting
{
"error": "Permission denied"
}
I am guessing I am supposed to be registered as an admin or get some data as an admin.
I actually need this for a react project but I was testing things in Postman and I don't understand how this thing is working.
I have to keep these rules.
Thank you for your help.
Your security rule requires the user to be an admin:
auth.token.admin === true
But signing the user in does not automatically make then an admin, as that would be a pretty big security risk.
You'll need to run code in a trusted environment that marks the user as an admin. This admin value is known as a custom claim, so I recommend reading the documentation on custom claims and specifically this section on setting custom claims through the Admin SDK.
After doing that, the next time you sign the user in, they'll have that admin claim and it will be passed along to the security rules.

What if someone stole your GoogleInfo.plist, and creates a Firebase User all the Time? [Firestore, Firebase Auth, Security rules]

i have a serious Question. I am developing the Security Rules for my Firestore Database. So what if someone decompiled my App, stole the GoogleInfo.plist, added this file to his Project, and creates multiple Accounts with it? I mean in the security rules you have to:
allow create: if request.auth != null;
So he could add a new Document every time he adds an FirebaseUser Account.
How to solve and secure this?
Are there other options like sign in with custom field at example:
I create a document ID.
and so we check in the Firestore rules:
match /document/{myDOC}
allow write: if request.auth.code == myDOC;
So what I mean here is, if I can set additional Information to the Request of my App, and check if the additional Information is Equal to the myDOC;
Thanks!!
This is all working by design. There is no "security" information in GoogleInfo.plist. It just contains data that instructs the Firebase SDK on how to find your project and its resources. Without that data, your app would know nothing about your project.
To secure your database, you will need to design your database to allow for per-user security, then write rules that determine which authenticated users can read and write which documents, as suggested in the documentation.
It's not possible to send extra information along with a query for the purpose of security. You should depend on what Firebase auth provides in request.auth in the rules language.
See also: Is it safe to expose Firebase apiKey to the public?
https://firebase.google.com/docs/auth/admin/custom-claims
Firebase Admin SDK allows you to define custom attributes on user accounts.
admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
When you are writing your rules, it is possible to check these custom attributes.
{
"rules": {
"adminContent": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
}
}
But as it is stated in the docs, you should consider these points:
Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database or other server side storage.
Custom claims are limited in size. Passing a custom claims payload greater than 1000 bytes will throw an error.

How to set Firebase Database rules? How to prevent .write from deleted user

Introduction
I am building a firebase web client app. I would like set Firebase Database rules.
New user registered to a firebase app. Firebase gave him a user.UID.
Then, admin delete OR disabled the user from firebase admin console.
User refresh client app.
(I find out that) user can still write to firebase database even though his account has been deleted/disabled.
.
Goal / Intention
I would like to set a rule that prevent access (.read OR .write) to firebase database when user does not exist OR disabled in admin console/(auth/users).
Some thing like this:
"rules":{
"$uid":{
".write":"auth.isUserActive(auth.uid) == true"
}
}
.
FIREBASE REFERENCE DOC: https://firebase.google.com/docs/reference/security/database/#auth
Question
How can I achieve the above intention? What are the rules should I set to firebase DB?
Deleting a user doesn't revoke existing tokens for that user. See Firebase authentication not revoked when user deleted?. If you're using one of the standard identity providers, this means that the users may still be able to access the data for an hour after you delete the account.
There is no API for you code to check whether a given uid still exists. And even if such an API existed, it wouldn't help in this case, since a malicious user could just bypass that check and call the API directly.
A simple way to deal with this scenario is to keep a whitelist of allowed or blacklist of disallowed users in your database. For a blacklist, you'd keep a top-level (world readable, admin only writeable) list of banned/deleted users:
banned
uid12345: true
When your admins delete a user, they also add them to this list.
And then in your security rules, you check and disallow access for banned users. E.g.:
"posts": {
".read": "auth != null && !root.child('banned').child(auth.uid).exists()"
}
You can do it by User Based Security as per the doc -v2
var FirebaseTokenGenerator = require("firebase-token-generator.js");
var tokenGenerator = new FirebaseTokenGenerator(FIREBASE_SECRET);
var token = tokenGenerator.createToken({ "uid": "1", "hasEmergencyTowel": true });
For the above created token, the you could write the rules as follows:
{
"rules": {
"frood": {
".read": "auth.hasEmergencyTowel === false"
}
}
}
This could be called once the UID Scope id about to end.
For reference: User Based Security Doc -v2

Share data with friends on Firebase

I am currently developing an app that needs a server backend to sync data between devices and also friends. So while researching if I should make my own server (which I'd prefer not to) I stumpled upon Firebase.
What I haven´t figured out is how public data is. From how I understood it, is that whatever I send up could be seen by every user of my app. I saw that you can define rules like "The user needs to be logged in", but can I specify that a certain child should only be visible to the user who created it? And then, is there a way to invite an other user to collaborate on that child? So that now only the creator and the invited user can see that child?
My app would be running on iOS in the beginning, but I guess thats not relevant to my question, is it?
Yes you can do that, you can set security rules to allow only the user with a specific UID (firebase user id) to write or read data like this example...
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid"
}
}
also with custom authentication you can create your custom claims to grant different access levels (for example admin, editor..etc)
example...
"frood": {
// A towel is about the most massively useful thing an interstellar
// hitchhiker can have
".read": "auth.hasEmergencyTowel === true"
}
and for the invitation part.. you can also grant permission by using a child node as a reference like in this example...
".read": "data.child(auth.uid).exists()",
In the last case.. if the data path requested has a child key that is equal to the uid of the client trying to access it the information will be able to read the data,
all the detailed information is here:
https://firebase.google.com/docs/database/security/user-security#section-revisiting-advanced-example

Is there a way to restrict registrations in firebase

Is there a way to restrict users from registering firebase email/password accounts so that new users can't sign up? I have a small app that only a few admins need to have access to(which I've manually created in the Firebase admin) and the way it's setup right now it seems like anybody could inject a little javascript and register an account.
Firebase Simple Login is an abstraction built on top of Firebase Custom Login for convenience. When using the email / password authentication, it's worth keeping in mind that this is just creating a mapping between that email and password, and automatically generating authentication tokens for use in your security rules.
In your specific case, if all of the "admin" users have already been created, you can achieve the behavior you're looking for through security rules. For example, if you want to only allow read / write access to your Firebase data tree to authenticated users in that list, try writing top-level read / write security rules that require that user to be in the "admins" list:
{
".read" : "auth != null && root.child('admins').hasChild(auth.uid)",
".write" : "auth != null && root.child('admins').hasChild(auth.uid)"
}
Those rules above ensure that the user is authenticated (via auth != null) and require that the authenticated user's id is in the list of admins (root.child('admins').hasChild(auth.uid)).
The last step is to actually make those users admins, by writing them to the admins subtree. Let's say you have users 1, 4, and 7 to make admins, update your data tree to reflect the following:
{
...
"admins": {
"1": true,
"4": true,
"7": true
}
}
As a result of this, even if other users are able to generate new email / password mappings using Firebase Simple Login, those mappings will have no impact on your application as it is restricted using security rules.
#RobDiMarco provided a great answer, but it has a flaw.
The rule root.child('admins').hasChild(auth.uid) will pass, in case auth.uid will be an empty string.
You can test this in Firebase Database Security Simulator, clearing out uid field ({ "provider": "anonymous", "uid": ""}).
This rule root.child('admins').child(auth.uid).val() === true will not pass with an empty uid.
You can also turn off new user registration by adding a cloud function that deletes new users upon registration. See this answer:
How to prevent new user registration on Firebase?

Resources