0
votes

Good morning!

I am working on a Firebase project in my Unity game and am working on securing the data in the database, however whenever I update the rules, it breaks my (otherwise working) handlevaluechanged function.

The data structure looks like this:

User_List -> Firebase_User_ID -> Health: 100, Name: Foo, etc ....

I want the rules (at a minimum, I'll add validation later) for this data to be:

"USER_LIST":
{
    "$UID" :
    {
            ".read": "auth.uid === $UID",
            ".write" : "auth.uid === $UID",
    }
},

Locally in the game, I get a reference with this call:

FirebaseDatabase.DefaultInstance.GetReference("USER_LIST").Child(USER_ID).ValueChanged += HandleValueChanged;

If I set the rules to:

            ".read": true,
            ".write" : true

everything works as expected. The client can update the database, and handlevaluechanged does its job of keeping the local data synced. When I switch the to the rules above where I verify the auth ID, the client still works. It can update the database no issues, provided the correct user ID is signed in. However, my Handlevaluechanged gets a permission denied error from firebase, as if the handlevaluechanged listener does not supply the proper user ID when it attempts a read from the DB.

I'm puzzeled because the rules allow me to get the reference in the first place, and update the database from the client, but I can't update the client from the database? What am I missing?

I have also tried to GetReference at the USER_LIST node instead of the USER_ID node with the same result.

Any insight would be greatly appreciated! Thank you in advance.

1
Have you made sure the above attachment is made inside of an AuthStateChanged event handler? You'll need to ensure that you connect and disconnect the handler whenever the user state is updated. If it's like the other SDKs, the current user will be considered "pending" (pretty much treated as null) until the user's authentication state is confirmed. In this short window, the above rules would throw the permission error. - samthecodingman
@samthecodingman Thank you for the reply! Hadn't considered this. Theoretically, Once the user is signed it, it should stay that way, correct? The behavior I am seeing is once the user is logged in, get the reference and start the listener. I can upload and download data fine, but the listener is rejected by my rules. Are you suggesting that the user is being signed out without me knowing, or the listener is being activated before the user is successfully signed in? Thanks again! - Chris Whipple
The listener will be denied access whenever your client tries to download the data when it doesn't have permission - either because the current access token is expired, the user is signed out or the access token has been invalidated (e.g. user deleted). The user isn't necessarily signed out, but while Firebase Auth is initialising, the user's access token often needs to be validated and refreshed - while this process is happening your listener will fail. Once the negotiation is finished, the AuthStateChanged event handler will fire indicating that auth is ready to use. - samthecodingman
Think of it like JavaScript's window.onload = doSomething();, but for the Firebase authentication service. - samthecodingman
You can always test your rules in the database simulator (Click on the Rules tab next to Data). I play with it a little in this article for Cloud Storage, but the functionality is basically the same for Realtime Database: medium.com/firebase-developers/… . You can also assert that FirebaseAuth.DefaultInstance.CurrentUser != null if you want a quick fuzz test to see if that's your issue. - Patrick Martin

1 Answers

1
votes

After ignoring this issue for a while and working on other bugs, I discovered the true issue and wanted to post in case anyone else finds themselves in this pickle.

I had a Unity scene dedicated to signing in users that created the authenticated Firebase user, then jumped to a new scene. In that new scene, I searched for the local user object created by when the user logged in, and then I set up the database reference and the handleValueChanged listener.

I think the issue was that searching for the user was being done asynchronously (I have not done more research to confirm this, but my testing in this specific case seems to suggest this is the case), so the task was started, and while it was looking for the user credentials, it created the DB reference and added a listener with a null user. Afterwards, the task completes and the user is no longer null, but the listener is already listening.

Then, if I did a database read or write, the call for read or write was from an authenticated user and would execute provided the RTB rules were satisfied. Successful writes would trigger the handlevaluechanged listener and draw up a permission denied error due to the listener not having an authenticated user ID associated with it (again, I reached this conclusion based on observation and testing with this specific project, not research).

Simply adding the DB reference to my local user object after it was created instead of searching for the user and then establishing the reference solved all of my issues.

I hope this helps!