6
votes

I added Firebase to my website and created a signup form. When the user clicks the "signup" button it creates a user using "Firebase Authentication", then it updates the user's displayName, and finally adds some data about the user to the database:

firebase.auth().createUserWithEmailAndPassword(email, password).then(function () {

    profile = firebase.auth().currentUser;

    profile.updateProfile({
       displayName: "name-value"
    }).then(function() {

        // Update successful.

        firebase.database().ref("/users/" + profile.uid).set({
            data:{
                country: "country-value",
                city: "city-value"
            }
        });
    });
}).catch(function(error) {
    // Handle Errors here.
});

Everything worked fine until I changed the database rules from:

{
    "rules": {
        ".read": true,
        ".write": true
        }
    }
}

To:

{
    "rules": {
        "users": {
            ".read": true,
            "$user": {
                ".write": "auth.token.name !== null"
            }
        }
    }
}

I used auth.token.name as it is written in the guide to check whether or not the user's displayName exists (I planned to make it check some more things about it later). Somehow "firebase rules" 'thinks' that auth.token.name equals null, even though I set the displayName before. Why is it happening and what can I do to make it work?

Thanks in advance :)

2
Are you trying using the same user previously created with a name or are you trying to signup a new user?cutiko
The purpose of the page that I've created is signing up a new user, changing the displayName, and finally uploading some data about him to the database.Programmer

2 Answers

3
votes

First, Firestore has replaced Realtime Database and should be used for all projects going forward - more features.

Re Realtime DB rules, it's is best practice to check uid variable vs display name. See Variables:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && newData.child('user_id').val() == auth.uid"
    }
  }
}

Finally, IIF you implement database security rules using Firebase Authentication, you MUST use .onAuthStateChanged to drive your application as your auth() variables WILL BE null for a second on initial page/app load as this is an asynchronous method. So, don't try and write or read anything on DOM ready. You must trigger db conversations within .onAuthStateChanged like below:

firebase.auth().onAuthStateChanged(function(user) {
    if (user) {
        //USER SIGNED IN. WE ARE READY
    }
    else {
        //USER IS NOT SIGNED IN
    }
});
0
votes

I had the same problem and figured out a better solution. Firebase has a feature called custom claims that is meant for the use case we have. You want to set some id on a Firebase Anonymous Auth User that is accessible from database security rules.

The high-level of the solution is to make a Cloud Function that uses the admin api to set a custom claim. You can't set custom claims from the client side.

Next, call the cloud function to update the custom claim then use Auth.auth().currentUser?.getIDTokenResult(forcingRefresh: true,... in order to retrieve the updated token which includes the custom claim. This step is critical because you need your custom claim to be sent as part of every request moving forward.

If anyone actually reads this I can type up a more detailed implementation example.