2

How can I make sure that the username (chosen by the user via an EditText field) is unique? I am currently using a realtime database:

FirebaseAuth auth = FirebaseAuth.getInstance();
FirebaseUser user = auth.getCurrentUser();
mDatabase = FirebaseDatabase.getInstance();
User mUser = new User(user.getUid().toString(), username);
DatabaseReference allUsersRef = mDatabase.getReference("AllUsers");
allUsersRef.child(user.getUid().toString()).setValue(mUser);

(username = Username chosen by the user; User = Object which just contains the UID and username)

This produces the following structure:

MyApp
 |
 ---AllUsers
     |
     ---THE_USERS_UID
          |
          |---username: "THE_USERS_USERNAME"
          |
          |---uid: "THE_USERS_UID"

So, if someone else opens my app and has to choose a username, how can I "prevent" him from choosing a username that has been already taken by somebody else?

Cheers!

Gilgames
  • 481
  • 4
  • 15
jdstaerk
  • 882
  • 1
  • 13
  • 30
  • you can use dataSnapshot.exists() or dataSnapshot.hasChild() methods. – ugur Sep 20 '16 at 21:14
  • Calam's answer has the important parts, which is that you'll need to secure this with rules. Also see [this classic answer](http://stackoverflow.com/questions/25294478/how-do-you-prevent-duplicate-user-properties-in-firebase) and probably a lot of [these](http://stackoverflow.com/search?q=%5Bfirebase%5D+unique+username) – Frank van Puffelen Sep 21 '16 at 00:09

1 Answers1

5

You can create another branch at the location /usernames that contains children with the keys of the usernames that have been taken and parent to the $uid of the user that claimed it, e.g.

{
    "users": {
        "$uid": {
            "username": {
                "callam": true
            }
        }
    },
    "usernames": {
        "callam": {
            "$uid": true
        }
    }
}

You can now create a validation rule at users/$uid/username/$username that only allows the write if the $username has been claimed by the user's $uid at /usernames/$username/$uid.

"users": {
    "$uid": {
        "username": {
            ...,
            "$username": {
                ".validate": "root.child('usernames/' + $username).hasChild($uid)"
            }
        }
    }
}

And you can make another rule that only allows you to claim a username at /usernames/$username if there isn't already a value at that location.

"usernames": {
    ...,
    "$username": {
        "$uid": {
            ".validate": "auth.uid == $uid && !data.exists()"
        }
    }
}

ALTERNATIVELY

Database

{
    "usernames": {
        "callam": "$uid"
    },
    "users": {
        "$uid": {
            "username": "callam"
        }
    }
}

Rules

{
    "usernames": {
        "$username": {
            ".validate": "auth.uid == newData.val() && !data.exists()"
        }
    },
    "users": {
        "$uid": {
            "username": {
                ".validate": "auth.uid == $uid && root.child('usernames/'+newData.val()).val() == $uid"
            }
        }
    }
}
Callam
  • 11,409
  • 2
  • 34
  • 32
  • 4
    That ` ".validate": "auth.uid == newData.val() && !data.exists()"` in the last rules is really the key to solving this question. It prevents anyone from overwriting an existing "claimed" username, but allows the owner to still delete it. – Frank van Puffelen Sep 21 '16 at 00:06
  • http://stackoverflow.com/questions/43752247/how-to-check-already-existed-email-in-firebase-database – chris May 03 '17 at 06:14