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: "test@test.com"
            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

upvote
  flag
So you want the Firebase rules to be checked on the Android client? – Da-Jin C
upvote
  flag
Yes, I want to check first if username is taken if firebase tell me no then create an account otherwise just ask the user to pick another username – FloGz
1 upvote
  flag
Aside from my answer about data structure and security rules, here are some questions where the topic has been covered before: //allinonescript.com/questions/29970681/…, //allinonescript.com/questions/25294478/…, //allinonescript.com/questions/15910165/…, //allinonescript.com/questions/20260476/… – Frank van Puffelen

3 Answers 11

up vote 35 down vote accepted

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: "test@test.com"
            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.

1 upvote
  flag
great answer, duplicative with //allinonescript.com/questions/25294478/… – Creos
4 upvote
  flag
Hi 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 the users/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. – Chris Conway
upvote
  flag
@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? – Relm
upvote
  flag
Thanks 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? – jdstaerk
upvote
  flag
Have an inverted data structure that maps uid to username and then verify that uid -> username === username -> uid. – Frank van Puffelen
upvote
  flag
@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 – Tope
upvote
  flag
Thanks, 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 – Dominic Tobias
upvote
  flag
Use a transaction to update the username and you'll be certain what happens. – Frank van Puffelen
upvote
  flag
@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? – Newbie

I dont know much about firebase security yet, but I may have solved the problem using Java. I have posted it below.

my data structure is

myapp
{
  users: {
          <unique generated-id>
          { username: "example.username" }
}
}


public boolean isUsernameExists(final String enteredUsername) {
        final Boolean[] isExist = {false};
        FBref.child("users").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
                    String existingUsername = (String) userSnapshot.child("userName").getValue();
                    if (existingUsername.equals(enteredUsername)) {
                        isExist[0] = true;
                    }
                }
            }
            @Override
            public void onCancelled(FirebaseError firebaseError) {
                //some error thrown here
            }
        });
        return isExist[0];
    }

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
        }
    }
});
upvote
  flag
You set it in the username list but not in the uid. Should we add a line on complete to do that? – user2997154

Not the answer you're looking for? Browse other questions tagged or ask your own question.