Firebase: How to handle users with same displayNames when they sign up with Google Provider? - firebase

I have an app where people can sign up for user accounts. They can use their Google account or sign up using email/password. I am storing the user's displayName in Firestore so I can reference it throughout the app as their username.
However, there is a problem:
Let's say "John Doe" signs up for a user account on my app and he uses his Google account named johndoe#gmail.com. His Google account's display name is also "John Doe". However, a few days later, another with the same name "John Doe" signs up for a user account, and although his Google account email may be different (let's say, johndoe2#gmail.com), his display name is still the same. Now, I have 2 users in my app with the same display name "John Doe". This causes an issue when I am trying to create user name routes such as: https://myapp.com/JohnDoe and handling user profile lookups.
What is the recommended way to allow users to use their Google accounts if there are other users that may have the same display names? The catch is I don't want to store UIDs in the URL as I want it to be clean with just the displayName.

The catch is I don't want to store UIDs in the URL as I want it to be clean with just the displayName.
It might be clean, as long as the number of characters within the displayName is reasonable. In your example, "John Doe", contains only 8 characters, including the whitespace character between the names, which is fine. But I've seen so many examples of names that are larger than 28, the number of characters that exist in a UID. Since the displayName property is set within the Google account and it can be changed only by the user, you're having three solutions left.
The first one would be to create your own mechanism for setting specific user names into your application. These user names can be set especially by the users. It can be the same as the names in the Google account or not. However, when someone chooses "JohnDoe" and a second user wants to set the same user name, it won't be possible, since a "JohnDoe" is already present. So before setting a new user name, you should always check if that one is already present. You can do that because it's something that you control. When using Firestore, this can be simply done by using:
db.collection("users").whereEqualTo("userName", "JohnDoe");
Or when using the Realtime Database:
db.child("users").orderByChild("userName").equalTo("JohnDoe");
Now, the first "John Doe" will have a profile that looks like this:
https://myapp.com/JohnDoe
While the second one might have something like this:
https://myapp.com/John_Doe
See the underscore? This kind of mechanism is very widely used. Facebook, Instagram, Twitter, eBay, Reddit, and many more do that:
https://www.facebook.com/JohnDoe/
https://www.instagram.com/JohnDoe/
https://twitter.com/JohnDoe/
https://www.ebay.com/usr/JohnDoe/
https://www.reddit.com/user/JohnDoe/
The second one would be to ignore the user names and use only the UIDs that come from the authentication process. Case in which, your URL will look like this:
https://myapp.com/TwentyEightCharactersLong
That's not unusual, since other big apps use it:
https://www.imdb.com/user/$userId/
The third one would be to create a combination between the UID and the user name. The best example would be Stackoverflow:
https://stackoverflow.com/users/$userId/$userName/
// ^ ^
Or LinkedIn:
https://www.linkedin.com/in/$userName-$userId/
// ^ ^
Doesn't matter which solution will you apply, you'll always have unique URLs. But it's up to you to choose which one of these solutions seems more clear to you.

It's quite common to see URLs suffixed with some random number or combination of alphanumeric characters like john-doe-1 and so on. That being said you would have to implement logic for this yourself maybe using Firebase Auth Triggers for Cloud Functions which will run whenever a new user is created and you can add an URL for their name.
You could also add some random string like /users/john-doe-qwerty and maybe add a paid feature that allows user to set their own URLs (if applicable for your application) i.e. vanity URLs.

I recently had this issue too, after playing around with 2 separate Google Play accounts, I found that the user.UserId is static and unique, and while the user.DisplayName can be changed, display names are unique.
Therefore, you should be safe storing data under UserId and then grabbing their current display name on login. Alternatively, to match your wants, you can save the data under the display name but you might want to track their user identification code in case they change their display name (if you wish to accommodate that)

Related

manually validate/accept users flutter firebase

so im building an app which has 2 users(staff and student) and the staff must accept/verify users manually. thereby when the student registers/signs up this would show up as a request on the staff side then after the staff accepts or rejects the request which allows the studentto enter the app. i've already coded the sign-in sign up pages which looks very similar to this using firestore authentication.
the only solution i could think of is adding the user sign up info as a request and saving it under a firestore collection("registration requests") i though about how the password field might be an issue but i found that people used the flutter_string_encryption package to save the password as a .doc field. then when the staff accepts the request the method/ function .createUserWithEmailAndPassword(email: email, password: password) would be called but i don't know if this is the "correct" way of achieving this.honestly i have no idea how to tackle this problem. is there is any resource that tackle the same issue or any other way this could be archived?
You could implement a system whereby any student can create an account using .createUserWithEmailAndPassword() but the account has an associated flag/boolean which prevents them from viewing content until it is true.
E.g
Student creates account using .createUserWithEmailAndPassword().
Upon creating new user, create a FireStore document containing a boolean set to false if the account is unverified.
Allow teachers to alter the FireStore record for students and change the boolean value to true upon verification.
Dynamically display content to users based on wether the boolean in their FireStore record is set to true or false.
If this sounds feasible, I can provide more detail on the specific code if you're not familiar with the packages.

Firestore Security Rules Shared Document by Group

I have a situation where a user can create a doc and then share it with a group of other users. They could share it to multiple different groups. I don't know how to set a rule for this.
Here is the database structure:
So in the group you have a list of docs that have been shared to it. My app loads the group that a user is in, then wants to load all the docs in the documents array. I need a way server side to say that this is OK. Up until now only the owner of the doc can read it.
I put a field in each doc that contains ids for each group its shared to. I think I want to say "check if the user is a member of any groups in the sharedToGroups" list but I can't work out how to do that unless I maintain another list somewhere say in the userProfile doc that has a list of circles the user is a member of. Even then I'd be trying to compare 2 lists and I'm not sure I can do that client side.
It would be nice to be able to get the group Id somehow from where the request is being issued from and just see if that is in the sharedToGroups array.
Any help or comments on how this can be achieved would be greatly appreciated, maybe it needs a different db structure.
You can try an approach of this sort:
I am not sure if this will help you but off the top of my head maybe you could enable permissions on firestore for the group document. As in, in the rules, for the group set up a function that validates the user with the user ID stored in the document with the ID attached in the auth via the firebase auth
Therefore, rather than trying to restrict access per document, restrict access per group.
I'm going to answer my own question. Not sure if its the correct protocol here (not a professional programmer or experienced Stack Overflower) but it might help someone.
I ended up adding a field in the user_profiles document that has a list of each group they are in. This list needs to be maintained as I create and add / remove people from groups along with the members list in the group itself.
The benefit of this is that I can use the users id from the request object to get that document from the data base in the security rule. I then have a 'sharedToGroup' array in the doc I'm trying to access and a "inGroups" array in the user_profile that I can access also. Then I use the hasAny operator to compare the two arrays and allow access if the sharedToGroup array has any values from the inGroups array.
My rule becomes:
match /_group/{groupId}{
allow create: if isSignedIn();
allow read: if isOwner()
|| resource.data.sharedToGroup.hasAny(get(/databases/$(database)/documents/user_profiles/$(request.auth.uid)).data['inGroups']);
allow write: if isOwner();
}
Only thing left to do is to secure the user_profiles doc to make sure not even the user can write to it since I don't want someone manually adding groups into their array.
I hope this might help someone someday - like I said I'm a not a pro here so take it with a grain of salt.

How to structure a like query in Firestore?

I'm building a simple application where posts appear on a user's home page and he can like or unlike them.
first when there were no users in the applications, I made a simple boolean field "liked", as shown in the figure, to determine if a post is liked or un-liked. However, when I started working with users, I find it hard to find the perfect structure for the likes field.
In Firestore, I added a field named "likedBy" for each post, which contains a map with a key of each user's id and a boolean to determine if the user liked the post or not.
I don't know if this structure is suitable or not, and if not, is there a better way to reach my goal?
The likedBy field is enough to cover most use cases.
Just store in likedBy an array of all users who liked the post by user ID. When a user likes - add the user ID to the array and remove it upon unlike.
That means you can also remove the likes and liked field as you can get it from reading the likedBy array.
I would recommend going one step further and save more than the User ID. You could also save the User displayName and photoUrl. That way, you don't need to read ($$$$money$$$$) the documents of every Users who liked Posts in order to display their name and/or avatar.

Best Practice to keep user data in firebase firestore?

I am using firebase as a backend for my Android App. And this app is a social media app where users can post, comment & like. so I am storing user data in user_collection and this collection is secured by security rules where if uid == auth.uid (any user can only access data of himself).
Now in this app when a user post something every user can see this post. And in post_collection I am saving userId in post_doc.
So the problem is I need to show name of user to other users and I have only userId but the problem is a user can't get name of other user by uid beacuse of security rules. now I have to solutions for this please tell me which one is better or you can also suggest any other solutions also?
I can use cloud functions getUserNameById() (Problem : I need to call this function very frequently in feed when user scroll)
I can store name also in post_doc (problem : when user changes his name then It will show old name in old post)
Thanks for you kind help
In a scenario like the one you describe, I would typically store the user name in each post doc. I would also ignore updates to the name, as I think of the user name in the post doc as a historical value: this is the name the user had when they posted this. Now you may want different behavior of course, in which case I recommend reading: How to write denormalized data in Firebase
Your approach with Cloud Functions is fine too, and quite common in some situations. But I tend to only use Cloud Functions for reading data, it the read operation itself is particularly complex, which isn't the case here. In a case like this, I'd recommend coming up with a data model that allows the use-case and security you want.
For example: if you create a collection usernames where each document has the UID as its document ID, and then contains a single field with the username for that UID, you could implement the lookup of the user name through Firestore.
So you could have:
Store the full user profile in /users/$uid.
Store the user name in /usernames/$uid.
Have a Cloud Function that triggers when /users/$uid is written and that updates /usernames/$uid.
The client then has read access to each /usernames/$uid document, or even to the entire /usernames collection in one go if needed.
This way the names can be cached on the client, and continue to work when the app is offline, unlike in your approach with a Cloud Function that looks up the user name.
Consider the solution: whatever public data you need (author name, author userpic link etc) just save it with the post at the time it had created.
So your Message Pojo will looks like:
id
authorName
text
etc..
and just display this name (authorName).
It will be the bad way to go any time to User_collection folder to take the name even if there are would be not strict security (becouse it takes time and document reads)

How to handle different-case usernames in Firebase?

I'm having an issue with Firebase DB involving case sensitive keys. For example: I create a "Username" key for every new user that registers. I'm validating this "Username" value through regex and also checking if the value entered already exists in the database (checking username availability). My issue is that I just realized Firebase assumes different sentence case of the same value is a different value.
For example:
"Username": john and
"Username": John are seen as two different/unique usernames
I was thinking taking the user's desired username input string and making it all caps (or all lowercase), creating uniformity in the database, but then it would kill the ability of having a mixed-case username. Is there a way to bypass this?
I had the same issue using firebase. The solution was create a new property with lowercase for "searchable" fields on my system. Also I remove the accents from words on this property.

Resources