I use Firebase $authWithPassword method for user login. I use the $createUser method to create registration for my users and on its success I update entry on my /users/ path to save the username, uid and some other details. Here is the code
var myDataRef = new Firebase(baseURL + 'datalog/');
var refObj = $firebaseAuth(myDataRef);
refObj.$createUser({
email: $scope.emailId,
password: $scope.password
}).then(function(userData) {
var UserUniqueUrl = new Firebase(baseURL + 'users/' + userData.uid + '/');
UserUniqueUrl.set({
'email': $scope.emailId,
'username': $scope.username,
'uid': userData.uid,
'theme': 'default'
}, function(error) {
if (error) {
console.log(error);
} else {
console.log('Successfully updated in user table');
}
});
}).catch(function(error) {
if (error.code == 'EMAIL_TAKEN') {
$scope.regStatusError = 'Email already registered!';
} else {
$scope.regStatusError = 'Unable to register! Try again later.';
}
});
And here is my security rules
{
"rules": {
"users": {
".read": "auth != null",
".write": "auth != null"
}
}
}
Now if I try to register it gives me permission denied error which I'm sure is because of the security rules ".read": "auth != null" and ".write": "auth != null". If I change the rules to ".read": true and ".write": true, the registration will work but anyone will be able to see my user data including uid and email id which I don't want to happen. How do i change my rules to fit my need?
This is how my user table will look like
Any help is appreciated.
Thanks.
All you want is to only allow creating a user in your firestore database when the user is authenticated:
match /users/{uid} {
allow create: if request.auth.uid != null;
}
However, you can go a head to verify the user's email as:
match /users/{uid} {
allow create: if request.auth.uid != null
&& request.auth.token.email_verified == true
}
Please note that uid will match your IDs of your documents.
yeah, you need to change your rules to something like this
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
this should fix it
Related
I have a firebase database with the following structure
I want to define a database rule where authenticated users can read the data only where authenticated user's id is equal to userId defined in the data.
How can i define this rule?
I have tried defining a rule as shown below but id didn't work. It doesn't allows any read operation
{
"rules": {
".read": "auth.uid == root.child('orders').child('userId').val()",
".write": "auth != null"
}
}
This is how i am making a GET request to firebase's REST api
axios.get(`/orders.json?auth=${firebaseAuthToken}`)
.then(response => {
if (response.status === 200) {
dispatch(fetchOrdersSuccess(Object.values(response.data)));
}
})
.catch(error => {
dispatch(fetchOrdersError(error.message));
});
try the following rule for reading orders
{
"rules": {
"orders": {
"$orderId": {
".read": "data.child('userId').val() == auth.uid",
".write": "auth != null"
}
}
}
}
I have a mobile application which reads the data from the firebase server without firebase login/authentication (posts and news) and I want to create an admin webpage where I can log in and add, or modify news, so I need a write permission there. My rules are currently:
{
"rules": {
".read": true,
".write": "auth !== null && ?????
}
}
Can I write something like "user.emailAddress == 'mail#example.com'"?
You can create a users table on database like
{
"users":{
"your UID":{
"isAdmin": true
}
}
}
Then edit rules :
{
"rules": {
".read": true,
".write": "auth.uid != null && root.child("users").child(auth.uid).isAdmin === true"
}
}
You might want to start by reading the documentation about securing user data. There is a lot to know here.
One possibility is using the known user's uid to restrict access. The auth.uid variable contains the uid.
".write": "auth.uid == 'the-known-uid'"
Also you can use auth.token to access some other things about the user, including email address (which may not be present):
".write": "auth.token.email == 'the#email.address'"
You can also use custom authentication tokens, which also is covered in the documentation.
Create database:
{
"users":{
"your UID":{
"isAdmin": true
}
}
}
Set rules:
Wrong:
{
"rules": {
".read": true,
".write": "auth.uid != null && root.child("users").child(auth.uid).isAdmin === true"
}
}
Right:
{
"rules": {
".read": true,
".write": "auth.uid != null && root.child('users').child(auth.uid).child('isAdmin').val() === true"
}
}
This is my DB structure
"tasks"
"$taskId"
...
"user": "firebase user id"
I have already written a rule ".read": data.child('user').val() === auth.uid" under $taskId. When I try to access a single task, this rule is taking effect.
Will this also guarantee that if I write a query like firebase.database().ref('/tasks').orderByChild('status').limitToFirst(1) I'll only get tasks that have user id field as auth.uid. Or should I also write a .read clause under tasks
There are several aspects to be answered in your question:
1/ At which level should you write the security rules?
If you write only at the task level like just follows, you will not be able to query the entire set of tasks.
You can test it by doing the following:
Rules:
{
"rules": {
"tasks": {
"$taskID": {
".read": "auth != null",
".write": "auth != null"
}
}
}
}
JS:
var db = firebase.database();
var ref = db.ref('tasks');
firebase.auth().signInWithEmailAndPassword("....", "....")
.then(function(userCredential) {
ref.once('value').then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val());
});
});
});
This will fail with "Error: permission_denied at /tasks: Client doesn't have permission to access the desired data."
If you change var ref = db.ref('tasks'); to var ref = db.ref('tasks/123456'); (123456 being an existing task id) you will get a result.
If you change your rules to the following, the two previous queries will work.
{
"rules": {
"tasks": {
".read": "auth != null",
".write": "auth != null"
}
}
}
2/ How should you do to only get tasks that have user id field as auth.uid?
The first point to note is that "Rules are not Filters", as detailed here: https://firebase.google.com/docs/database/security/securing-data#rules_are_not_filters
So if you implement security rules as follows:
{
"rules": {
"tasks": {
"$taskId": {
".read": "auth != null && data.child('user').val() === auth.uid",
".write": "auth != null"
}
}
}
}
You will need to write a query that includes the same restriction on the user uid, like the following:
var db = firebase.database();
firebase.auth().signInWithEmailAndPassword("....", "....")
.then(function(userCredential) {
var ref = db.ref('tasks').orderByChild('user').equalTo(userCredential.user.uid);
ref.once('value').then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val());
});
});
});
But this query will not work, again, because "Error: permission_denied at /tasks: Client doesn't have permission to access the desired data."
You cannot do the following neither, since "Shallower security rules override rules at deeper paths.":
{
"rules": {
"tasks": {
".read": "auth != null",
".write": "auth != null"
"$taskId": {
".read": "auth != null && data.child('user').val() === auth.uid",
".write": "auth != null"
}
}
}
}
One solution is to use Query-based Rules (see the doc here) and write your rules as follows:
{
"rules": {
"tasks": {
".read": "auth != null &&
query.orderByChild == 'user' &&
query.equalTo == auth.uid",
".write": "auth != null"
}
}
}
However, as you have probably noticed, this will prevent you to order your query (and filter it) by something else than the user (e.g. by status), since "You can only use one order-by method at a time."
The solution would therefore be to create a second data structure in parallel to your existing structure, where you add the user as a top node, like
"tasks"
"$taskId"
...
"user": "firebase user id"
"tasksByUser"
"$userId"
"$taskId"
...
You would use the update() method to write to the two data structures simultaneously. See the doc here.
I gave .read: true under tasks and it is considering the rules written under the individual task objects before returning the results.
Im having trouble with the security rules for firebase and Im not 100% where I am going wrong. I am thinking that maybe I have my data structure wrong:
{
"users": {
"uid": {
"displayName": "Name";
}
},
"modules": {
"id": {
"title": "buttons",
"uid": "(user id string)"
},
"id": {
"title": "navbars",
"uid": "(user id string)"
}
},
"snippets": {
"id = moduleID": {
"id (of snippet)": "(id string)" {
"uid (user ID)": "(string)",
"body": {
"css": "(some code)",
"html": "(Some code)",
"name": "(string)",
"description": "(string)"
}
}
}
}
Everything in the app works fine, but when I started to add security rules I got access denied errors. Im just wondering if I have the data structure correct in the first place or is the security rules completely wrong?
Security rules:
{
"rules": {
"users": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
},
"snippets": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
},
"modules": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
}
}
Any advice would be greatly appreciated.
It seems the rules are malformed based on the data structure.
The rules have $uid's in each node but your data doesn't match that. Users has uid but modules has id and snippets has id = moduleID.
$uid is a variable that holds the node name so it can be referenced inside { } so you should (for readability) rename that variable in the other two nodes to something that makes more sense inside each {}. Like in modules, have it $module_id.
However. I think the jest of this is you want to limit reading snippets and modules to authenticated users. To do that, you can reference the users node.
a .read rule would be something like this
"modules": {
"$module_id": {
".read": "auth != null && root.child('users/' + auth.id).exists()
}
So your modules node can be read by a user that is auth'd and their uid also appears in the users/ node
Are you using the Firebase Bolt compiler for rules? I had to write some complex rules and doing it by hand gets confusing very quickly.
Below is what it would looks like. Very easy to make changes, compile and try them out.
//current logged in user
isUser(uid) = auth != null && auth.uid == uid;
//does this module id exist
hasValidModule(module_id) = root['modules'][module_id] != null;
//dont let anyone read or write to top node
path / {
read() = false;
write() = false;
}
path /users/$user_id
{
write() = isUser($user_id);
read() = isUser($user_id);
}
path /snippets/$module_id/$snipit_id/$user_id
{
write() = isUser($user_id) && hasValidModule($module_id);
read() = isUser($user_id);
}
path /modules/$user_id
{
write() = isUser($user_id);
read() = isUser($user_id);
}
Here's the json it spits out:
{
"rules": {
"users": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
},
"snippets": {
"$module_id": {
"$snipit_id": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id && newData.parent().parent().parent().parent().child('modules').child($module_id).val() != null"
}
}
}
},
"modules": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
}
}
}
There's some info on the Firebase blog but the doc that really helped me is this
https://github.com/firebase/bolt/blob/master/docs/language.md
So I have this db structure:
Under profile I want email & provider-name to be readable only for admin and
Username readable for every logged in user.
How I can achieve that?
Here is my rules:
{
"rules":
{
"users":
{
"$uid":
{
// grants write access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid === $uid",
"profile":
{
// grants read access only for registered users
".read": "auth !== null",
"email":
{
// This doesn't work with firebase as I was reading doc.
".read": false
}
}
}
}
}
}
So after a bit of research and reading about denormalize structure I guess this way will work. The fact is that I'm tempted to nest, but probably is a bad idea on firebase.
{
"rules":
{
"users":
{
"$uid":
{
// grants write access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid == $uid",
"public-profile":
{
// grants read access only for registered users
".read": "auth !== null"
}
}
},
"private-profile":
{
"$uid":
{
".read": "root.child('users').child(auth.uid).child('role').child('admin').val() === 'true' && root.child('users').child('1').child('role').child('admin').val() === 'true'",
".write": "root.child('users').child(auth.uid).child('role').child('admin').val() === 'true' && root.child('users').child('1').child('role').child('admin').val() === 'true'"
}
}
}
}