Cloud Firestore rules only allow author to read document - android

I have the following document
and I have set the following rules
Every time I try to read the document using the Android SDK
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users").get().addOnCompleteListener(appExecutors
.networkIO(),
task -> {});
I am getting this error
"com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions."
I am not sure what is wrong is it the rules or the Android call.

It looks like you meant to type resource.data.author_id instead of resource.data.author_d in the rule.

This code is trying to access every document in the entire users collection:
db.collection("users").get()
If there any any document in that collection that violates the security rules you set up, then it will fail.
Did you instead intend to try to access the single document that the user is supposed to have access to?

I think you can fix this from firebase consol. You just need to sllow access to all user. As I remember by default there was allowed only authorized users.

It seems that I had to add a where close in my code
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
FirebaseFirestore.setLoggingEnabled(true);
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users").whereEqualTo("authorId",user.getUid()).get().addOnCompleteListener(appExecutors.networkIO(),task -> {});

Related

What counts as a read in Firestore?

What counts as a read in Firestore:
db.collection("Collection").document(id).get()
OR:
db.collection("Collection").document(id).get().addOnCompleteListener { task ->
task.result.get("field")
task.result.get("field")
task.result.get("field")
task.result.get("field")
}
Both. You are calling .get() method so that makes a call to Firestore servers (if offline persistence is not enabled or document is not found in cache). Just creating the DocumentReference however does not charge:
// just a reference, document data not fetched
db.collection("Collection").document(id)
In the 2nd code snippet, the task.result seems to be fetched data and you are just read a single field locally.

What is this thing is exactly called in firebase real time database

I dont know what is this thing is called but i want to get a refrence of it in android so i want to know what exactly is this thing is called , please check the image down
im taking about this line above "Quote" , whaat is this line is called and how do i get a databse refrence of it in android and really sorry for that bad handwriting though
right now i want to get refrence of 'Quote" im doing it like this
databaseReference = FirebaseDatabase.getInstance().getReference("Quotes");
but how can i get a refrence of that line above"Quote"
databaseReference = FirebaseDatabase.getInstance().getReference("What should i put here to get that refrence ?");
Seems you are using realtime database that is called firebase reference url
you can try as:
import { getDatabase, ref, child, get } from "firebase/database";
const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
if (snapshot.exists()) {
console.log(snapshot.val());
} else {
console.log("No data available");
}
}).catch((error) => {
console.error(error);
});
You can find your Realtime Database URL in the Realtime Database section of the Firebase console.
Depending on the location of the database, the database URL will be in one of the following forms: https:// DATABASE_NAME . firebaseio.com (for databases in us-central1 ) for more details on it you can access the documentation on
https://firebase.google.com/docs/database/web/read-and-write
If you want to get a reference to the root of the database, you can call getReference without any value:
rootReference = FirebaseDatabase.getInstance().getReference();
Also see the documentation for getReference() without parameters, which says:
public DatabaseReference getReference ()
Gets a DatabaseReference for the database root node.
Returns: A DatabaseReference pointing to the root node.

Unable to set data in firestore subcollections

While uploading data on cloud firestore
lateinit var db: DocumentReference
db = FirebaseFirestore.getInstance().document("users/${mAuth.uid}")
val items=HashMap<String,Any>()
items["w"] = "t"
db.set(items).addOnSuccessListener {
Toast.makeText(this,"Data updated successfully", Toast.LENGTH_LONG).show()
}.addOnFailureListener{
Toast.makeText(this,"Data upload failed",Toast.LENGTH_LONG).show()
}
works, but
lateinit var db: DocumentReference
db = FirebaseFirestore.getInstance().document("users/${mAuth.uid}/othercollection/otherdocument")
val items=HashMap<String,Any>()
items["w"] = "t"
db.set(items).addOnSuccessListener {
Toast.makeText(this,"Data updated successfully", Toast.LENGTH_LONG).show()
}.addOnFailureListener{
Toast.makeText(this,"Data upload failed",Toast.LENGTH_LONG).show()
}
fails. ie. it prints "Data upload failed".
other method of declaring document path also fails, .collection().document().collection().document()..... also fails
In such cases, the most common operation that you need to do is to log the message that comes from addOnFailureListener which tells you what is all about. Most likely is a problem related to insufficient permissions. To solve this, please set the security rules that correspond with the queries that you are performing.
The error was insufficient permissions. I was using security rules from my old project which were not defined for nested documents. Problem resolved after changing that.
For someone else' reference, always take care while changing your project apps or adding new ones, make sure you check resources so that they are applicable on new applications linked to your project.

"allow create" not working for certain collection in Firestore Rules

