Search code examples
firebase-realtime-databasefirebase-authenticationfirebase-security

Firebase rules to give access to a path based on email address


I'm writing an app where it should be possible to invite users to a family. Once the user accepts they become members.

The data looks like this:

families: {
   familyKey1: {
      name: "First Family Name"
   },
   familyKey2: {
      name: "Second Family Name"
   },
},
invites: {
   inviteKey1: {
      familyKey: "familyKey1",
      email: "user@domain.tld"
   }
},
users: {
   uid1: {
      familyKey: "familyKey2"
   }
}

I want a logged in user to be able to access the /families/familyKeyX/ path if:

  1. familyKeyX exists /users/uidX/ for the currently logged in user.
  2. There is an invite under /invites/inviteKeyX/ where the email matches the currently logged in user and the invite contains familyKeyX

The first part works fine by creating a rule as follows:

"families": {
   "$familyKey": {
      ".read": "root.child('users').child(auth.uid).child('familyKey').val() == $familyKey"
   }
}

The second part however I cannot get to work. It could have been set up the same way if I could post the email as part of the path to the object but firebase does not allow the @ sign so I had to generate the inviteKey. But that key is unknown when I write the rule and firebase doesn't allow searches or wildcards on the rule path.

I was thinking of making the path {"invites": { "familyKeyX": { "email": "user@domain.tld"} } }. But that is a no-go since it only allows for one invite per family. There could be multiple users with different emails with pending invites. As soon I introduce a unique key to form a list of invites I don't know the path to write the rule...

How do I solve this?


Solution

  • In Firebase security rules you can read/check data at a known path in the database. You cannot however search for data under a specific path in the database.

    So while in code you could do:

    root.child("invites").orderBy("email").equalTo("user@domain.tld")
    

    The equivalent to this query is not possible in security rules.


    In order to be able to look up whether a given email has access to the family, you will need to store the email address of the user as a key in a separate map. Since . is not allowed inside a key, you'll have to encode that - typically by replacing it with a , which is allowed in a key and conveniently cannot appear in an email address.

    So:

    emailToInvites: {
       "user@domain,tld": {
          "familyKey1": "inviteKey1"
       }
    },
    

    Now with this in place, you can replace . with , in the email address and then use that to look up if an invite exists under emailToInvites.