validating read access to specified authenticated user - firebase

I'm planning to create a realtime database for a chatting apps with private message channel, is there any specific rules that we can check the authenticated user has access to this room?
my database structure is mostly like this:
{
"channel": {
"unique_room_id": {
"participants": {
"uid1": 1537259273000,
"uid2": 1537259273000
}
"message": {
....
}
}
}
}
Is it possible to use hasChild like how it is used on .write rule or we need to manually validate the reference which means it's not really possible to add more participants to the room?
If possible I want to avoid the latter, thanks in advance
Note:
I'm also open to any alternative structures, and maybe some explanation why it is recommended

It's always easiest to use top-level lists instead of nesting multiple entity types in a single list. So remodel your data to:
{
"participants": {
"unique_room_id": {
"uid1": 1537259273000,
"uid2": 1537259273000
}
}
"messages": {
"unique_room_id": {
....
}
}
}
Now you can ensure that only participants in a room can read its messages with:
{
"rules": {
"messages": {
"$roomid": {
".read": "root.child('participants').child($roomid).child(auth.uid).exists()"
}
}
}
}

Related

How to fetch all resources from Firebase database with nested security rules?

In firebase, is it possible to configure a rule so that when a parent resource is fetched, it only returns child items that the user as access to?
I have a data structure like so:
{
application_access_request: {
app_id_1: {
access_key_1: {
email: "abc#b.com"
},
},
app_id_2: {
access_key_2: {
email: xyz#c.com
},
}
}
}
And then I have rules like so:
{
"application_access_request": {
"$appId": {
"$accessId": {
".read": "data.child($accessId).child('email').val() === auth.token.email",
},
}
},
}
As a user logged in with email abc#b.com,
When I request all resources from application_access_request/,
Then I want app_id_1 and it's children to be accessable / returned to the user,
Is it possible to allow reading of all application_access_request but only return apps that the auth'd user has access to?
No, security rules cannot be used to selectively return information (see rules are not filters). You may, however, be able to use querying to solve this use case. For example, if your data was structured:
{
application_access_request: {
app1: {
access_key: "user#example.com"
},
app2: {
access_key: "user2#example.com"
}
}
}
You can use query-based rules to limit querying:
"application_access_request": {
".read": "auth.uid != null && auth.token.email_verified &&
query.orderByChild == 'access_key' &&
query.equalTo == auth.token.email"
}

Firebase Rules: Allow user to read data only if user is included in a child node

I am attempting to rewrite my database rules to only allow members of a specific 'collection' to access that collection only if that member is included in the teams list. Referring to the image attached below, this is what my rule currently looks like:
{
"rules": {
"collection": {
"$collection_id": {
"$teams" : {
".read": "data.child('id').val() === auth.uid"
}
}
}
}
}
However this doesn't seem to work and I believe it's incomplete. How can I update my rules to match this design structure? If any change in my structure is necessary to support this, do pitch that and I will attempt to update the current production data.
You could reach into the teams node like this:
{
"rules": {
"collection": {
"$collection_id": {
".read": "data.child('teams').child(auth.uid).child('id').val() === auth.uid"
}
}
}
}
Right now you have unnecessary redundancy in the teams node. The id of the child node is repeated in its own child id. You can simplify your database and rules if you simply set teams/{teamId} = true in the database, your rule could look like this instead to allow only users listed under teams to read the entire collection:
{
"rules": {
"collection": {
"$collection_id": {
".read": "data.child(auth.uid).val() === true"
}
}
}
}

Ho to get all fields from firebase except one? [duplicate]

I have a collection of signatures where each signature has a few properties: public: fullname, city and then email.
I want to keep the email property private and I've been struggling with writing the correct rules to only return fullname and city. Here is what my rules.json looks like so far:
{
"rules": {
"signatures": {
"$signatureID": {
"public": {
".read": true
},
"email": {
".read": false
}
}
}
}
}
When I go to the /signatures end point, I would like to receive an array of signatures with the public data and not receive the email addresses.
So far I haven't had any luck getting this to work the way I want it to. Am I doing something wrong? Should I structure my data differently?
With respect to security rules, Firebase operations are all-or-nothing.
As a result, attempting to load all of the data at /signatures will fail because your client does not have permission to read all of the data at that location, though you do have permission to read some of the data there. Similarly, writing to a location behaves the same way, and full permission is required before your operation will continue.
To handle this use case, consider restructuring your data like this:
{
"rules": {
".read": false,
".write": false,
"signatures-public": {
".read": true,
"$signatureID": {
// ... public data here
}
},
"signatures-private": {
"$signatureID": {
// ... private data here
}
}
}
}

Adding .indexOn for nested data

I am getting the firebase warning "Using an unspecified index. Consider adding ".indexOn": "E-mail" at /user/544dceb6-98d-4f7e/clients.
The rule I added is:
{
"rules": {
"user": {
"$clients": {
".indexOn": ["E-mail"]
}
}
}
}
This is the call I'm making:
firebaseRef.child(authData.uid + "/clients").orderByChild("E-mail").equalTo(this.props.params.clientId).on('value', function(clientSnapshot) { ... }
This is how my data is structured:
database
+ user
+ 544dceb6-98d-4f7e...
+ clients
+ -K96sXIXRUIOK8....
+ E-mail
I suspect the issue has something to do with the two layers of keys but can't find a solution.
Many thanks!
You're missing a level in your rules.json:
{
"rules": {
"user": {
"$userid": {
"clients": {
".indexOn": ["E-mail"]
}
}
}
}
}
Side note: it is somewhat confusing that you have user (singular) and clients (plural) in the tree. Making them consistent helps in keeping your data structure easier to understand.

Firebase Security Rules: Public vs. Private Data

I have a collection of signatures where each signature has a few properties: public: fullname, city and then email.
I want to keep the email property private and I've been struggling with writing the correct rules to only return fullname and city. Here is what my rules.json looks like so far:
{
"rules": {
"signatures": {
"$signatureID": {
"public": {
".read": true
},
"email": {
".read": false
}
}
}
}
}
When I go to the /signatures end point, I would like to receive an array of signatures with the public data and not receive the email addresses.
So far I haven't had any luck getting this to work the way I want it to. Am I doing something wrong? Should I structure my data differently?
With respect to security rules, Firebase operations are all-or-nothing.
As a result, attempting to load all of the data at /signatures will fail because your client does not have permission to read all of the data at that location, though you do have permission to read some of the data there. Similarly, writing to a location behaves the same way, and full permission is required before your operation will continue.
To handle this use case, consider restructuring your data like this:
{
"rules": {
".read": false,
".write": false,
"signatures-public": {
".read": true,
"$signatureID": {
// ... public data here
}
},
"signatures-private": {
"$signatureID": {
// ... private data here
}
}
}
}

Resources