I am having a database in Firestore which have the number of collections for which I have defined security rules with read, create, update and delete operations.
Till Saturday noon, all the rules with given condition was allowing to read and write the data to the collections but suddenly after that in one specific collection named "locations" it giving PERMISSION DENIED exception only for creating document in the same collection but read, update and delete works.
So I changed rule for that collection like for testing as below
match /locations/{locationID} {
allow read: if true;
allow create: if true; //Condition commented...
allow update: if <condition>;
allow delete: if false;
}
and in Android Client I have coded as
database.collection("locations").add(mapData)
.addOnCompleteListener {
if (it.isSuccessful) {
//Success
} else {
//Exception
}
}
Even though, making to allow create: if true, it gives the same exception of Missing or Insufficient Permission. but when I change it to "allow write: if true" it works and document is added
When I changed collection name to test in both Rules and Client Code, it creates new document in the same collection but when I changed collection name to locationData, location and locations, it won't work.
Is this an issue in Firestore rule or What can be the solution for this?
It seems like your update rule is triggered while trying to save the dataMap to Firestore.
Please note that add() is basically a doc.set().
The behavior of set() is that it tries to overwrite the doc if it already exists and will perform an update operation thereby triggering the update rule in your security rules.
You have two options:
1) Check the data you're trying to save already exist in the locations collection.
2) Set the update rule to allow true so that you can verify that the document already exists.
Hope that helps.

Detect Firebase Auth Provider for Loged in User

How can i check that my user Loged in to my app Using which Auth Provider ? I wanted to detect that did my user loged in to my app using Facebook auth provider or using Email Provider or by using Google auth provider . I have searched this in Firebase Docs but i couldnt find any proper answer ,
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser.getProviderData().size() > 0) {
//Prints Out google.com for Google Sign In, prints facebook.com for Facebook
e("TOM", "Provider: " + firebaseUser.getProviderData().get(firebaseUser.getProviderData().size() - 1).getProviderId());
}
You can always check the list of providers as Malik pointed out. However, as you can have multiple providers linked to the same user, to get the sign in method of the current User with multiple providers, you have to check the ID token. You need to check firebase.sign_in_provider claim in the token. That will give you the sign in method used to get the ID token. To get it on the client, you need to getIdToken and then parse the returned JWT with some JWT parser.
You can use method getIdTokenResult() of your user object (firebase.User) to get IdTokenResult object, whose signInProvider property you can use to detect signin method of your logged in user.
Reached here for same issue, unable to find the provider that user used to login.
I am working on react-js app using react-firebaseui lib for readymade UI.
After a lil struggle, I simply analysed the "user" object returned by auth and in that we receive an array "providerData".
Without further analysis I decided to use:
const signinProvider = user.providerData[0].providerId;
In my case, we use only 2 providers google & password.
I get the user from "onAuthStateChanged" function as below:
import { fbAuth } from "./firebase";
fbAuth.onAuthStateChanged(function (user) {
if (user) {
console.log("authStateChanged:=====", user);
useItFurther(user);
} else {
console.log("authStateChanged: no user logged in.");
cleanUpAuthDetailsIfApplicable();
}
});
CAUTION: I haven't researched why is providerData an array and what more can be there in that array, what could be the sequence when there are more than 1 objects in that array, etc.
In my case, we had to add a condition for email validation based on provider. Like, for a particular domain email address, force user to use a specific provider.
I have been puzzled over this problem and couldnt find an appropriate solution for a long time as well. The solution turns out to be short:
String strProvider = FirebaseAuth.getInstance().
getAccessToken(false).getResult().getSignInProvider();
So, if (strProvider.equals("password")) then the authentication is by Email + Password,
if (strProvider.equals("google.com")) then the authentication is via Google,
if (strProvider.equals("facebook.com")) then the authentication is via Facebook.
Addition
However, with this one-liner you can get an exception wchich can be prevented by adding OnSuccessListener like so:
mAuth = FirebaseAuth.getInstance();
mAuth.getAccessToken(false).addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
#Override
public void onSuccess(GetTokenResult getTokenResult) {
strProvider = getTokenResult.getSignInProvider();
}
});
Alternative
The getProviders() method list is sorted by the most recent provider
used to sign in. So the first element in getProviderData() is the
method that the user used to sign in.
Also the reason why
FirebaseAuth.getInstance().getCurrentUser().getProviderId() returns
firebase is because a backing Firebase Account is always created
irrespective of how the user signs in. This helps with linking and
unlinking of accounts where users may want to attach more than one
credential (however your view of the FirebaseUser has not changed).
as mention this post.

Categories

Resources