0
votes

I have the following rules for my Firebase Realtime Database:

{
  "rules": {
    "users": {
      "$user_id": {
        ".read": "auth !== null && auth.uid == $user_id",
        ".write": "auth !== null && auth.uid == $user_id",
        ".validate": "data.hasChildren(['calculations', 'daily', 'firstName', 'lastName', 'monthly', 'profileImageUrl', 'uid', 'username', 'weekly'])",
        "calculations": {
          ".validate": "data.isNumber()",
        },
        "daily": {
          ".validate": "data.isNumber()",
        },
        "firstName": {
          ".validate": "data.isString()",
        },
        "lastName": {
          ".validate": "data.isString()",
        },
        "monthly": {
          ".validate": "data.isNumber()",
        },
        "profileImageUrl": {
          ".validate": "data.isString()",
        },
        "uid": {
          ".validate": "data.isString()",
        },
        "username": {
          ".validate": "data.isString()",
        },
        "weekly": {
          ".validate": "data.isNumber()",
        },
        "$other": {
          ".validate": false
        }
      }
    }
  }
}

Old Firebase Realtime Database Rules that worked for new users:

{
  "rules": {
    ".read": "auth.uid == $user_id",
    ".write": "auth.uid == $user_id"
  }
}

Firebase Authentication with email and password:

// Firebase Authentication to create a user with email and password
auth.createUserWithEmailAndPassword(email, password)
    .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
            val user = auth.currentUser
            // Send an email verification to the user. Only allow them to login after they
            // have verified their email.
            user!!.sendEmailVerification().addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    // Save username, first name, and last name on Firebase Database
                    val uid = auth.uid ?: ""
                    val ref = database.getReference("/users/$uid")
                    val newUser = User(uid, userName, firstName, lastName, 0.0, 0.0, 0.0, 3, "")
                    ref.setValue(newUser)

                    Toast.makeText(
                        this,
                        "Please check your inbox and verify your email address.",
                    Toast.LENGTH_LONG).show()

                    val intent = Intent(this, LoginActivity::class.java)
                    intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_NEW_TASK)
                    startActivity(intent)
                }
            }
        }
    }
    // If sign in fails, display a message to the user.
    .addOnFailureListener {
        Toast.makeText(
            baseContext, "Failed to create an account: ${it.message}",
            Toast.LENGTH_LONG
        ).show()
    }

User class:

@IgnoreExtraProperties
data class User(
    val uid: String,
    val username: String,
    var firstName: String,
    var lastName: String,
    val daily: Double,
    val weekly: Double,
    val monthly: Double,
    val calculations: Int,
    val profileImageUrl: String
)

These rules work for existing users. But if I try to create a new user, these rules don't work. I can't create new data in my database for new users that have signed up. How do I fix this?

1
It's impossible to say how to change the rules without seeing the code that exercises them. Please edit your question to include the minimal code that now gets rejected by these rules, that you want to be accepted.Frank van Puffelen
@FrankvanPuffelen I have included the minimum code used that I want the rules to accept.Tom Darious
If you log the value of uid right after you get it with val uid = auth.uid ?: "" what is the output? Also: this is one of those cases where using a ?: operator is actually hurting you. Your code assumed uid is not empty, so you should not convert null to "" there but instead fail if uid is still null.Frank van Puffelen
@FrankvanPuffelen The output of uid is the new user's ID. uid is not empty and this code worked before the changes to my database rules. I changed the rules to enhance security but if I revert back to my less secure rules, the rules don't work for new users who just signed up.Tom Darious
OK. So based on those previous rules the problem is not in the authentication but in the validations you added. I recommend disabling the validation rules, and then one by one reenabling them and testing again until you've found the one that fails. At worst that should lead to a smaller problem for us to look at, but you might find the cause of the problem on your own while isolating it.Frank van Puffelen

1 Answers

1
votes

My guess is that you need to validate that the new data is the correct type. So:

"calculations": {
  ".validate": "newData.isNumber()",
},

If you check the Firebase documentation on validating data you'll see that they also all check newData instead of data (which refers the to data before the write operation).