0
votes

Here's the use case:

  1. Anyone can register to use the app. Authentication is handled by Firebase Auth.

  2. Registered users can only read and write their own details. Access to the database is managed by Firebase security rules. For example:

    {
      "rules": {      
             ".read" : "root.child('Admins').hasChild(auth.uid)",
             ".write" : "root.child('Admins').hasChild(auth.uid)",
      "Companies": {
           "$company" : {
               ".read" : "data.child('firebaseuid').val() === auth.uid", 
               ".write" : "data.child('firebaseuid').val() === auth.uid",
               "firebaseuid" : {
                                ".write" : ???? ,   
                                ".read" : ????,
               },
               "details" : {
                                ".read" :  "data.parent().child('firebaseuid').val() === auth.uid", 
                               ".write": "data.parent().child('firebaseuid').val() === auth.uid",  
               },
               },
       },
    }  
    

QUESTION: What are the appropriate security rules for "firebaseui"?

  1. Obviously, setting the read/write attributes true won't do. That's an open database.

    "firebaseuid" : {
      ".write" : true ,   
      ".read" : true,
    },
    
  2. Equally, :"auth !== null" doesn't work, because it would allow ANY registered user to read/write everybody's information.

    "firebaseuid" : {
      ".write" :"auth !== null" ,   
      ".read" :"auth !== null",
    },
    
  3. Setting the rule to allow only the registered user to read/write works...almost.

    "firebaseuid" : {
      ".read" :  "data.parent().child('firebaseuid').val() === auth.uid", 
      ".write": "data.parent().child('firebaseuid').val() === auth.uid",  
    

    }

Almost, because this suffers from the First Write Problem. That is, the user would not be able to initialize the firebase uid ("firebaseuid") at registration, because in that first instance, the firebaseuid field is null, causing the security rule to return false.

  1. One could add a firebase validator to the security rule, so that the user would be to write to "firebaseuid" when and only when the field is blank. This works well enough, if there is only one registered user. But suppose there are multiple admins per organization? In this case, this solution fails.

  2. One might want to write a Firebase Cloud function that is triggered upon registration, but the Firebase user class doesn't contain enough information. At registration the only info available is email an password.

  3. One could write a more complicated Cloud function triggered by a database create or http put, but every approach i can think of, has a security hole.

I realize this must be a very common problem, so there must be a simple answer. Can somebody please point me in the right direction?

1
Not sure I understand correctly. Is the "firebaseuid" property meant to have the UID of the user that created that company as its value?Frank van Puffelen
It might be easier to follow if you describe the use-case you want to have, instead of all the options that you considered that won't work. I.e. "My app allows a user to register a company. The user that registers a company, automatically becomes an admin for that company's data. An existing owner of a company can add a new owner to that company."Frank van Puffelen
Frank, Yes, the "firebaseuid" is meant to have the UID of the user. This enables this security rule: "data.parent().child('firebaseuid').val() === auth.uid" which is consistent with the recommendation made here: youtube.com/watch?v=PUBnlbjZFAI&t=1213s (time 18:41).RichardZ
Frank, Specific use case: There is a company in my firebase database with no registered admins. There is a table in the database with a read-only list of pre-approved admins. A pre-approved admin 1) uses Firebase auth to register, 2) the app looks up the email address in the pre-approved table, 3) if the email matches and the email is verified, the registration is completed. Question: what is the security rule for Company/$company/details? Thank you.RichardZ
@RichardZ pre-approved table and Company/$company/details are 2 different tables right?Angus Tay

1 Answers

0
votes

You'll typically want to allow the user to set firebaseuid to their own UID, and only if it currently doesn't have a value. So:

"firebaseuid" : {
  ".write" : "!data.exists() && newData.val() === auth.uid"
},