Module 0274: Firebase in Android App

Tak Auyeung, Ph.D.

March 22, 2017

1 About this module

2 What is a Firebase database?

Firebase is a non-relational method to store and manage back end data.

From the data perspective, a single Firebase “database” is a hierarchical value-pair look up tree. A value-pair look up mechanism is also called a hash in Perl, a associative array in PHP, a Map in C++, a dictionary in Python, a key-value pair list in AppInventor. It is important to note one key difference, however. A Firebase database is persistent as a cloud resource, as opposed to the mentioned language features that are all volatile.

In other words, a Firebase database is not divided into tables, tables into rows. A Firebase database is much more flexible and hierarchical. Currently, A Firebase database may have up to 32 levels of hierarchical structure. Furthermore, as a cloud resource, a Firebase database can also consume a huge amount of space and still work efficiently.

In addition to the data aspect, Firebase is also an app, meaning that it is programmable. “Cloud Functions” written in JavaScript can be triggered by various means and the code be executed in the cloud (not on an Android device).

Firebase is also more, including storage, web hosting and various other cloud centric services. This article focuses on the database aspect and touches on authentication.

3 Flow to making an Android app that uses Firebase

3.1 Create a signed shell app

There is no need to develop much code first. The key is to make sure the project is signed, and there is a certificate corresponding to this process. This is important because an Android app that makes use of Firebase needs to authenticate to Firebase using its certificate.

3.2 Create a Firebase

This step may be optional if the new Android app is sharing the same Firebase as one that is already existing.

First, go to the Firebase home page. You have to sign in first to use Firebase. Sign in first. Note that it seems you can use the apps.losrios.edu account. Click “Get started for free”. In the following screen, click “create new project”. Then give your project a name.

3.3 Link a Firebase to an Android app

In Android Studio, click “Tools—Firebase”. Click “Realtime database— Save and retrieve data”. Then follow the steps in sequence.

To connect to Firebase, Android Studio open a Chrome instance so sign in and ask for permissions. After reviewing all the permissions, click “Allow”. Once you sign in and allow the permissions, Android Studio takes over again and ask which Firebase to connect to. Since one should be created by now, click “Choose an existing Firebase or Google project”, then select the Firebase. Then click “Connect to Firebase”.

Next, click “Add the Realtime Database to your app”. This step will prompt you to accept changes to both the root and app level build.gradle files. Click “Accept Changes”.

At this point, the Android app is connected to the Firebase database. However, because no authentication is set up yet, the application by default has no access. In the Firebase Assistant, click back (to “Firebase”) and set up authentication.

Step 2 of the assistant adds Firebase authentication to the Android app. Again, Android Studio prompt for acceptance to changes to the app level build.gradle file. Click “Accept Changes” so that Android Studio can proceed. This step only enables authentication, but it does not enable individual authentication providers (like Facebook or GMail). To do that, click the “Firebase console” link in Android Studio Assistant.

Once in the browser, click the Firebase that is going to be configured. In the Overview pane, click Authentication, then click “set up sign-in method”.

By default, all providers are disabled. To enable, hover over the row corresponding to the provider and click the edit (pen) icon. Then slide the “Enable” slider to enable. Note that for some sign-in methods, the SHA1 fingerprint of the Android app is needed. Click “SHA1 fingerprint” to figure out how to get the finger print.

The involves running the command, copy the SHA1 finger print to the clipboard. Then, in Firebase console (in the browser), click the configure (gear) icon next to “Overview”. Go to “Project settings”. Under the “General” tab, scroll to the bottom and find the Android app that is linked. Then click “add fingerprint” and paste the copied SHA1 finger print.

Note that one SHA1 finger print may be listed already, this is the debug finger print that allows the app to be debugged. You need to add the release finger print in order for released APKs to work.

The rest of the Assistant screen shows the basic code to make use of basic email/password authentication.

3.4 Which providers to enable?

While there is the great flexibility to select which providers to enable, a typical end user has too many accounts to manage already! It is logical that most end users may want to consolidate authentications to just a few providers instead of tracking one user name and password per app or per web site.

