I got my app working with read and write with wide-open permissions and now I'm locking it down. My app won't read or write though. I get permission denied errors despite the Firebase rules simulator saying that my rules are ok for a Facebook authenticated user whose UID I got from a successful firebase signInWithProvider. What am I missing?
{
"rules": {
"items": {
"$uid": {
// user must match the authenticated user
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
}
}
}
This is my data structure:
my-firebase-app
-items: {
-<uid123> : [
{label:'apple'},
{label:'banana'}
]
-<uid456> : [
{label:'pear'},
{label:'cherry'}
]
}
I sign into firebase after facebook auth by doing, firestack.auth.signInWithProvider(provider, facebookAccessToken, ''), which gives me my user object including uid
I push to /items/uid123 and get an item id, 333
I set the item {label:'apple'} for the new ID (333) at /items/uid123/333
I subscribe to the collection of items at /items/uid123 by doing this with the web sdk: itemsRef.child(uid).on('value', (snapshot) => .....
My set call looks like,
const newPostRef = itemsRef.child(uid).push();
newPostRef.set(itemWithID)
set promise gets rejected with permission denied error.
All the things above work fine if my .read and .write are simply set to true which leads me to think my syntax or structure is just off in the rules def. Would love some input.
There is nothing wrong with the security rules, and in theory, both the set and the query operations look fine.
In practice however, you are making a grave mistake. As you revealed in the comments, you are trying to use both the Web SDK and the react-native-firestack library at the same time!
The authentication state is not shared between the two, thus the on('value') query is completely unauthenticated. If you added the third parameter to on (the cancel callback), you would see the permission denied error.
You must eliminate the web SDK completely, and use the realtime database via firestack too.
Related
I have the following Firebase realtime database rules:
{
"rules": {
".read" : "auth != null",
".write": false,
"Games": {
".indexOn" : "WeekId"
},
"Version": {
".read" : true
}
...
}
I keep getting the following email from Google:
[Firebase] Your Realtime Database 'xxx-9e542' has insecure rules`
We've detected the following issue(s) with your security rules:
any logged-in user can read your entire database
Without strong security rules, anyone who has the address of your database can read / write to it, leaving your data vulnerable to attackers stealing, modifying, or deleting data as well as creating costly operations.
So based on my rules above, I KNOW, that I have rules in place to allow logged in users to read the Games node and that ALL users can read the Version node even non authenticated users.
As far as I know, it needs to be this way because I require ALL logged in users to be able to access the Games node information, else how would they be able to view the list of games they can select from!?
As for the Version node, I use that in the instance I need everyone that downloaded my app to "Force Upgrade" my app cause of a change that is required. In this instance, I would need the user that have downloaded an older version of my app and that are either "logged in" or "not logged in" and force them to update the app or else they can not use it.
Can anyone let me know if I am off base with how I structured my security rules or is this "normal" and that I am receiving the email just as a FYI!?
How have others setup their rules!? or what are the "best practices" for setting up security rules!? esp. if you need logged in users to access the information of any particular node(s) 24/7!?
The main problem with your current rules is that anyone can sign in through the API and read your entire database, even if they know nothing about your application. They can just read the root of the database, and then start looking at your data.
The first step to improve security would be to not allow read on the top-level, but only at lower levels:
{
"rules": {
".write": false,
"Games": {
".read" : "auth != null",
".indexOn" : "WeekId"
},
"Version": {
".read" : true
}
...
}
Now nobody can read from the root, and you must know that Games or Version node exists in order to read it.
I am using Relatime database from firebase to read few flags and do some actions in android app. I used to get mail of insecure database read and write rules so I changed to following:
{
"rules": {
".read": "true",
".write": "false"
}
}
And now, I only get mail about insecure read.
[Firebase] Your Realtime Database 'abc-xyz' has insecure rules
We've detected the following issue(s) with your security rules:
any user can read your entire database
But if I change read to false then I am unable to read any value changes in real time. Can someone please help me understand how do I secure both read and write but also able to keep reading values from app?
PS: I don't use Firebase auth in my app as of now.
Firebase Auth is a cool thing, if you don't want your user to log into the authorization provider account, you can use an anonymous account which gives you a unique user ID of your app, etc.
Then you can write rules like:
"rules": {
".read": "auth != null",
".write": "auth != null"
}
If you don't store user data, you probably don't need any authorization. You can still restrict reading and writing of users by adding some area when read / write is available.
E.g:
"rules": {
"PublicData":{
"SomePublicChild":{
"ChildProperty1": { ".validate":true },
"ChildProperty2": { ".validate":true },
"$other": {".validate":false },
},
".write":true,
".read":true,
".validate":"newData.hasChild(SomePublicChild)"
},
"PrivateData":{
".write":false,
".read":false,
}}
These rules will allow anyone to write / read to the PublicData node and to anyone else to write / read the PrivateData node. The rules will also protect the structure of your public data, they only allow writing to the PublicData object with the ChildProperty1 or ChildProperty2 properties, and will block writes with any other property key.
It's not big thing but you won't recive more mail about insecure rules.
Environment: Firebase Unity 6.6.0, Unity Editor 2019.2.8f1
I am seeing the error "This client does not have permission to perform this operation." for new Anonymous users who authenticated moments before. If I wait 5-30 seconds, and connect again with the same user, the issue does not appear.
My relevant Real Time Database security rules
{
"rules": {
"gameSettings":{
".read": true,
".write": false
},
"userCompletedMissions": {
".write": false,
"$uid": {
".read": "$uid === auth.uid",
}
},
"userRanks": {
".write": false,
".read": "auth != null",
"english": {
".indexOn": ["rankOrderKey", "dRankOrderKey", "wRankOrderKey", "mRankOrderKey"],
},
"spanish": {
".indexOn": ["rankOrderKey", "dRankOrderKey", "wRankOrderKey", "mRankOrderKey"],
}
}
}
}
My firebase project is a mobile game. First time players create an Anonymous FirebaseUser account with await auth.SignInAnonymouslyAsync();, and immediately listen for data. Anything that requires ".read": true works flawlessly, but anything with ".read": "auth != null" and ".read": "$uid === auth.uid" trips my Security Rules and throws the error "This client does not have permission to perform this operation."
For example: FirebaseDatabase.DefaultInstance.GetReference("userRanks/english/SOME_USER_ID").ValueChanged += SomeFunction(...);
It's clear that the User is authenticated but Firebase's Security Checker doesn't know yet. Is there a way for me to wait until I am actually Authenticated?
Edit: additional information
What I did find interesting was the logs from following these repro steps:
1. My Unity Game had an authenticated Firebase User "Semadikp6FheE1FZMqi7MnCy0Iv2" in the Unity Editor on my Mac OS Desktop
2. In the Firebase Console, I navigated to the Authentication Tab, found the user "Semadikp6FheE1FZMqi7MnCy0Iv2", and deleted the account.
3. The next time I started my Unity Editor, all of the "This client does not have permission to perform this operation" errors referenced the deleted user "Semadikp6FheE1FZMqi7MnCy0Iv2"
4. My Unity game tolerates runtime errors by reloading the game scene (Unity Editor Play Mode does not stop). When the scene reloaded, it initialized everything from scratch again including Firebase API calls. The Firebase User it referenced on this attempt was a new Firebase User "UTc2NqwgYPTUFnnDCGSTLQm0yv63". Everything worked smoothly.
These seems like a bug related to caching, either on the Unity Firebase client, or Firebase service.
Further explanation:
I was repeatedly deleting the Anonymous account connected to my Unity Editor device in an attempt to reproduce the bug preventing my team mate from running the game. Whenever he runs the game in Unity Editor, he gets hit with "This client does not have permission to perform this operation" errors that do not clear up on subsequent attempts. He has joined this project a couple weeks ago, and the project is 11 months old. It's very likely an innocent issue like a "file is missing" from the GIT checkout. Unfortunately, I haven't found any obvious mistakes on my part, and the problem remains unsolved. Please advise.
It doesn't sound like this is an issue based on the question, but the easiest suggestion I have is to make sure you have anonymous authentication enabled:
Next, you can check to see if the user is currently authenticated by checking:
FirebaseAuth.DefaultInstance.CurrentUser != null;
If you wanted, you could also check IsAnonymous on CurrentUser. I like to check this in a handler to the StateChanged listener for a global "the user has signed in" signal.
With all that said, since you're using await, you can always say:
FirebaseUser user = FirebaseAuth.DefaultInstance.SignInAnonymouslyAsync();
if (user != null) {}
or in a coroutine (my favorite when working with Unity):
Task<FirebaseUser> signInTask = FirebaseAuth.DefaultInstance.SignInAnonymouslyAsync();
yield return new WaitUntil(() => signInTask.IsComplete);
FirebaseUser user = signInTask.Result;
if (user != null) {}
Finally, I noticed that you're using Firebase 6.4.0 rather than 6.6.0 (the current latest). At some point there was an issue with FirebaseAuth.DefaultInstance going out of scope if you didn't hold onto it (that is, if the GC decided to run through after you let go of any reference to FirebaseAuth.DefaultInstance), calls to other Firebase products wouldn't have authentication information.
I would recommend updating to the latest SDK if you can, but also try to hold onto FirebaseAuth in a script marked as DontDestroyOnLoad. Kind of like this:
public class AuthHolder: MonoBehaviour {
public FirebaseAuth Auth {get; private set;}
void Start() {
Auth = FirebaseAuth.DefaultInstance;
DontDestroyOnLoad(gameObject); // should be at the root of a scene
}
void OnDestroy() {
Auth = null; // make sure it can get cleaned up when you exit the game
}
}
If this fixes your issue on the latest Firebase Unity SDK, open up an issue on the GitHub Issues page.
If you haven't seen it yet, I do cover Authentication and Unity in some detail in this video. Although it focuses on Email/Password rather than Anonymous authentication.
I hope that helps!
--Patrick
I'm developing a proof-of-concept application, and thus my security requirements are low. I therefore changed the access rules to public access:
{
"rules": {
".read": true,
".write": true
}
}
This works as expected. The problem is that after a few minutes, the rules change back to default values (auth required) and I get an access error "Error: permission_denied at /cases: Client doesn't have permission to access the desired data."
Why on earth is this so?
Thanks.
You must edit database.rules.json in your IDE then save it then deploy it.
I have this DB structure in Firebase:
[bets]
-KTd9VKWJwHd_L6j_oAF
date
team1
etc
-KTdCc7uVmueNtcYzU1m
date
team1
etc
[users]
I'm now trying to write Firebase rules to allow users to only read bets assigned to them. But I ran into problems right away. I have these very basic rules:
{
"rules": {
"bets": {
"$bet": {
".read": true,
".write": true
}
}
}
}
When I try to visit /bets (where I display a list of bets) I get thrown the error:
Error: permission_denied at /bets: Client doesn't have permission to access the desired data.
If I instead place the .read and .write under bets instead of $bet, it works fine. What am I missing?
Firebase evaluates read permission at the location where you attach a listener. You attach a listener to /bets, so it rejects the listener since you don't have read permission on /bets.
If you have read permission to /bets, you also can read everything under it. So this means that you can't use Firebase Database security rules to filter data.
See the section rules are not filters in the Firebase documentation. Or search for Firebase questions mentioning "rules are not filters" here and you'll see this is a common pitfall for developers.