I am having trouble in writing firebase permissions. I want those users if authenticated only write to users section and Everyone else should be able to read or write to any section of the database. Is there any way that I can define rules for every table default to true and restrict only user section to be authenticated or I have to explicitly write rules for every table.
PS. It would be great if someone could guide me what rules should I implement for an app with features for sending and receiving a message with the following structure:
-Chat
-Friends
-Users
-message_notifications
-messages
-notifications
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
"Users":{
"$uid":{
".read": true,
".write": "auth.uid == $uid"
}
}
}
}
Is there any way that I can define rules for every table default to true and restrict only user section to be authenticated?
Once a user has access to data at a certain level in your database, they have access to all data under that level. You cannot revoke this permission on a lower level. So there's no way to give a user access to all data at the root, and then exclude one node.
What you can do is use the $ wildcard rules to create two types of top-level nodes:
{
"rules": {
"Users":{
"$uid":{
".read": true,
".write": "auth.uid == $uid"
}
},
"$others": {
".read": true,
".write": true
}
}
}
With the above rules, users can:
Only read the /Users/$uid node of a user if they know the UID of that user.
Can only write their own /Users/$uid node.
Can read and write all other data.
Related
I have a Firebase database that I want to only allow users who have access to that application to be able to read from and write to.
My data structure is like so:
{
"applications": {
"id_1": {
"feature": {
"a": true,
"b": false
},
"users": {
"user_id_1": true,
"user_id_2": true
}
}
}
}
As you can see, each application can have many users who have read/write access.
I only want users in the users object to be able to retrieve that application.
I have rules like so:
{
"rules": {
"applications": {
".read": "auth != null",
".write": "auth != null",
"$appId": {
".write": "data.child('users').child(auth.uid).val() === true",
".read": "data.child('users').child(auth.uid).val() === true"
}
}
}
}
".read": "auth != null", allows any user who is logged in to be able to retrieve all applications. I only want users user_id_1 or user_id_2 to be able to read that application.
In pseudo code, I would do something like this:
{
"rules": {
"applications": {
".read": "only users in `root.applications.$appId.users` can read", // I need to replace `$appId` some how
".write": "auth != null",
"$appId": {
".write": "data.child('users').child(auth.uid).val() === true",
".read": "data.child('users').child(auth.uid).val() === true"
}
}
}
}
How can I restrict it so when user user_id_1 fetches their applications, they only see apps they have access to?
You're hitting a few common problems here, so let's go through them one by one.
1. Security rules can't filter data
You're trying to control in your rule on /applications what application will be returned when a user tries to read that node. Unfortunately that is not possible, because security rules grant all-or-nothing access.
So either the user has access to /applications (and all data under it), or they don't have access to it. You can't set a rule on /applications to grant them access to some child nodes.
In the documentation, this is referred to as rules are not filters and the fact that permission cascades.
2. Avoid nesting data
You're trying to grant access to /applications, but then store two types of data under there for each application. In cases like that, it is usually better to store each type of data as its own top-level list.
So in your case, that'd be:
{
"application_features": {
"id_1": {
"a": true,
"b": false
},
},
"application_users": {
"id_1": {
"user_id_1": true,
"user_id_2": true
}
}
}
This allows you to grant separate access permissions for the application users and its features. While it means you'll have to read from both branches to get all information of each user, the performance difference there is negligible as Firebase pipelines those requests over a single socket
For controlling access and the most scalable data structure, Firebase recommends that you avoid nesting data and flatten your data structure.
3. Model the data in your database to reflect the screens of your app
Since granting anyone access on /applications gives them access to all data under that, you'll likely need another place to store the list of applications for each user.
I usually make this list explicit in my databases, as another top-level list:
{
...
"user_applications": {
"user_id_1": {
"id_1": true
},
"user_id_2": {
"id_1": true
}
}
}
So now when you want to show the list of applications for the current user, you load the IDs from /user_applications/$uid and then look up the additional information for each app with extra calls (which in turn can be pipelined again).
This one is not in the documentation, but a common pattern with NoSQL databases. I recommend checking out my answers to Many to Many relationship in Firebase and Firebase query if child of child contains a value.
The user needs to be able to load a list of the other users, which is why I have set the "read" to true. They should only be able to write within their own folder.
My question is, does this expose all of the users information to eachother? If a user has sensitive in one of their folders information, can this be stolen?
{
"rules": {
"users": {
".read": true,
"$userId": {
".write": "$userId === auth.uid"
}
}
}
}
As you said, the ".read": true will give every user access to all the users data, since this rule is saying that we can read everything from the users node in your database.
Your rule only allows the current logged in user to write on its own node, but others users can't.
I have implemented Firebase authentication with Gmail, Facebook,Twitter.
I have successfully logged in firebase, Then i have changed Database Authentication rules to below.
{
"rules": {
".read": false,
".write": false
}
}
In firebase console it is showing login users list but while retrieve data it is showing permission denied. Could you please help me to resolve this issue?
if i change above settings to true i can able to read and write data.
Setting both to false will deny read/write access to all users. You can use the auth syntax in your security rules to identify whether or not a user is logged in.
For example, to allow read/write access for logged in users only your rules would look like -
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
As the name (Database Authentication rules) suggests, these rules are for your Firebase-Database, that who should be allowed to read and write the data on your database and who should not be.
So, in your case, what have you done is- You've set both read and write to false. This means no one will be able to either read or write to your database. And that's what is happening (You're not able to write to the database).
But, when you set both of them to true, then everyone will be able to read and write.
So, if you want everyone to be able to read and write to your database, then your rules will look like this:
{ "rules": {
".read": true,
".write": true"
}}
If you want nobody to be able to read and write to your database, like temporary disabling your website or app:
{ "rules": {
".read": false,
".write": false"
}}
If you want everyone to be able to read your database but want only authenticated members to be able to write to your database like they've purchased the premium account and now they can edit the site, then the rules would be:
{ "rules": {
".read": true,
".write": "auth != null"
}}
If you want only authenticated members to be able to read and write to your database, then the rules would be:
{ "rules": {
".read": "auth != null",
".write": "auth != null"
}}
And if you want to explore more about how this stuff works. Then here is the link to official documentation: Understanding Firebase Database Rules
I want users of my app to have access to see profile of other users that is accessed via real-time database. I'm referencing to db via
ref.once('users/'+uid, snapshot => snapshot.child('users/'+uid).val())
Rules i have:
{
"rules": {
"users": {
".read": true,
"$uid": {
".write": "$uid === auth.uid"
},
},
}
I don't get why i can't access users/:uid even though simulator gives success message by ticking read and running simulation on <firebaseURL>/users.
If i set ".read": true" under rules it does allow me to read the data, but that may bait me later on if i would want to implement stuff that i don't want to be available to unauthorized users.
Edit (solution):
The problem was with referencing to firebase. Instead of firebase.database.ref('users') I was referencing too root itself by firebase.database.ref().
That reference caused to apply default read/write rules.
You could give ".read": "auth != null" to allow all authorised users to read data under users object and prevent unauthorised users from reading it.
I have a question for my own sanity. Below is one portion of my firebase rules
{
"rules": {
".read": "auth != null",
".write" : false,
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
"tokens": {
".write": "(newData.val() < data.val())"
}
}
},
...
If I understand correctly the rules state that:
ALL users must be auth'ed in order to read ANY node
ALL user can NOT write to any nodes
Specific to the User node:
In order to read from your own data, you need to be auth'ed and you can only read your own node
In order to write to your own user data, you must be auth'ed and you can only write to your own node
The user/token node can only be decremented and never increased by any user
Can someone confirm my assumptions/understandings reading Firebase security rules documentation.
Also does anyone have any good articles or helpful tips on using the simulator!?
An important concept with the security rules is that read/write rules "cascade" down the tree. This is discussed briefly in the documentation. That means that as you read your rules from top to bottom, the first rule that grants access takes precedence over any rules specified below it on children of that location.
Addressing each of your items:
ALL users must be auth'ed in order to read ANY node (YES)
ALL user can NOT write to any nodes (non-auth'ed users can NOT write to any nodes)
Specific to the User node:
In order to read from your own data, you need to be auth'ed and you can only read your own node (YES)
In order to write to your own user data, you must be auth'ed and you can only write to your own node (YES)
The user/token node can only be decremented and never increased by any user (see below)
In your current rules, the check for smaller token is not effective because the prior rule granting write access to an auth'ed user overrides it. You also need to address the case where there is no existing token value. My suggestion for fixing that is to use a .validate rule. The documentation recommends:
Used once a .write rule has granted access, to ensure that the data
being written conforms to a specific schema.
{
"rules": {
".read": "auth != null",
".write": false,
"users": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
"tokens": {
".validate": "!data.exists() || (newData.val() < data.val())"
}
}
}
}
}
As for the Simulator, I don't know of any user guide, but have managed to learn how to use it by experimentation. It's a very effective tool for understand the rules.
Here are a few cases of using the Simulator:
When you open the Simulator, Authenticated is off, which simulates a non-authenticated user. To simulate a read, click on the read button, enter a location: e.g. /users/xyz/tokens, and click on Run. You will see a red X on the lines of the rules that forbid that operation. To simulate an authenticated read, click on the Authenticated button and, for convenience, enter a simple user UID, like "Frank". Now enter location /users/Frank/tokens, click on Run and observe that the read succeeds.
You can do similar tests for writing, entering a location, auth settings and value.