4 The sequence of events to use Google Sign In

In onCreate of the activity, start with calling FirebaseAuth.getInstance to get an instance of a FirebaseAuth object. This object specifies and registers several callbacks to handle important events related to authentication such as sign in, sign out and user change. A FirebaseAuth object is designed work from the UI/main thread.

Next, set up a AuthStateListener object to get notified when there is a authentication state change.

Another key object that can be created in parallel in the onCreate is a GoogleSignInOption object. As the name of the class implies, an object of this class stores and manages options related to a Google sign in. In a Google sign in session, various information of the user can be requested, and it is up to a GoogleSignInOption object to track such requests.

After sign in options are specified, the next object to create is an object of the class GoogleApiClient. The creation of this object consumes the GoogleSignInOptions object. Note that the Google API consists of many components, Google Sign In is only a part of the Google API. This is why the construction of a GoogleApiClient object requires a method call (of its nested Builder class) to specify which API(s) should be added to the API client.

At this point, there is no actual authentication. However, we have two important objects created. The first is a FirebaseAuth object that represents a Firebase database authentication session, the other one is the GoogleApiClient object that represents the Google Sign In session.

The actual sign in process is either automatic or manually triggered. Either way, it is done by using an Intent object to delegate the Google Sign In process to another activity. The specification of this Intent object needs the use of the GoogleApiClient object created earlier. The activity started by this Intent object has a result that should be processed.

Using the onActivityResult method of an activity, a requesting activity can be notified when a requested intent is fulfilled and a result is available. At this point, the GoogleSignInResult is available to be processed. This object is acquired by using the Intent object as a parameter in the call to Auth.GoogleSignInApi.getSignInResultFromIntent.

This result object indicates whether the Google Sign In was successful. If so, we can then get a GoogleSignInAccount object from this result, representing the actual account. Using this account, a AuthCredential object is created from calling GoogleAuthProvider.getCredential, using an ID token from the account object.

This may sound very convoluted because since we have an account, why not using the account object itself to sign in to Firebase? We will explain the security aspect of this process in the next section.

Once we have a AuthCredential object, then we can finally sign in using the FirebaseAuth object created in the first step to finish the process.

5 Security considerations

The Google Sign In process seems exceedingly complex because there are many “parties” involved, and trust is only established among some of the parties.

5.1 Google API needs to know the identity of the client

This is the first security concern that is not even addressed by Android app coding. The Google API server (which serves Google Sign In) does not communicate with a client that cannot be authenticated. As a result, the first step to configure an Android App is to choose a certificate and specify the SHA1 finger print of the App in the Google Sign In feature of the Firebase database.

You also need to modify the Gradle file to include the compilation of com.google.firebase:firebase-auth:... as this package supplies many of the classes needed. To use Google Sign In, then the com.google.android.gms:play-services-auth:... package also needs to be compiled.

5.2 Keep the app out of the loop

Once Google API can authenticate the source of request via the SHA1 finger print, it still does not trust the requesting App itself. Google Sign In can return a lot of information about the authenticated user, and as a result Google API needs to be careful about how much the app can get.

This lack of trust is handled by several methods. First, the app never gets the user name or password. Recall that the actual sign in is handled by a system-owned activity started by an intent. This system-owned activity is the actual UI that handles the user name and password. It is also responsible to communicate with Google API using the GoogleApiClient object prepared by the requesting app (it contains the credential of the requesting app, along with the specification of Google Sign In is one of the APIs requested).

When the system-owned activity gets sign in result, it is in the form of a token. This token is known on the Google API side to represent a particular account. To the sign in request app, this token is like a random sequence of bits. This way, Google Sign In makes sure an app cannot get to actual account information.

5.3 Authenticating with Firebase

Google Sign In and Firebase are connected and they both understand the token generated after a successful Google Sign In. However, this connection must be made by the app.

