I have a node called profiles that have a list of id's.
I want to allow read access to the child nodes only and prevent reading all profiles.
This is what I have in rules, but it allow to read all profiles.
{
"rules": {
"profiles":{
".read": true,
".write": false
}
}
}
and this is what I have under profiles
{
"1" : {
"id" : "1",
"name" : "test1"
},
"2" : {
"id" : "1",
"name" : "test2"
}
}
Typically you'll store each user's profile under a key that has the value of their Firebase Authentication UID. So:
{
"profiles": {
"uidOfUser1": {
"id" : "1",
"name" : "test1"
}
"uidOfUser2": {
"id" : "2",
"name" : "test2"
}
}
}
In that case you can secure it with these rules:
{
"rules": {
"profiles": {
"$user_id": {
// grants read access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".read": "$user_id === auth.uid"
}
}
}
}
In the security rules the value of auth.uid is the UID of the user that is currently signed in to Firebase Authentication. There is no way to spoof this value, so it's a great way to secure data access. The above rules allow a user to read a specific profile when their auth.uid matches the key of the profile. So uidOfUser1 or uidOfUser2.
Also check out the Firebase documentation on securing user data, which describes it in more detail.
Related
I want to give .write permission only to those authenticated users with isAdmin:true
Below is the structure of Database.
In the clients(users) node uid is the Firebase generated auth uid.
{
"clients" : {
"-M8bGQaB....." : {
"isAdmin" : true,
"uid" : 5gvR1GzT...
}
},
"products" : {
"-M81GzT....." : {
"productName": "Product 1"
},
"-M81GzT....." : {
"productName": "Product 2"
}
}
With your current data structure there is no way to look up the user profile in the security rules, as security rules can't search across all users.
That's why the proper data structure to store user uses their UID as the key. In your case that'd look like:
"clients" : {
"5gvR1GzT..." : {
"isAdmin" : true
}
},
With this JSON, you can then only allow users to write a product when the isAdmin property for their UID is set to true with:
{
"rules": {
"products": {
"$productid": {
".write": "root.child('clients').child(auth.uid).child('isAdmin').val() == true"
}
}
}
}
I'm creating an application which lets users create items and then allow other users to subscribe to those items. I'm struggling to craft a rule that will prevent users from subscribing more than once to an item.
Here is an example of my data structure (anonymized, hence the "OMITTED" values):
{
"OMITTED" : {
"name" : "Second",
"body" : "this is another",
"userName" : "Some User",
"userId" : "OMITTED",
"created" : 1385602708464,
"subscribers" : {
"OMITTED" : {
"userName" : "Some User",
"userId" : "OMITTED"
}
}
}
}
Here are my Firebase rules at present:
{
"rules": {
".read": true,
".write": "auth != null",
"items": {
"$item": {
".write": "!data.exists()",
".validate": "newData.hasChildren(['name', 'body', 'userId', 'userName']) && newData.child('userId').val() == auth.id",
"subscribers": {
"$sub": {
".validate": "newData.hasChildren(['userId', 'userName']) && newData.child('userId').val() != data.child('userId').val()"
}
}
}
}
}
}
How can I prevent users from subscribing more than once? What is the rule I need to prevent duplicate users within the subscribers list based on userId?
Since security rules can't iterate a list of records to find the one containing a certain bit of data, the trick here is to store the records by an ID which allows for easy access. There is a great article on denormalization which offers some good insights into this practice.
In this case, if your use case allows, you may simply want to switch your data structure so that records are stored by the user's id, rather than storing the ID as a value in the record, like so:
/users/user_id/items/item_id/subscribers/user_id/
In fact, as you'll see in denormalization, you may even benefit from splitting things out even farther, depending on the exact size of your data and how you'll be reading it later:
/users/user_id
/items/user_id/item_id
/subscribers/item_id/user_id
In either of these formats, you can now prevent duplicates and lock down security rather nicely with something like this:
{
"users": {
"$user_id": { ".write": "auth.id === $user_id" }
},
"subscribers": {
"$subscriber_id": { ".write": "auth.id === $subscriber_id" }
}
}
How can I add new rule to Firebase Realtime Database so that users can read data where recid equal to user ID?
I have a massage table with this structure:
"messages" : {
"-KyyjeMOtc7fWAsOiuiP" : {
"recid" : "FL5hyQJrsHWRQsRtiLe1PxkyRnk1",
"senderid" : "6K6pQHaCishDlCb0Y9AaN3zI22n1",
"text" : "hi"
},
"-KyykczCNpsSL6a1t8vt" : {
"recid" : "FL5hyQJrsHWRQsRtiLe1PxkyRnk1",
"senderid" : "6K6pQHaCishDlCb0Y9AaN3zI22n1",
"text" : "test"
},
}
I want a rule that when data is added to the database, only the user whose uid is equal to recid can see the data.
To achieve this, you can create user-based security rules for your database, something similar to:
{
"rules": {
"messages": {
"$messageId": {
".read": "auth.uid == data.child('recid').val()",
".write": "auth !== null"
}
}
}
}
In this example, $messageId uses a $location variable that will match any key under your messages list. Then, we grant read access only if the current user's auth.uid matches the recid child value.
A super admin is an user with special uid which is able to access of everyone to edit their profile and publish new content.
How to make a super admin?
Consider using custom user attributes. It is more efficient and cheaper than using Real Time Database to lookup if a user is an Admin on every authenticated request:
https://firebase.google.com/docs/auth/admin/custom-claims
You would set the Admin role on the user upon creation:
admin.auth().setCustomUserClaims(uid, {admin: true})
You can propagate it to the client after ID token refresh.
currentUser.getIdToken(true)
Then you can simply enforce the rule:
{
"rules": {
"adminContent": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
}
}
If you don't use rules or Firebase RTDB, then enforce it on your backend by parsing it from the ID token via Firebase Admin SDK:
// Verify the ID token first.
admin.auth().verifyIdToken(idToken).then((claims) => {
if (claims.admin === true) {
// Allow access to requested admin resource.
}
});
Basically, it's all about data structure and the belonging security rules.
To get started, build a data structure where you have some kind of user roles in it.
For example like this:
{
"data" : {
"user1id" : {
"name" : "MisterX"
},
"user2id" : {
"name" : "John Doe"
}
},
"users" : {
"user1id" : {
"role" : "admin"
},
"user2id" : {
"role" : "member"
}
}
}
Each user has a property called role.
Now you can define your security rules and make use of the role property to define the right access rights:
"data" : {
"$userid" : {
".read" : true,
".write" : "root.child('users').child(auth.uid).child('role') === 'admin'",
}
}
In the case above just admins are able to write to the data/userid node. You can apply this to all the different nodes you want to.
I'm really scratching my head here, and I've been reading as much as I can on many other cases. I'm new to Firebase, but got some understanding of it. I was wanting to restrict certain records of the database to certain users. So here's my JSON:
"accounts" : {
"13asdf313" : {
"dog" : "bacon",
"email" : "luis#fakeemail.com",
"first" : "luis",
"last" : "xxxx"
},
"HlELrrGDbiMKgxxxx" : {
"name" : "Luis"
},
"anthony" : {
"email" : "anthony#fakeemail.com",
"last" : "xxxx",
"name" : "anthony"
},
"jpSq6UzX0mcAvExxxx" : {
"name" : "anthony"
}
}
Here are the rules set up based on what I've been reading:
{
"rules": {
"accounts":{
"$uid":{
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
},
}
}
In the simulator, I used the bottom condition (I even put /accounts in the location field). I used the UID: HlELrrGDbiMKgxxxx, copied straight from Firebase account list.
This is always coming up as failed
What am I doing wrong?
You have to insert accounts/HlELrrGDbiMKgxxxx into the Location field.
Otherwise you are trying to access the whole database (standard location is root, which covers the whole database). Your rule is just set for the child accounts/$uid and to access it the user id from authentication and the child location in the database must match.