How to create public/private user profile with Firebase security rules?

{
  "rules": {
       "users": {
            "$uid":{ 
                 //Private whatever under "uid" but Public is exposed
                 ".read": "auth != null && auth.uid == $uid",
                 ".write": "auth != null && auth.uid == $uid",

                 "public": { ".read": "auth != null" }
                 }
               }
            }
}
  • I've created these rules to have users public/private profile
  • "users/{uid}/public" profile should be accessible by any users those are authenticated, but cannot access the data under "users/uid"

Here is some fake data that is stored in my firebase database.

{
  "users" : {
    "YFIIAgwa2kaannrXjwvSZmoywma2" : {
      "Name:" : "Example 1",
      //This public child should be accessible by 
      //"Example 2" but cannot know the name of 
      // this user
      "public" : {
        "email" : "[email protected]"
      }
    },
    "YgSfSzPzxLbyDL17r6P9id2cdvH2" : {
      "Name:" : "Example 2",
      //This public child should be accessible by 
      //"Example 1" but cannot know the name of 
      // this user
      "public" : {
        "email" : "[email protected]"
      }
    }
  }
}

I want to know if this is the robust way to prevent any users from accessing user's critical information! Is there anyway I can improve this by using validate? I am open to any suggestions you guys have. I want to create the best and simple security rules for my app.


Solution 1:

You can definitely secure access to the private and public data with your current data structure.

But one use-case you'll likely want at some point is to show a list of the public info for all users. With your current data structure that is not possible, because Firebase's security model cannot be used to filter data. For a great answer covering this, see Restricting child/field access with security rules.

Most developers split the public and private data in completely separate subtrees:

{
  "users" : {
    "YFIIAgwa2kaannrXjwvSZmoywma2" : {
      "Name:" : "Example 1",
    },
    "YgSfSzPzxLbyDL17r6P9id2cdvH2" : {
      "Name:" : "Example 2",
    }
  },
  "public_profiles": {
    "YFIIAgwa2kaannrXjwvSZmoywma2" : {
      "email" : "[email protected]"
    },
    "YgSfSzPzxLbyDL17r6P9id2cdvH2" : {
      "email" : "[email protected]"
    }
  }
}

You can then secure access with:

{
  "rules": {
     "users": {
        "$uid":{ 
             ".read": "auth != null && auth.uid == $uid",
             ".write": "auth != null && auth.uid == $uid",
        }
     },
     "public_profiles": {
        ".read": "auth != null",
        "$uid":{ 
             ".write": "auth != null && auth.uid == $uid",
        }
     }
  }
}

Now any authenticated user can listen to /public_profiles, which means you can easily show a list of these profiles.

Solution 2:

Hmm wouldn't it be easier to (re)structure the db so that you have a public and a private field per user? Something like:

{
  "users" : {
    "YFIIAgwa2kaannrXjwvSZmoywma2" : {
      "private": { 
        "Name:" : "Example 1" 
      },
      "public" : {
        "email" : "[email protected]"
      }
    },
    "YgSfSzPzxLbyDL17r6P9id2cdvH2" : {
      "private": { 
        "Name:" : "Example 2" 
      },
      "public" : {
        "email" : "[email protected]"
      }
    }
  }
}

/UPD: This way it should be easy(er) to have the different permissions because they won't inherit them from the parent?