Firebase user account security with firebaseSimpleLogin

我们两清 提交于 2019-12-06 09:35:53
Kato

You've essentially asked for someone to write an entire security schema for your app. It would be better if you understood security rules thoroughly before attempting to apply them to a complex structure like this. A good study of the docs from start to end would take you a long way to a fully functional solution.

Let's just focus on what seems to be the core problem, which is that you aren't sure how to make invites work securely with a client-only solution. (The node.js solution should also be obvious with this understanding, given the additional firepower provided by being able to create our own tokens) I'll make a lot of assumptions here; just apply these ideas to your current use case as you see fit.

A data structure:

/invites/$game_id/$uuid/true  (a place to store invited users)
/accepted_invites/$gameid/$userid/$uuid/true (a place to store accepted invites)
/games/$game_id (the place we want to invite users into)
/users/$user_id (a place where we put profiles for existing users)

1) When a new user creates an account in the app, write their profile into /users. Secure users/ as follows:

"users": {
   "$userid": {
      ".write": "auth.uid === $userid"
   }
}

2) To invite a user, create a uuid, which represents an unguessable id, and store it in invites/$game_id. Note that nobody should be able to read this path.

"invites": {
   "$game_id": {
     "$invite_id": {
        // I can only create an invite for groups I'm a member of
        ".write": "root.child('games/'+$game_id+'/members/'+auth.uid).exists()",
        ".validate": "newData.val() === true"
     }
   }
}

3) To join a game, a user must first accept the access token, which proves that they know the token (since they can't read the invites path) and links the token to their account id. The value of this entry is the uuid of the invite.

"accepted_invites": {
   "$game_id": {
      "$user_id": {
         ".write": "auth.uid === $user_id",
         ".validate": "root.child('invites/'+$game_id+'/'+newData.val()).exists()"
      }
   }
} 

4) A user can write themselves into a game if they have accepted an invite or when it is initially created and there are no members yet (the !data.parent().exists() rule)

"game": {
   "$gid": {
      "members": {
        "$uid": {
           ".write": "auth.uid === $uid",
           // I can join a group if a) I'm creating it or b) I have accepted an invite
           ".validate": "!data.parent().exists() || root.child('accepted_invites/'+$gid+'/'+auth.uid).exists()"
        }
      }
   }
}

Another enhancement to our client-side solution would be to assign the invites a priority, which represents when they expire, and then reference that priority in the security rules to control how long the token is valid.

I ended up going with idea #3, thanks to the help of @Kato's suggestions. The solution of using the built in rules and designing a schema allowed us to avoid needing a 3rd party auth server, so far. Some example of the schema was as follows:

 "game_detail" : {
      "$game_detail" : {
        ".read" : "data.child('owner').val() === auth.email || root.child('admins').val() === auth.email",
        ".write" : "newData.child('owner').val() === auth.email || root.child('admins').val() === auth.email"        
      }
    },

Then, the additional key that made #3 possible was that, in addition to having a schema of security rules, we also created a generic admin credential that can be used by anonymous users when they are logged in to access a subset of the DB and perform necessary cross account operations.

Thanks all for the input here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!