how to get data from cloud firestore where user.uid equal to document id in flutter? - firebase

I am Having this profile screen which shows users info.
after user authenticated I am storing data in cloud firestore with document id is equal to user-id.
Now, I want to retrieve data from cloud firestore with having current userId is equal to document id.
For now i have this :
class UserManagement {
getData() async{
String userId = (await FirebaseAuth.instance.currentUser()).uid;
print(userId);
return Firestore.instance.collection('users').document(userId);
}
but this is not working properlywhen i log out and after re-login with different user it showing me same data.
UserManagement().getData().then((results) {
setState(() {
userFlag = true;
users = results;
});
});
Now, how get other fields like name,email,course,phonenumber..etc
and all values all storing into user.right?

If the document id in your firestore is equal to the userid in the Firebase authentication console, then you need to retrieve the uid first and pass it as an argument to the method document():
getData() async{
String userId = (await FirebaseAuth.instance.currentUser()).uid;
return Firestore.instance.collection('users').document(userId);
}

Your query is fetching all of the documents in the "userData" collection, then picking out the first document from that entire set. This will be the same set of documents for all users that have read access to that collection. I don't see why you would expect a different result for different users. Perhaps you meant to access a single document for a user given their user ID, instead of all of the documents. If that's the case, you should request that document by its ID with Firestore.instance.collection('userData').document(uid)' whereuid` is the ID of the currently signed in user.
Also, your code is querying a collection called "userData", but your screenshot shows a collection called "users", so that is confusing.

Related

Find document ID if the query know two of his fields, firestore rules

I'm trying my best to set up a functionality in my application that will allow people who know a group's login and password to join it.
Each group has a document in the "Groups" collection, and each user has a document in the "Users" collection.
To keep the id and the password information, I have another collection named "AuthGroups", containing as many documents as there are groups, with two fields: "login" and "password". Each auth document has the same ID as the corresponding document the Groups collection.
So, here is my strategy:
When the user valid the login and password, a first query is sent to the database, to find a document with theses credentials in the "AuthGroups" collection.
If a document is found, its ID is used to do another query in the "Groups" collection to retrieve the group's data.
Queries could look like this:
var ID = await firestore.collection('AuthGroups')
.where('login', isEqualTo: login)
.where('password', isEqualTo: password)
.get()
.then((value) {
return value.docs.first.id;
});
var groupName = await firestore.collection('Groups')
.doc(id)
.get()
.then((value) {
return value.get('name');
});
Now, let's speak about firestore rules to make it secure...
To prevent someone malicious from seeing all documents in my "AuthGroup" collection. I told myself that my rules need to only allow queries containing both "login" and "password" fields. But I don't know how to do it right, and if it's even possible...
Same thing for the documents in the "Groups" collection: users can only get a document if they know its ID.
A solution could be to name my documents in my "AuthGroup" collection like "login + password", and store the group's ID in it. And in my rules, allow only list requests like that:
service cloud.firestore {
match /databases/{database}/documents {
match /AuthGroup/{organization} {
allow list: if request.auth != null;
}
}
}
I told myself that my rules need to only allow queries containing both "login" and "password" fields. But I don't know how to do it right, and if it's even possible.
It's not possible. You can't check for specific query parameters in security rules.
A solution could be to name my documents in my "AuthGroup" collection like "login + password", and store the group's ID in it. And in my rules, allow only list requests like that
Yes, that is a possible solution. Or you can hash a concatenation of login and password strings so you don't expose them in the document id or exceed the max length of that ID.

Firestore onAuthStateChanged and users in a subcollection

I'm building an app where users can authenticate, in Firestore I save extra data from that user (username, age).
Now in my app, users are coupled to events, I chose to have an events collection, which has a users subcollection.
I'm using the firebase onAuthStateChanged listener to see when my user has logged in. However the issue I'm not facing is, to get the firestore data for my user, I need to know which event this user belongs to, which is of course, data I do not have access to at the time the user signs in, for example:
const onAuthStateChangedPromise = new Promise((resolve, reject) => {
auth.onAuthStateChanged(async firebaseUser => {
if (firebaseUser !== null) {
const user = await getDoc(doc(db, 'events/${eventId}/users', id))
useAuth().user = user
return resolve(user)
}
return resolve(null)
}, err => {
reject(err)
})
})
In the example above, to get my user's data, I need to know the eventId, which I can not possible determine from the authenticated user.
I'm wondering how to achieve this?
I could save the eventId in localStorage as soon as the user registers, but that can cause issue's, since the complete app then relies on something being set on localStorage
The typical way to solve this would be to add the UID of the user in a field inside the events/${eventId}/users documents and then use a collection group query across all users collections. This will give you a list of all event/users docs for that user.
To find the event for such an event/user doc, you first take the DocumentReference for the DocumentSnapshot and then go up the parent chain twice to get to the parent event document.

Unique field in Firestore database + Flutter

I'm trying to implement a normal authentication system in my app, but I'd like to create a new field for each user that is the "uniqueName" so users can search and add each other in their friends list. I was thinking of adding a textField in the signup form for the uniqueName and updating my User class adding a new String in this way:
class User {
String email;
String name;
String uniqueName;
String userID;
String profilePictureURL;
String appIdentifier;
...
}
Now, since I have this method for the email&password signup:
static firebaseSignUpWithEmailAndPassword(String emailAddress,String password,File? image,String name,) async {
try {
auth.UserCredential result = await auth.FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailAddress, password: password);
String profilePicUrl = '';
if (image != null) {
await updateProgress('Uploading image, Please wait...');
profilePicUrl =
await uploadUserImageToFireStorage(image, result.user?.uid ?? '');
}
User user = User(
email: emailAddress,
name: name,
userID: result.user?.uid ?? '',
profilePictureURL: profilePicUrl);
String? errorMessage = await firebaseCreateNewUser(user);
if (errorMessage == null) {
return user;
} else {
return 'Couldn\'t sign up for firebase, Please try again.';
}
}
how do I have to modify it in order to add this new field in the registration? Since I have to check that the uniqueName insert by the user is effectively unique before creating a new user in the database, what can I do?
Furthermore, I think that it would be cool if this check is made concurrently to the filling of the form, how can I do it? (this is not necessary)
Thanks everyone for the answers
You have to save your users in a collection, then check if uniqueName already exists in the collection. If it exists, return error.
Then when a new user account is created, save the uniqueName.
// this function checks if uniqueName already exists
Future<bool> isDuplicateUniqueName(String uniqueName) async {
QuerySnapshot query = await FirebaseFirestore.instance
.collection('PATH_TO_USERS_COLLECTION')
.where('uniqueName', isEqualTo: uniqueName)
.get();
return query.docs.isNotEmpty;
}
// call the above function inside here.
static firebaseSignUpWithEmailAndPassword(String emailAddress, String password, File? image, String name,) async {
if (await isDuplicateUniqueName(name)) {
// UniqueName is duplicate
// return 'Unique name already exists';
}
// ... the rest of your code. Go ahead and create an account.
// remember to save the uniqueName to users collection.
I suggest doing the following steps:
Create your own users collection (for example users) in Firestore, which you might have done already. (I don't think that User is a good class name, since Firebase Authentication is using the same name. Try MyUser or something.)
Add authentication triggers that will ensure that whenever a Firebase user is added or deleted, it will also be added to or deleted from users collection, use Firebase uid as identifier.
Create a solution to check whether a uniqueName already exists in users collection. You can use a Firestore query, but in this case you have to allow unauthenticated access to read users, at least uniqueName field. (Since the user is not authenticated yet at this point.) A Firebase Cloud Function is another option.
When users enter their desired uniqueName, run the check before creating Firebase user. You can do it when user enters this or when you start the signup process.
If uniqueName is unique, you can try to create Firebase user. Be aware, this step can also fail (for example e-mail name taken etc.). Your users document will be created by the authentication trigger you set up in step 2.
Finally, you have to store this uniqueName in users collection. At this point you will have uid of the newly created Firebase user, so you can use Firestore set command with merge option set to true, so you don't overwrite other data.
It is important to note that you can't guarantee that the Firebase trigger already created the new document in users by the time you arrive to point 6, it is very likely that the trigger is still working or not even started yet. That's why you have to use set both in the authentication trigger and in your own code that sets uniqueName: which "arrives" first, will create the document, and the second will update it.
Also, for the same reason, you have to allow inserts and updates into users collection with Firestore rules. This might sound a little scary, but keep in mind that this is only your own user list to keep track of uniqueName, and authentication is based not on this, but on Firebase Authentication's user management which is well protected.
Last comment: this is not a 100% solution. It is quite unlikely, but theoretically can happen, that some else reserves a uniqueName between you check whether it's unique and the user is actually created. To mitigate this, it is a good idead to make the check just before Firebase user is created. Even in this case a slight chance remains for duplicates.

I want to CRUD the value of the FireStore in the method

・ What I want to do.
I have stored the document ID of the currently accessed room in user collection>document>field>documentID.
I want to retrieve it and rewrite the document in the room collection.
But I can't rewrite it.
I want to know how to get the data and rewrite it.
[The document ID in the user collection is the user ID.]
I want to retrieve the field in the following method
void _onConferenceTerminated(message) async {
//[Image 1] I get the documentID being accessed from the documentID in the field of the user collection
final user = FirebaseFirestore.instance.collection('user').doc(uid()).get();
final getDocId = user.data['documentID']
//[*image2]Use it to access the room document and reduce the roomCount
final setRoomCount = await FirebaseFirestore.instance.
.collection('room')
.doc(getDocId)
.set({'roomCount': roomCount - 1});
}
//Get the user ID
String uid() {
final User user = FirebaseAuth.instance.currentUser!
final String uid = user.uid.toString();
return uid
[image1]
[image2]
You could use .update() instead of .set() if you want to change only one field. I also recommend you to use FieldValue.increment(value) which is built-in function in firestore.
Value could be both int and double.
FieldValue.increment(1)
FieldValue.increment(2.0)
Also, you can use negative numbers to decrease the value. In your case, you can try the below code.
await FirebaseFirestore.instance
.collection("room")
.doc(getDocId)
.update({"roomCount": FieldValue.increment(-1)}
Documentation: https://firebase.google.com/docs/firestore/manage-data/add-data#increment_a_numeric_value

How to add a document to a collection in cloud firestore

I have a collection called 'users'. I'm trying to add a user to the collection after Google authentication but I keep getting the following error:
FirebaseError: [code=invalid-argument]: Invalid document reference. Document references must have an even number of segments, but users has 1.
Here is the code
this.googlePlus.login({
'scopes': '',
'webClientId': environment.googleWebClientId,
'offline': true,
})
.then(user => {
// save user data on the native storage
const userRef: AngularFirestoreCollection<User> = this.afs.collection<User>(`users/`);
const data: User = {
email: user.email,
displayName: user.displayName,
uid: user.uid
};
userRef.set(data)
.then(() => {
this.router.navigate(['/home']);
Google+ is being discontinued so you should look at Firebase Authentication, or GCP's new Cloud Identity Platform.
In the case of Firebase Authentication, you must listen to the .onAuthStateChanged observer. Once it fires off your user object, you then take that and write a new user document to a users collection in Firestore. Best practise is to use the uid of the firebase.auth().currentUser.uid as the user document ID in your users collection.
Your userRef refers to a collection, and the type of object is called a CollectionReference. You're attempting to call set() on it with some object that should become a new document in that collection. But that's not the way it works. Instead, it looks like you want to call add() to add a new document with a new random ID.
If you somehow already know the ID of the new user document, you should build a DocumentReference with that id, then use set() on that DocumentReference to create the document.

Resources