Firebase Database paths must not contain ‘.’, ‘#’, ‘$’, ‘[‘, or ‘]’

I’m trying to minimize the amount of data downloaded for subscriber accounts. I’d like to store all of my subscribers in the path /subscribers/[emailAddress]/ on my Firebase Database. If I can’t use the email address as part of the path, I’ll be forced to download the entire list of subscribers and then search through the list for the correct subscriber. This is not ideal.

Another option would be to assign each app an id and look up the subscriber’s information using that id. I’d like to avoid that scenario so that my subscribers will be able to use any device that they are logged into.

I could use the authenticated id provided by say Google Firebase Authentication, but I want my subscribers to be able to use all Android devices like Amazon Fire tables that do not support Firebase Authentication. Using each authentication services id seems promising at first, but what if I log in on an Android phone with GFA, and then later switch to my Kindle Fire and use some other authentication, the keys won’t match up. Email addresses seem like my best bet.

I’m left with some additional problems when using the email address as part of the path:

Users may wish to change their email addresses.

Emails could be typed in upper or lower case.

Emails can’t be safely translated as the address may contain any of these characters:

  ! # $ % & ' * + - / = ?  ^ _ ` . { | } ~

A lookup table is an answer:

I’m going to add a new path to my database “/authenticatedIDs/” which will contain a list of unique keys. I can surely get away with using the epoch time combined with a string of 6 random numbers to make the lookup “key”. (see the underlined code in the box below, which is only executed when there is no key found for the email) The probability of two people getting the same key would be very very small. They would need to run the exact same code at the same second & generate the same exact 6-digit number via a random number generator. I can safely say for my purposes this will never happen.

Incremented key sound great, but I have no way to lock the Firebase DB while incrementing (without worrying about deadlocks), so there is no way to ensure that two different apps didn’t reserve the same id at the same time before it can be incremented.

I’ll use a lookup table.

Here’s my repository function for looking up keys by email address. The email lookup is in bold. Unfortunately, the code will need to look through every key searching for a match of the email address, but I have a plan to minimize the number of times this will occur:

//subscriptionRepository.kt

    fun getKeyFromEmail(email: String) {

        var newKey = database.child("authenticatedIds").get().addOnSuccessListener {
            val matchingRecord = it.children.firstOrNull() { snapshot->
                snapshot.value == email
            }
            var newKeyHolder = matchingRecord?.key ?: ""

            Log.d(TAG, "KEYLOOK UserLookup $newKeyHolder")

            if (newKeyHolder.isNullOrEmpty()){
                // this may be the first time the user has authenticated via any auth provider.
                // create a new key and store it in the firebase:
                val epoch = System.currentTimeMillis()
                val randomNumber = Random.nextInt(0,1000000)

                newKeyHolder = "${epoch}${randomNumber}"
                // write the new key to the authenticationIds path:
                database.child("authenticatedIds").child(newKeyHolder).setValue(email)
                Log.d(TAG, "KEYLOOK Created new Authentication Lookup id for ${email}, key is ${newKeyHolder}")

            }
            subscriptionViewModel.masterKey = newKeyHolder
            //write the key to the view model. it will be written back to the shared preferences in MainActivity.onStop()


        }.addOnFailureListener {
          subscriptionViewModel.apiStatus.postValue("Error finding subscriber information for $email")

        }


    }

Minimizing Lookups

To minimize the number of lookup table reads, I’ll store the last email address used and the previous key in Shared Preferences on the device and only use the lookup table when the email address changes or there is a problem looking up the record with the stored key.

The best solution that I came up with was to write the key to the shared preferences when the activity stops.

//Excerpt from MainActivity.kt
fun getPrevKey(){

    val sharedPrefs = this.getPreferences(Context.MODE_PRIVATE)
    this.prevKey= sharedPrefs.getString("lookupKey", "")
    this.prevEmail = sharedPrefs.getString("lookupLastEmailUsed", "")

}

fun storePrevKey( key: String, email: String ) {
    val sharedPrefs = this.getPreferences(Context.MODE_PRIVATE)
    // Write the new authenticated email address to the user prefs.
    with(sharedPrefs.edit()){
        putString("lookupKey", subsViewModel.masterKey)
        putString("lookupLastEmailUsed", email)
        apply()
    }

}
override fun onStop(){
        super.onStop()

        if (!subsViewModel.masterKey.isNullOrEmpty()) {
            //if the key lookup was successful, save the key, and email used to find it.
            storePrevKey(subsViewModel.masterKey, subsViewModel.userEmail.value ?: "")
            Log.d(TAG, "KEYLOOK wrote current key as previous key. ")
        }


        Firebase.auth.signOut()
    }

Note: All code is a work in progress. This is the code I’m working on, and it’s posted as I run into errors. This work is not suitable for reuse, and no guarantees are offered to its suitability for any function. If you do find something of interest, feel free to reuse it in whole or part.

Leave a Reply

Your email address will not be published. Required fields are marked *