i am new to firebase. i have set up a firebase realtime database and can read from and write to it if the read and write rules are set to true.
i have a problem with authentication.i have set up authentication for google and email plus password.
my goal is to allow any user to read the data but only one user (myself) can write data after logging in using a single email address and password.
i can successfully read from and write to the database if i login with google (with rules set to: auth != null.)
i can also read from and write to the database using the same rules (auth != null) if i log in with the email address and password.
i don't know how to set it up to only allow write access for the single user logging in with an email address and password.
i have tried including a user node in the rules but i can't get access when using the simulator (see below) and i don't know how to include the uid (which i can get after logging in) when building the reference - this is the reference i currently use (which works with the read and write rules set to true):
DatabaseReference databaseReference = mRootReference.child("Action_helper/1/Action_table_of_contents");
i have not included a users node in my database as i am assuming that is taken care of by firebase authentication.
here is the layout of my data:
i have tried the simulator using various rules options. testing access using these settings in simulator (choosing the Custom provider option):
Auth {"provider" : "firebase", "uid" : "Rp3OgoaABMN3hqTv0aF29ECQRCQ2"}
note: i get the provider and uid from Firebase object after logging in with an email address and password which i have set up in Firebase authentication:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// User is signed in
userId = user.getUid();
String provider = user.getProviderId();
i would appreciate some help in 1) formulating my rules, 2) if and how i should change my data structure, and finally 3) how to include the uid in the database reference which i'll use to write data to the database.
thanks
There is no users node so, defining in rules would not help. I think the rule that may work would be something like below (assuming 0 and 1 are uid):
{
"rules": {
"Action_helper":{
"$uid":{
//user-based security
".read": "auth != null && $uid === auth.uid",
".write": "auth != null",
}//$uid
}//Action_helper
}// rules
}
Examining above rules by default if we do not define rules then it is false i.e. at Action_helper it is false for both read and write. When it comes to the node uid (where $ denotes wild card) then, we check if the user id of logged in user is same to uid of this node and define rules accordingly.
I highly recommend to go
through the link The key to Firebase security - Google I/O 2016 , it is very helpful, easy to follow, and best explanation I found so far with demo example.
The data layout will depend on your requirement and screens. Although Firebase allows 32 level of nesting it is highly recommended to nest nodes as less as possible. And other important thing to think about the data layout is to keep data as denormalize as possible even if we
have to make copies of fields across the nodes.
To include uid in database reference you can keep on appending each child:
DatabaseReference databaseReference = mRootReference.child("Action_helper).child(uid).child("Action_table_of_contents");
So, here we are referring from root node to child "Action_helper" and going further down to it's child that matches uid and of that uid we are referencing to the child "Action_table_of_contents".
thanks for the help. i managed to get it working (partly) but am not sure that i am doing it correctly. here is my data structure (i have changed the names)- there is one user node (using the authentication uid), and two child nodes which contain the data:
and here are my rules:
essentially it works in the simulator but in code, i am able to log in and read and write. BUT i now have a problem, if i don't log in then the uid passed in the query reference is null, if i put a dummy value as the uid then i can't access the data at all (as the data is under users/the_valid_uid node and the dummy uid does not match the_valid_uid).
so how do i build a database reference without hard coding the valid user's uid? so that i can access the data in the Addiction_items and table_of_contents_items nodes (my aim is to allow anyone to read data in both nodes but to only allow one user (myself) to be able to write to both nodes after logging in with my email address and password?
thanks
Related
I'm beginner and I'm developing a chrome extension that shows data received from my firebase realtime database. It does not need Login, or any personal information.
I'm trying to use REST API. Until now I have been in a test mode so I opened access to data for all. But Google keeps mailing me that I have to change the access rule because it is dangerous.
My present access rule is this:
{
"rules": {
".read": true,
".write": false
}
}
Now, I fetch data from https://<project name>.firebaseio.com/<database name>.json. But if I change the rule, the access will be denied.
So I want to add some key to the url and change access rule according to it so that I can fetch data from the url. For example,
https://<project name>.firebaseio.com/<database name>.json?some_key=<some_key>.
I do not need personal keys so I want only one key just like when we get information from open APIs. For example, when I use weather api, I get my api key from the host and adds the key to url.
Is this possible? If it is, I wonder two things.
the key that I can use (for example, realtime base wep API key)
access rule of realtime database
You can't pass parameters to the Firebase database like this:
https://<project name>.firebaseio.com/<database name>.json?some_key=<some_key>
But if you change the order of the values, it suddenly becomes possible:
https://<project name>.firebaseio.com/<some_key>/<database name>.json
We then change the security rules to only allow access when somebody already knows the secret key:
{
"rules": {
".write": false,
"$some_key": {
".read": true
}
}
}
Now the secret key is part of the path of the data, and only a user that already knows the key can read the data for that key.
This question already has an answer here:
Creating Firebase Storage Security Rules Based on Firebase Database Conditions [duplicate]
(1 answer)
Closed 1 year ago.
I want to allow read permission on a file in Firebase Storage only if the value of a certain node in the Firebase Realtime Database is true. Is it possible to do so? If yes, then how?
That is not possible at this time. You would have to use Cloud functions or your own servers using the Admin SDK to do that.
The Admin SDK essentially has full access to your Firebase project's resources so you can check if the value you are looking for in the Admin SDK exists. If yes, proceed with getting the signed URLs (or the data you are looking for from storage) else return an error.
A simple cloud function for that would be something like:
exports.getStorageData = functions.https.onCall((data, context) => {
const {uid} = context.auth;
const {fileName} = data;
if (!uid) return {error: "User not authorized!"}
// Read for data in realtime database
const dbRef = admin.database().ref("/path/to/data");
if ((await dbRef.once("value")).val()) {
return {error: "Required Value is not true"}
}
//Get signed URL or any data from storage
const storageRef = admin.storage().bucket()
//.....
return {data: "..."}
});
You need to use such secure env such as server or functions as only client side validation is not secure. But is the true value in your database something like a role or anything? You can try adding those in Custom Claims. I'm not totally sure about your use case but if it something like allowing access to specific folders or something then you can add a claim the_folder_id: true. This is not the best solution if a user can have access to a lot of folders. In that case you can assign groups as mentioned in the documentation. But satisfies your needs then you can try the following security rules along with this.
// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
allow read: if resource.metadata.owner == request.auth.token.groupId;
allow write: if request.auth.token.groupId == groupId;
}
A manager needs to make the team's schedule live/public. The main challenge I'm running into is my firebase db security rules require users to be logged it in order to read data from it. They sort of look like this:
{
"rules": {
.read:true,
.write:true
}
"profiles":{
".read": true,
".write": true
},
"$clinicId":{
".write":"root.child($clinicId).child('permissions').child('admins').child(auth.uid).val() == true",
".read": "root.child($clinicId).child('permissions').child('members').child(auth.uid).val() == true"
}
}
}
What comes to my head is the following:
1- When the manger publishes the schedule, I would generate a token, add it to my permissions rules, and embed the token in the shared url as a param.
2- Then when an employee tries to access the url, I would use the token to sign in the user into the app.
I have explored claims and tokens in Firebase and I'm not quite sure which one would be the best neither this is a correct approach.
I would appreciate any insights.
Thank you in advance!
So it sounds like you want to make the data public, but harder to access.
Why don't you just include the id of the public doc in the url ? It is already a big-hard-to-guess hash. Which is all you'd be doing.
Additional security might include:
keep public docs separate from private ones, different collections
or use a flag on the doc that you can check the "visibility" of before returning to the client
I'm sure I'm missing something wrt Firebase Storage rules, but I've done the following:
STEP 1
Firstly I set the following Firebase Storage rule:
service firebase.storage {
match /b/{bucket}/o {
match /items/{dev_key}/{perm_id}/{file_name} {
allow write: if request.auth.uid == dev_id;
allow read: if request.auth.token.permId == perm_id;
}
}
}
I expected only signed in users with a custom claim permId matching the relevant location to be able to download the file, allow read: if request.auth.token.permId == perm_id;.
So, I then set a custom claim in Cloud Functions on a user as follows:
STEP 2
admin.auth().setCustomUserClaims(uid, {permId: '1'}).then(() => {
// send off some triggers to let user know the download is coming
admin.database().ref(`collection/${uid}/${itemId}`).update({
downloadReady: true
});
});
Then I signed the user out and signed back in again... which set the custom claims.
I checked that they were set in Cloud Functions as follows:
STEP 3
admin.auth().verifyIdToken(idToken).then((claims) => {
console.log("--------------claims -------------");
console.log(JSON.stringify(claims));
});
And I saw in the claims string... permID: "1"
On the client side I then requested a downloadURL (here is hopefully where I'm going wrong)... I expected this to not be the public download url but rather the download url that the Firebase Storage security rules will check:
STEP 4
var pathReference = storage.ref('items/<some-key>/1/Item-1');
pathReference.getDownloadURL()
.then((url)=>{
console.log("url: ", url);
})
The url I received from this call gave me this link
https://firebasestorage.googleapis.com/v0/b/emiru84-games.appspot.com/o/games%2FcfaoVuEdJqOWDi9oeaLLphXl0E82%2F1%2FGame-1?alt=media&token=45653143-924a-4a7e-b51d-00774d8986a0
(a tiny little image I use for testing)
So far so good, the user with the correct claim was able to view this image
I then repeated step 2, logout/login again, except this time with a permId of "0". I expected the url generated previously to no longer work since my user no longer had the correct custom claim... and the bucket location was still at the same location (bucket/dev_key/1/filename) but it still worked.
If I repeated step 4 I got a new url, which then gave the appropriate 403 error response. However the old url still worked (I guess as long as the token parameter is tacked on). Is this expected, if so, I'm not sure I understand how the Storage security rules make a difference if the download url is public anyway?
Any help clearing my foggy brain would be appreciated.
The download URL in Cloud Storage for Firebase is always publicly readable. It is not affected by security rules.
If you don't want to allow public access to a file, you can revoke its download URL.
I've recently built an app using Firebase as the data store and secured it using the security rules that only the user can read and edit their data which all works fine.
But now I want to build an admin section to list users and update details if need be, but the problem I'm running into is the fact that I cant access their data as I'm not the user. I'm seeing if its possible to allow read or write permissions to the user or admin?
UPDATE
Token generation
var tokenGenerator = new FirebaseTokenGenerator(authSecret);
var token = tokenGenerator.createToken({admin: true});
Security rule
".read": "auth.admin == true || otherauthmthod"
The method that you described above will work, assuming you update your security rules to look for the auth.admin bit. Alternatively, and likely a bit easier, is to generate an admin token, which will allow you to skip the execution of security rules entirely. This can be accomplished via:
var token = tokenGenerator.createToken({ any: "data" }, { admin: true });
See https://github.com/firebase/firebase-token-generator-node for more details.