Cant read all data with security rules - reactjs

I am trying to read all events node as user that have admin node,
and able to read only related event if not admin.
The issue that I cant get all events with this security rule.
"Event":{
"$uid": {
".read": "auth.uid == $uid || root.child('Users').child(auth.uid).child('admin').val() == 'admin'",
".write": true
}
where my requests looks like:
var starCountRef = firebase.database().ref("Event/"); //trying to read all events as admin
starCountRef.on("value", (snapshot) => {
const data = snapshot.val();

The problem we have here is that the RTDB rules work from top down. That means if one rule in the top denies the access it doesn't matter what the one down says. In your case giving access to the whole list to the admin would be no problem but then also to each owner of the event would be. Because those are probably no admins. And you can access the whole list only when you put the .read above the uid.
There is one way I could imagine to make it work. There are query-based rules. More about them here.
You could write your rules like this:
"Event":{
".read": "query.equalTo == auth.uid ||
root.child('Users').child(auth.uid).child('admin').val() == 'admin'"
}
You would then need to access the data with a query to get it:
db.ref("Event").orderByKey()
.equalTo(auth.currentUser.uid)
.on("value", cb)

Related

Firebase Authentication (multiple types of users)

i am working on a react.js project which authenticate from firebase but i have 3 multiple type of user's in it (super Admin,vendor admin,vendor staff) with different privileges. how can i authenticate them from firebase and get to know this is my venor admin or vendor staff etc ???? because firebase just authenticate single type of user!
You can control access via custom claims using the Firebase Admin SDK on your own server of through Cloud Functions for Firebase.
Set claims server side for a specific bid with a method like this:
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.
});
Then set up your database with a structure that separates admin and vendor admin content:
/
/admin
/data for admins here
/vendorAdmin
/ data for vendor admins here
/staff
// data for staff here, or perhaps this data is accessible to all since the admins may need access to it.
In the Firebase Console, customize the rules to restrict these locations to those who include the proper claim:
{
"rules": {
"admin": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
"vendorAdmin": {
".read": "auth.token.vendoradmin === true",
".write": "auth.token.vendoradmin === true",
}
"staff": {
".read": "auth.token.staff === true",
".write": "auth.token.staff === true",
}
}
}
This is a simplified example, so you'll have to customize it further to meet the needs of your app.
You can maintain a users table in your database, and every time you sign up a user just add them there as well, using the uid.

Firebase rules for a single authenticated user

I have created a small app which uses Firebase as its database for my portfolio. A lot of the website renders a different view whether an authenticated user is logged and I would like to have one account that has the ability to log in and would be authenticated but would not have permission to write. My current rules are as follows:
"rules": {
".read": true,
".write": "auth != null", }
I know this allows anyone to read the data (which I want) but I am wondering if there is a way to "trick" my react app into thinking a user, with an example uid of "Example123", is authenticated but if that user tries to write it fails?
I have tried something like:
"rules": {
".read": true,
".write": "auth != Example123", }
but firebase did not like that. Is it possible to do what I am wanting using firebase rules or would I need to figure out a different approach? Thanks in advance.
You could do as follow:
Create a node users in your database, under which you will store the specific user(s) with its (their) User UID, like:
- users
- Lt7kif73eL58dFCYcmeEfOzUl2n1 <- User UID
- restrictedRights: true
- name: xxxxxx
and write your rule as follow
".write": "auth != null && !root.child('users').hasChild(auth.uid)"
Of course you should add this rule to your database:
"users": {
".read": ....,
".write": false
}

Securing system-generated nodes in firebase

I've been going through the rules guide but haven't found an answer to this.
App users are able to submit "scores" of different types, which are then processed in JS and written to a "ranking" node. I have it set up so that every time a new score is submitted, the rankings are automatically recalculated and a new child is written if the user doesn't exist or updated if the user exists.
My question is how to secure this "ranking" node. Everyone should be able to read it, nobody except the system should be able to write it. This would prevent people from submitting their own rankings and aggregate scores.
EDIT
This is the operation:
Ref.child('rankings').child(uid).once('value', function (snapshot) {
if (snapshot.exists()) {
snapshot.ref().update(user); //user object created upstream
} else {
var payload = {};
payload[uid] = user;
snapshot.ref().parent().update(payload);
}
});
How would I add custom authentication to this call? Also, since I'm using AngularJS, is there any way to hide this custom token or would I have to route it through a backend server?
The key part of your problem definition is:
only the system should be able to write it.
This requires that you are able to recognize "the system" in your security rules. Since Firebase security is user-based, you'll have to make your "system" into a user. You can do this by either recording the uid from a regular user account or by minting a custom token for your "system".
Once you have that, the security for your ranking node becomes:
".read": true,
".write": "auth.uid == 'thesystem'"
In the above I assume you mint a custom token and specify thesystem as the uid.

Purposefully not returning child values when retrieving a Firebase object

I have a user object in my firebase db that contains some sensitive date (e.g., email).
For situations where I need to fetch user information (who are not the current user), how do I prevent returning this data? (For example, if a user could view other users' profile data.)
Options
Using Firebase security rules: problem with this option is that there are ton of errors thrown when trying to retrieve the user object (even though I only want to "hide" sensitive child values under that user object).
Restructure my data so that public and private data are in two different paths.
Option #2 seems viable to me, but I want to check before making a major architectural overhaul that there's not an easier solution?
With respect to security rules, Firebase operations are all-or-nothing.
As a result, attempting to load all of the data at /users/<uid> will fail because your client does not have permission to read all of the data at that location, though you do have permission to read some of the data there. Similarly, writing to a location behaves the same way, and full permission is required before your operation will continue.
To handle this use case, consider restructuring your data like this:
{
"rules": {
".read": false,
".write": false,
"users": {
"$uid": {
// Users can read / write all of their own data
".write": "auth != null && auth.uid == $uid"
"public": {
// public data goes here
".read": true // Make all of this data public
},
"private": {
// private data goes here
}
}
}
}
}

Implementing "read" and "write" security rules in Firebase

In my app I want to have a "can read"- and a "can write" view. When the app starts without existing parameters ("first time user"), then 2 random hashes are created, together with the firebase app secret they are POSTed to the PHP FirebaseTokenGenerator to receive my token.
Then I want to do this:
The first hash ("read hash") represents an "anonymous user" entry
The second hash ("write hash") represents a "key" which is a child entry of the user entry
Which looks like this:
<appname>
users
<read_hash>
key
<write_hash>
[other user related data]
...
The hashes can be used as URL parameters, e.g. "myapp.com/#read_hash/write_hash"
What I want to achieve is:
when the user only has his read_hash (calling just "myapp.com/#read_hash"), he should be able to SEE all his entries he entered the first time (having the app create the write_hash / "key" for him). But he is not allowed to modify them.
When he provides his write_hash (calling "myapp.com/#read_hash/write_hash") writing to Firebase is allowed.
My security rules:
{
"rules": {
"users": {
"$user": {
".read": "$user == auth.read_hash",
".write": "$user == auth.read_hash && root.child('users').child($user).child('key').val() == auth.write_hash"
}
}
}
}
My problem is: How do I store the write_hash the first time without my security rule ".write" preventing writing ??
Any other idea how to achieve this? Any architectural / inconsistencies?
I'm using these libs:
Backbone JS with backbone-firebase.js
FirebaseTokenGenerator PHP
Thanks in advance.
First, I should note that you should never put the Firebase Secret in your app. It should always be stored safely on a secure server. Your original question suggested you were sending the token down to a server from the client.
I would suggest having the read_hash be the same as a user id, and then storing a "key" at //key when the user is first created.
Then I'd suggest a rules structure as follows:
{
"rules": {
"$userid": {
".read": "$userid == auth.read_hash",
".write": "!data.exists() || ($userid == auth.read_hash && data.child("key") == auth.write_hash)",
}
}
}
Assuming you already have the write hash's key (because you've authenticated already according to your example), this will work:
{
"rules": {
"users": {
"$user": {
".read": "$user == auth.read_hash",
"$key": {
".write": "$user == auth.read_hash && $key === auth.write_hash"
}
}
}
}
}
If you need to create the hash before obtaining the key, for some strange use case (unlikely, but this will give you some key insights to security rules), you can do something like this:
".write": "$user == auth.read_hash && (!data.exists() || $key === auth.write_hash)"

Resources