I am trying to access custom fields in my auth for controlling access to my data in firebase, The users field in firebase looks like this
users: {
email: string,
isAdmin: boolean,
name: string
}
I have this rules for ensuring that only admin can read the entire table of orders
"orders":{
"$userOrders": {
".read": "$userOrders == auth.uid || auth.isAdmin == true",
"$eachUserOrder": {
".read": "$userOrders == auth.uid || auth.isAdmin == true",
".write": "!data.exists()"
}
},
".read": "auth.isAdmin === true"
},
In the simulation if I pass this as part of the token it works,
{
"provider": "anonymous",
"uid": "559a9195-e2ab-4039-91ac-877d2afca482",
"isAdmin": true
}
But for users with the field isAdmin set to true on the browser it says permission denied.
util.js:233 FIREBASE WARNING: Exception was thrown by user callback. Error: permission_denied at /orders: Client doesn't have permission to access the desired data.
I don't understand what is wrong at this time.
Related
I'm getting permission denied after I have authenticated with Anonymous Auth
[ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter (21033): PlatformException(-3, Permission denied, )
...
_getCurrentUser: FirebaseUser({providerId: firebase, uid: DOIL...............u54j1, displayName: , email: , isAnonymous: true, isEmailVerified: false, providerData: [{providerId: firebase, uid: DOIL//////////////54j1, displayName: , email: }]})
My rules on the Firebase DB are
{
"rules": {
//Test rule
// "users": {
// "$uid": {
// ".read": "auth != null && auth.uid == $uid",
// ".write": "auth != null && auth.uid == $uid",
// }
// }
// General rule - closed to everyone but app uses
".read": "auth != null",
".write": "auth != null"
}
}
The code I use to save data - works fine with DB rules set to open to all.
_saveUserData(UserEntry userData) async {
print("_saveUserData jsonData =" userData.toJson().toString());
// SAVE MY DATA TO DB
// tUsers = db reference to the child node users on Firebase
_tUsers.push().set(talentUserData.toJson());
}
What am I doing wrong ? Any assistance is appreciated. Thanks.
In the hope of saving someone a headache. I found the problem to be not getting a fresh instance of the Firebase Database. I created the instance in my main.dart and passed it to my HomePage in the constructor (per the example file in the library).
When I got a fresh instance ...out of desperation debugging...it worked. I can now have anonymous login so that only people who have installed the app can write to the database.
I want to stop the signup process if the username exists. I don't want to create Email\Password (Authentication) and other info if the username exists
database rules:
{
"rules": {
"users": {
".read": "(auth != null)",
".indexOn": ["username"],
"$uid": {
"$username":{
".write": "(auth != null) && !data.exists() || newData.exists()"
}
}
}
}
}
I have added !data.exists() in .write , but still it duplicate usernames.
And then I added ".validate" to make username atleast 3 characters, to see if the signup process gonna fail if its longer, but it created email\password and other stuff but didn't wrote any data in database root\users\
After registered new user with email\password I got this error, Which means username couldn't be more than 3 characters. How to stop the signup process and show alert message at this situation?
[Firebase/Database][I-RDB03812] setValue: or removeValue: at /users/jt94ARqOoodtU3kYp3W1MtlUZ4m1 failed: permission_denied
Please help me if you could fix the problem.
Your simplified rules are:
{
"rules": {
".write": "(auth != null)",
"users": {
"$uid": {
"username": {
".write": "auth != null && (!data.exists() || !newData.exists())",
So you first grant any authenticated user write access on the root of the data and subsequently say that a user can only write if there is not data a the current location.
That won't work: once you grant permissions at a certain level, you cannot take that permission away on a lower level.
The Firebase documentation describes this as Read and Write rules cascade:
Child rules can only grant additional privileges to what parent nodes have already declared. They cannot revoke a read or write privilege.
After a user registers, we create a username for the user under /users child.
How can I setup my rules so that only the server can create new users and user data cannot be modified by other users.
Javascript
<script>
var ref = new Firebase("https://myapp.firebaseio.com");
var usersRef = ref.child('users');
var usernameField = $('#usernameInput');
var emailField = $('#emailInput');
var passField = $('#passInput');
$('#submitBtn').click(function() {
ref.createUser({
email : emailField.val(),
password : passField.val()
}, function(error, userData) {
if (error) {
console.log("Error creating user:", error);
} else {
console.log("Successfully created user account with uid:", userData.uid);
usersRef.push({
uid: userData.uid,
username: usernameField.val()
});
}
});
});
</script>
Current rules:
{
"rules": {
"users": {
".indexOn": "username",
"$uid": {
".write": true,
// grants read access to any user who is logged in with an email and password
".read": "auth !== null && auth.provider === 'password'"
}
}
}
}
To set up something so that "only the server can create new users", you can access your database using their Server SDK. This way, you can set rules that only apply to access to the database from the server.
However, if you want a web-based UI for admins to create new users, you need to write a Node.js or Java backend web server (utilizing the Server SDK). You can't use the client-side JS SDK, or Firebase hosting.
Just follow the instructions in the Server Side setup section of their documentation.
Then add the databaseAuthVariableOverride option to initializeApp:
var firebase = require("firebase");
firebase.initializeApp({
serviceAccount: "path/to/serviceAccountCredentials.json",
databaseURL: "https://databaseName.firebaseio.com",
databaseAuthVariableOverride: {
// maybe use something more descriptive and unique
uid: "server-side-app"
}
});
Then the following as your rules (warning: not tested):
{
"rules": {
"users": {
".indexOn": "username",
// grants write access if the authenticated user is accessing
// via the Server SDK
".write": "auth != null && auth.uid === 'server-side-app'",
// grants read access to any user who is logged in with an email/password
".read": "auth != null && auth.provider === 'password'"
}
}
}
There is no concept of "only the server" when you use Firebase. Each user writes their own data, so to secure that you need to use Firebase's User Based Security.
In this case the rules are fairly simple: you want each user to be able to only write to their own node. You can do this by modifying your rules to:
{
"rules": {
"users": {
".indexOn": "username",
"$uid": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "auth.uid === $uid",
// grants read access to any user who is logged in with an email and password
".read": "auth.uid !== null && auth.provider === 'password'"
}
}
}
}
Note that I copied the .write rule from the first example in the Firebase documentation on User Based Security. So it's probably a good idea to read that page (again), since it explains a lot more than just the answer to this question.
I have below data stored in my firebase:
firebaseRoot
admins
simplelogin:1:
users
simplelogin:1
email: abc#xyz.com
picture: csd
provider: password
uid: simplelogin:1
simplelogin:2
email: abc1#xyz.com
picture: zsd
provider: password
uid: simplelogin:1
and following security rules:
{
"rules": {
"admins": {
".read": "root.child('admins').child(auth.uid).val() === true",
".write": "root.child('admins').child(auth.uid).val() === true"
},
"users": {
"$user":{
".read": "$user === auth.id || root.child('admins').child(auth.uid).val() === true",
".write": "$user === auth.id"
}
}
}
}
My authorization requirements are as below.
admins can be read and added only by the existing admin only. This works.
All users can be read by the admin but should not be able to write user data.
a user can read and update his own user data.
Currently with above rules, I am not able read users data both for admins and logged in users. I get below error message. Please provide your help. Thanks.
var rootRef = new Firebase('https://xxxxx.firebaseio.com/');
var users = rootRef.child('users');
users.on('value', function(snap) {
console.log(snap.key(), snap.val());
}, function(error) {
console.log(error);
});
Error:
Error: permission_denied: Client doesn't have permission to access the desired data.
There are two pitfalls when it comes to Firebase security rules:
rules cascade
This means that once you give somebody (read or write) access on a certain level in the JSON structure, you cannot take that right away anymore on a lower level
rules are not filters
This means that you can only read a node if you have read access to all data in that node. If you only have read access to part of the data, a read operation for the complete data will fail.
In your security rules, you only give permission to read (some of) the children under users. So trying to read the entire users.on('value' will fail.
You can solve this by giving the administrator .read access to the users node.
"users": {
".read": "root.child('admins').child(auth.uid).val() === true",
"$user":{
".read": "$user === auth.id",
".write": "$user === auth.id"
}
}
Here is my working example:
ajsecuretest
roles
simplelogin:1
role: 'admin'
simplelogin:2
role: 'editor'
users
simplelogin:1
email: 'abc#xyz.com'
picture: 'a.jpg'
provider: 'password'
uid: 'simplelogin:1'
simplelogin:2
email: 'xyz#abc.com'
picture: 'b.jpg'
provider: 'password'
uid: 'simplelogin:2'
Rules:
{
"rules": {
"roles":{
".read": "root.child('roles').child(auth.uid).child('role').val() === 'admin'",
".write": "root.child('roles').child(auth.uid).child('role').val() === 'admin'",
"$id" : {
".read" : "$id === auth.uid"
}
},
"users": {
".read" : "root.child('roles').child(auth.uid).child('role').val() === 'admin'",
"$user":{
".read": "$user === auth.uid",
".write": "$user === auth.uid"
}
}
}
}
Before in my app I was creating accounts successfully until I put some 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",
// grants read access to any user who is logged in
// with an email and password
".read": "auth !== null && auth.provider === 'password'"
}
}
}
}
then, the accounts are created because I see those accounts in the dashboard. But once I try to create, I am getting this kind of errors
FIREBASE WARNING: set at /users/simplelogin:32 failed: permission_denied
Since you want the user to be able to create their own node initially, you'll need to explicitly allow the case where the location is new.
Based on the documentation on the auth parameter:
".write": "!data.exists() || auth.uid === $uid""
Note: I'm not entirely sure this will work, but felt it'd be more readable as in answer-format than as a "try this" comment.