A AuthCredential object is no more than a thin wrap of a Google Sign In token. Note that GoogleAuthCredential is the actual subclass of the object, but AuthCredential is a super class that permits the unification of different authentication providers in the use of a Firebase database.

Once Firebase completes its own sign in process using the token generated by Google Sign In, then the Firebase database associates the user authenticated by Google Sign In as one of the potential users who can access the resources.

In other words, at this point, the Firebase project says “I know who you (the authenticated user) are.” However, there is no specification of what this user can do. In a way, an account is created with the Firebase project.

6 Back to the database

To use a Firebase database, an app needs a FirebaseDatabase object as a starting point. This is acquired from calling the static method FirebaseDatabase.getInstance. The can be done without any authentication, but then the database connection will be marked as unauthenticated and without any user ID.

The majority of work is not done by a FirebaseDatabase object, however. Most of the work is done by a DatabaseReference object. A DatabaseReference object is like the “working directory” of a command line interface, it represents “the current spot” of a particular connection to a database.

A location in a Firebase database can be specified in a fashion similar to that of a file path. For example, “/Users/tauyeung” in a Firebase database means exactly starting from the root node of the database, find a key named “User”. The corresponding value is expected to be a node (with its own tree or key-value pair list). The “tauyeung” is a key in the sub node.

Writing to a Firebase database is accomplished by the method setValue of a DatabaseReferecne object. There are several variants of this call, all return immediately! A setValue operation is inherently slow. The first variety of setValue methods return a Task object. A Task object is a Google API abstract class that represents an “asynchronous operation”. This means an operation that is independent from the thread that performs the call that results in a Task object.

An app can attach an OnCompleteListener object to a Task object so that the completion of the operation triggers custom code execution. One variant to attach an OnCompleteListener attaches the onComplete call to the queue of the main/UI thread of an Activity. This variant is useful when the result of a setValue operation should trigger some UI changes.

The bottom line is that an app should not assume a setValue operation is going to be successful. The confirmation of a successful setValue call is the callback of a onComplete method of an OnCompleteListener object.

Interestingly, there is no getValue method in the DatabaseReference class. An attempt to read a value is accomplished by attaching a ValueEventListener object to a DatabaseReference object. The onDataChange callback is where the value of a key is returned.

7 Database basic design

A Firebase database differs from a “traditional” database in that it is not relational. The database itself does not support “join” operations. This is not exactly a limitation of a Firebase database, but an attribute of all “big data” database back ends. The problem with a join operation is that it is difficult to scale in a cloud computing environment. Furthermore, a traditional database is also formatted in a way that makes it difficult or inefficient to handle data of varying sizes.

However, a Firebase database still has fast indexing of keys. Furthermore, values of keys can also be designated as (non-unique) indices (using the “.indexOn” rule). Anything that is indexed is sorted quickly and searched quickly.

In a Firebase database, instead of using an associative table to link two related tables, add the association directly to the “tables” to be related. Entries of table contains key(s) to the other table, and vice versa. This is considered “bad design” in relational database design because it is not normalized, and it is not efficient.

It is important to understand the fundamental difference between a “big data” (e.g., Firebase) database and a relational database. In a relational database, a column (field of a row) has a fixed size. On the other hand, an entry of a big data database is hierarchical, and a member can be its own associated array.

This fundamental difference makes it possible to get rid of associative tables and move the relations into the entries to be related, and do it with great efficiency.

A second fundamental difference between a relational database and a big data database is the cost of storage. Relational databases were conceived in an era when storage was expensive compared to demand. As a result, duplication of data is considered a bad idea. Furthermore, there was no clustering technology nor the network infrastructure to support scalable computing.

Fast forwarding to the big data era, storage is plentiful compared to demand. In other words, storage is cheap. Networking infrastructure is efficient enough to support massively parallel clustering techniques. These technological changes means join operations need to go due to the bottleneck nature of such operations not being scalable. However, duplication of data is embraced because storage is inexpensive.

In summary, if an entry has a “to many” relation with children of a node, then the entry itself should include a child associative array so that the keys correspond to the keys of the “to many” target children.