2

I have a firebase realtime database. Currently it contains two nodes admin and common

{
  "admin" : {
    "adminval" : 9898574632,
    "adminval1" : 645354536,
    "adminval2" : 7776756433
  },
  "common" : {
    "commonval" : 123433221
  }
}

I added to each user custom claim roles which describes roles user has in my system. It looks like this

{'roles': ['ROLE_ADMIN', 'ROLE_USER']}

Now I would like to restrict access so only users with claim ROLE_ADMIN are allowed to read/write admin node and with either of the roles can read/write node common.

How to do it ? I tried something like it:

{
    "rules": {
       "admin": {
           ".read": "auth.token.roles.contains('ROLE_ADMIN')",
           ".write": "auth.token.roles.contains('ROLE_ADMIN')"
       }
       "common": {
           ".read": "auth.token.roles.contains('ROLE_USER') || auth.token.rules.contains('ROLE_ADMIN')",
           ".write": "auth.token.roles.contains('ROLE_USER') || auth.token.rules.contains('ROLE_ADMIN')"
       }
    }
  }
zkohi
  • 2,486
  • 1
  • 10
  • 20
Clyde Barrow
  • 1,924
  • 8
  • 30
  • 60

2 Answers2

1

I am afraid we cannot use Array or List in Realtime database security rules. Language of rules can work with only limited set of types. You can check the documentation here https://firebase.google.com/docs/reference/security/database.

We can walk around this limitation for instance by using String in claims:

{'roles': 'ROLE_ADMIN,ROLE_USER'}

Now we can check the appropriate role using

".read": "auth.token.roles.contains('ROLE_USER') || auth.token.rules.contains('ROLE_ADMIN')"

, because String supports contains method. Another option, as you suggest in your comment, is to set claims like

{'role_admin': true, 'role_user': true}

and then check the role in rules

".read": "auth.token.role_admin == true || auth.token.role_user == true"

Side note: we can use List and in operator in Firestore security rules, see doc here https://firebase.google.com/docs/reference/rules/rules.List.

zelig74
  • 482
  • 1
  • 6
  • 15
0

I've never used arrays in custom claims, so am not sure how that works, or why it doesn't work for you.

But I did notice that you're doing a weird mix of role-based access control and level-based access control in your rules, so want to give some general hints/observations:

  • You're marking your admins with both roles: ROLE_ADMIN and ROLE_USER.
  • But then you have an OR in the common node, checking for either role. This means there's no need to give the admin the ROLE_USER too.
  • Keep in mind that custom claims are limited to 1000 bytes and are sent with every request. I'd highly recommend dropping the ROLE_ prefix, since that's already implied in the roles name of the claim.
  • If you have a universal logic that admins can see more than regular users, consider using a numeric access_level claim instead. Say a regular user is level: 1, while an admin is level: 2. You can then in your claims check for the minimum level: ".read": "auth.token.access_level >= 1"
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • thanks for your points I'll think about them - anyway the example in my question it's just some abstract case - the real question is: Can I use array in custom claims ? Why the `contains` method doesn't work ? I want to have role-based access and a user can have multiple roles and I want to avoid having claim for each role like `{panelAdmin: true, otherPanelAdmin: false...` - what is the best approach to such case ? – Clyde Barrow Feb 12 '20 at 15:40