Firebase android : make username unique
Solution 1
Part of the answer is to store an index of usernames, that you check against in your security rules:
app : {
users: {
"some-user-uid": {
email: "[email protected]"
username: "myname"
}
},
usernames: {
"myname": "some-user-uid"
}
}
So the usernames
node maps a username to a uid. It essentially reads as "username 'myname' is owned by 'some-user-uid'".
With this data structure, your security rules can check if there is already an entry for a given username:
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
}
This validates that the username isn't claimed by anyone yet OR it is claimed by the current user.
Solution 2
Save usernames as suggested by Frank but when you save usernames, use runTransaction function in Firebase to make sure that the username is not taken. This function is guaranteed by Firebase to be an atomic operation so you can be rest assured of no collision
firebaseRef.child("usernames").child(username).runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.getValue() == null) {
mutableData.setValue(authData.getUid());
return Transaction.success(mutableData);
}
return Transaction.abort();
}
@Override
public void onComplete(FirebaseError firebaseError, boolean commited, DataSnapshot dataSnapshot) {
if (commited) {
// username saved
} else {
// username exists
}
}
});
FloGz
BY DAY : Android developer BY NIGHT : App developer FOR FUN : Football player, The Walking Dead Addict "When opportunity knocks, some people are in the backyard looking for four-leaf clovers" My app : https://play.google.com/store/apps/details?id=com.app.floriangz.droid404
Updated on November 04, 2021Comments
-
FloGz over 2 years
Parse will shut down at the end of the year, so I decided to start using Firebase. I need to implement a register process with 3 fields : email, username, password (Email & username must be unique for my app).
Since, Firebase is not providing an easy way to manage username like Parse, I decided to use only the email/password registration and save some additional data like username. Here is my users data structure :
app : { users: { "some-user-uid": { email: "[email protected]" username: "myname" } } }
But, what I want to do is to make the username unique and to check it before creating an account. These are my rules :
{ "rules": { ".read": true, ".write": true, "users": { "$uid": { ".write": "auth !== null && auth.uid === $uid", ".read": "auth !== null && auth.provider === 'password'", "username": {".validate": "!root.child('users').child(newData.child('username').val()).exists()"} } } } }
Thank you very much for your help
-
Creos almost 8 yearsgreat answer, duplicative with stackoverflow.com/questions/25294478/…
-
Chris Conway almost 8 yearsHi Frank, I think it would also be important to note that you should probably cast all checks to
usernames
to lowercase and store lowercase, that way you can't have identical usernames of varying case. The value stored in theusers/some-user-uid/username
field can be case sensitive, that can be the readable version, which should allow for a more user friendly username selection and case adherence. -
user2997154 almost 8 yearsYou set it in the username list but not in the uid. Should we add a line on complete to do that?
-
Relm over 7 years@Frank van Puffelen can you confirm what Viv is saying on his answer about using a Transection instead of a normal set query? And also, any regex you've written before that can be used to validate allowed characters for user names?
-
jdstaerk over 7 yearsThanks for your awesome answer! It really helped me. But there is still a problem I guess.. In your current implementation, the same user can claim as much usernames as he wants (if they are not taken). So he can basically create username1 till usernameN. How can we achieve, that the old username gets deleted before he claims his new one?
-
Frank van Puffelen over 7 yearsHave an inverted data structure that maps
uid
tousername
and then verify thatuid
->username
===username
->uid
. -
Tope about 7 years@DDerTyp to avoid leaving behind old usernames that are no longer used do the following updates as a transaction (inherent in the multi-update feature of firebase) ` Simultaneous updates made this way are atomic: either all updates succeed or all updates fail.` firebase.google.com/docs/database/web/read-and-write update1: remove entry from usernames/$old_username:$userid update2: update entry in users/$userid/username:$new_username update3: add entry in usernames/$new_username:$userid
-
Dominic almost 7 yearsThanks, will the validation give back a response of which field failed validation? Otherwise you could not give a meaningful response in the UI to the user and the check would have to be separate
-
Frank van Puffelen almost 7 yearsUse a transaction to update the username and you'll be certain what happens.
-
Newbie over 6 years@FrankvanPuffelen Your answer is great. But would this structure help me implement a username and email login structure? Wouldnt it be better if I stored username : { some_username : some email} so that I can retreive the user's email and log him in. Also if my question is correct, what security rules would I use to make sure that it is only readable and writable during username creation?