3

I am a newbie to Firebase so any hint will be appreciated. Right now I am using Firebase Login & Auth to handle all the authentication for me. But I want to store some user data, like username DOB, created_at... I have created a users node, and I am appending children nodes to users using the unique id I get on sign up or login. However I want to ensure that there are no duplicate usernames in the system. How can I check if a username already exists before writing to the server?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Youssef Hammoud
  • 471
  • 4
  • 13
  • You'll need to set up a data structure and security rules for this. See http://stackoverflow.com/questions/25294478/how-do-you-prevent-duplicate-user-properties-in-firebase – Frank van Puffelen May 29 '16 at 14:53

2 Answers2

4

I'm currently investigating the same thing. My issue is that I also want persistence enabled, so I need to be aware of what can be accessed offline. If you're using persistence, I would also recommend disallowing particular operations such as checking username existence if your client is offline, which you can do by listening to ".info/connected" as further detailed here:

https://firebase.google.com/docs/database/ios/offline-capabilities#section-connection-state

My personal workflow for this is as follows:

  1. Login to user's account
  2. Check if the user already has a username

Check the firebase database to see if their user details includes a username:

DB/users/*userUID*/username != nil
  1. If they don't have a username, then prompt them to set a username.

When they set their username, check if the username exists in:

DB/usernames/*username*/ != nil

If it doesn't exist, then write the username and userId in the two database locations checked above.

eg.

user.uid = wewe32323

username = scuba_steve

DB/usernames/scuba_steve = wewe32323
DB/users/wewe32323/username = scuba_steve

So now you have the DB/usernames reference that you can check quickly to see if anyone has a username already, and you also have DB/users/ where you can quickly find a username for a given user.

I won't say this is fool-proof, as I still a few concerns around concurrent requests. My current issue I'm investigating is that lets say you delete the username association to a particular user, the user can depend on their local copy of the database to incorrectly assert that they are still assigned to that username.

You could look into the database write rules to disallow anyone to modify existing data (enforcing that you can only write to the DB/usernames directory if there is no existing data. This would prevent overriding of whoever sets the username first, which I think is an important step.

It may also be worth investigating Transactions:

https://firebase.google.com/docs/database/ios/save-data#save_data_as_transactions

But I believe correct write rules as mentioned in the paragraph above should allow dependable writing.

Chris Conway
  • 1,028
  • 7
  • 18
  • Further on this, I would advise storing the username in lowercase and inserting/checking in lowercase in the `DB/usernames/` directory, and the entered case in the `DB/users/` directory, that way "Scooby" and "scooby" can't be registered to two different users, but you still retain their desired capitalisation when you retrieve a user's username from their user info. – Chris Conway May 29 '16 at 06:41
  • I've implemented this more effectively into one of my more recent apps, and I would definitely recommend using Transactions. Feel free to comment or chat with me if you would like further advice on that, or on my implementation. Most of my above mentioned value and database checking methods are still relevant though, just more suitable to call within a Transaction block. – Chris Conway May 29 '16 at 13:22
  • I have documented this in more detail here: http://stackoverflow.com/q/37543872/5960425 – Chris Conway Jun 01 '16 at 20:19
1

You can check if the username is stored like this:

let username = "fred"
self.ref.child("users/\(username)").observeSingleEventOfType(.Value, withBlock: { snapshot in
        if snapshot.exists() {

If the snapshot returned in the closure exists, then the username "fred" exists.

Ivan C Myrvold
  • 680
  • 7
  • 23