I started today with Firebase and try to create a sample iOS app.
At the moment I'm stuck with securing my data.
Lets say I have this demo data.
{
"Demodata" : {
"demodata-1" : {
"id" : 1,
"uid" : 1234
}
}
}
and this rule file
{
"rules": {
"Demodata" : {
"$uid" : {
".read" : "auth.uid == $uid"
}
}
}
}
and a logged in user with the uid : 1234
I log in with this code.
ref.authUser(email, password: password,
withCompletionBlock: { error, authData in
completion(result: error)
})
Then I try to get the data with this code.
demoDataRef.queryOrderedByChild("id").queryEqualToValue(queryValue).observeSingleEventOfType(.Value, withBlock: { snapshot in
// do some stuff once
print(snapshot)
})
But I never get the data back from Firebase. When I remove the rule and add a ".read" : true to the parent everything works.
I'm sure I miss something but at the moment I'm a little bit lost. Maybe some one can point me in to the right direction.
There are two things wrong here. One of them is easy to fix, the other one is more difficult.
1. "Ordering skips a level"
You're missing a level in your JSON data. When you call queryOrderByChild("id")
the ordering is applied to the "id" child property of each child node of demoDataRef
.
To fix it, add a level for the users to your JSON:
{
"Demodata" : {
"demodata-1" : {
"user1": {
"id" : 1,
"uid" : 1234
},
"user2": {
"id" : 2,
"uid" : 2345
}
}
}
}
2. Rules are not filters
The second problem is that you're trying to execute a query on Demodata
, but your user doesn't have read access to Demodata
. This means that Firebase will immediately reject the operation. If you attach withCancelBlock
, you'll see that rejection.
So while a user may be able to read a specific record (or even each specific record), they are not able to query the list of records. This is a common misunderstanding of Firebase's security rules that is covered in the documentation under rules cascade and rules are not filters.
Unfortunately this means that you'll typically have to pick a different way to structure your data. When storing users, the common solution is to store each user under their uid:
{
"Demodata" : {
"demodata-1" : {
"1234": {
"id" : 1,
"name" : "user1"
},
"2345": {
"id" : 2,
"name" : "user2"
}
}
}
}
Now each user can access their own profile with a direct lookup:
demoDataRef.childByAppendingPath(queryValue).observeSingleEventOfType(.Value...
Also see:
- How only grant read access to data that own specific value in Firebase?
- Firebase rules - Cannot access to each node
- Show only items that have userid as child