Firebase Authentication by Example with Android

This post is a step by step tutorial on how to implement Firebase Authentication in Android. This post is a continuation of my Firebase Realtime Database tutorial, and I encourage you to read that tutorial first if you are new to Firebase. When we implement authentication to our app, what we are in effected doing is putting our app behind a wall such that anyone requiring access to the app or content will have to go through a login process. An effective authentication process, therefore involves two steps, the authentication process and the handling of successful authentication. We will begin with the later which is handling of a successful Firebase authentication.

In this post, we will continue with the Pronto Diary project that was created in Firebase database tutorial.

Add Firebase Authentication to Android Project

There are two steps to add Firebase authentication to an Android Studio project, first you have to enable the sign-in method that you want to implement over at Firebase console. For this tutorial, we are going to implement email sign-in and Google sign-in so only those two methods have been enabled like this:

Enable Firebase Authentication Method

The next step is to add Firebase authentication to our Android studio project. The best way to do this, it to use the Firebase Android Studio tool. This accomplishes two things: first, it will add Firebase authentication to your google-services.json file and second, it will add the applicable dependencies to your app gradle file. To use the Firebase tool, first make sure that you are logged into Android Studio with the same Google account that you used to create the Firebase project and then click on tools/Firebase like this:

You also need to click on Connect to Firebase if this app has not been connected to Firebase yet. Then you need to click on Add Firebase authentication like this:

Handle Successful Firebase Authentication

We will be implementing the actual authentication workflow in a moment but first, let us handle a successful authentication. If you do not have the source code from the last tutorial, then you can download it below and then follow the steps to handle a successful Firebase authentication.

Download Source Code

Step 1: Require Sign In

Each time Firebase Authentication completes successfully, it will hand over a Firebase User object representing the successfully authenticated user. This user object contains basic information about the logged in user such as name, email, profile image url if any and a proprietary Firebase user id. What you do with this user is up to you, just do not change the user id, you can save this to your own database or do something else. For our demo purposes, we are going to do two things with the user object.

  1. Create a Navigation drawer header displaying the user’s name, email and profile picture.
  2. Create a per user database reference that contains just the user’s data and associate that location (aka folder) with the user.

To do this, we want to erect a login wall in the app launcher Activity DiaryListActivity.java. In the onCreate() method of this class we want to check if the current user is a valid Firebase User. If the user is not a valid Firebase User we want to send them to another Activity that handles the authentication, let us called that Activity AuthUiActivity.java. Then at the top of the Activity named DiaryListActivity.java define two references for Firebase Authentication and Firebase User like this:

private FirebaseAuth mFirebaseAuth;
private FirebaseUser mFirebaseUser;

And then within the onCreate() method, we want to instantiate these two references like this:

mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseUser = mFirebaseAuth.getCurrentUser();

Now we want to check if this user object is null, if it is null then we know that the user does not have a valid admission ticket so we send them to the ticket booth aka AuthUiActivity.java to get authenticated.

if (mFirebaseUser == null){
//Not signed in, launch the Sign In Activity
startActivity(new Intent(this, AuthUiActivity.class));
finish();
return;
}

If the user passes the null test, then we can proceed to retrieve their name, email, and picture like below, first we have to add the respective instance variables for mUsername, mPhotoUrl, and mEmailAddress.

if (mFirebaseUser == null){
//Not signed in, launch the Sign In Activity
startActivity(new Intent(this, AuthUiActivity.class));
finish();
return;
}else {
mUsername = mFirebaseUser.getDisplayName();
mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
mEmailAddress = mFirebaseUser.getEmail();
}

As soon as we pass this point, we know that we have a valid FirebaseUser object and we can proceed to use their information to create a Navigation drawer like this:

private void setupNavigationDrawer(Bundle savedInstanceState) {
mUsername = TextUtils.isEmpty(mUsername) ? ANONYMOUS : mUsername;
mEmailAddress = TextUtils.isEmpty(mEmailAddress) ? ANONYMOUS_EMAIL : mEmailAddress;
mPhotoUrl = TextUtils.isEmpty(mPhotoUrl) ? ANONYMOUS_PHOTO_URL : mPhotoUrl;

IProfile profile = new ProfileDrawerItem()
.withName(mUsername)
.withEmail("someemail@gymmail.com")
.withIcon(mPhotoUrl)
.withIdentifier(102);

mHeader = new AccountHeaderBuilder()
.withActivity(this)
.withHeaderBackground(R.drawable.header)
.addProfiles(profile)
.build();
mDrawer = new DrawerBuilder()
.withAccountHeader(mHeader)
.withActivity(this)
.withToolbar(toolbar)
.withActionBarDrawerToggle(true)
.addDrawerItems(
new PrimaryDrawerItem().withName("Journals").withIcon(GoogleMaterial.Icon.gmd_view_list).withIdentifier(Constants.JOURNAL_ENTRIES),
new PrimaryDrawerItem().withName("Tags").withIcon(GoogleMaterial.Icon.gmd_folder).withIdentifier(Constants.TAGS),
new PrimaryDrawerItem().withName("Logout").withIcon(GoogleMaterial.Icon.gmd_lock).withIdentifier(Constants.LOGOUT),
new PrimaryDrawerItem().withName("Delete Account!").withIcon(GoogleMaterial.Icon.gmd_delete).withIdentifier(Constants.DELETE)
)
}

We are using Mike Penz Navigation Drawer library to create this Navigation Drawer and this takes care of displaying the user’s information. The next thing we want to do with the FirebaseUser object is to update the Journal Entries list such that only the data that belongs to this user is displayed and then when it is time to add data, we want to add the currents users data to a specific location.

Associate User Data with Firebase User

We need to update Firebase database rules in the Firebase console in order to associate each user with their data. Currently, the database rule is configured for public access like this:

{
"rules": {
".read": true,
".write": true
}
}

We can now update that rule to gives each authenticated user a personal node at /users/$user_id where “$user_id" is the same user Id that is exposed at the Android client. Here is the updated rule.

{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}

So, knowing that the user id in the cloud and the client is consistently unique, we can now create a per user database reference like this.

journalCloudEndPoint =  mDatabase.child("/users/" + mFirebaseUser.getUid() + "journalentris");

The above line will create a “users” node (visualize it as a folder) at the root of our cloud database endpoint. This user’s node will contain an entry for each authenticated database user. Under this node is a child node called “journalentries” which will now hold only the Journal Entries of this user. We can now attach a ValueChangedListener to this endpoint like before, and it will only listen for events that are related to the user’s data.

Working with Firebase UI Auth Library

Now that we have handled a successful authentication we need to now implement the authentication workflow using Firebase Authentication. Thankfully, the Firebase UI library makes it easy to implement authentication. Matter of fact, it is highly recommended by the Firebase documentation to use Firebase UI library because it followed best practices and its open source.

Moreover, the library comes with a sample app that demonstrates most of the common use cases of the library. An easy way to implement Firebase authentication is to copy this sample app, specifically the AuthUiActivity and the SignedInActivity to your project. You will need to copy all the associated resource files — they styles, strings, colors, and layout over and then modify as you see fit once your project recompiles.

For this demo app, we will implement email and Google sign in. If you want to implement other authentication providers such as Facebook and Twitter, then you have to perform additional steps as outlined here.

Show Sign In Screen

We will define the login workflow in the AuthUiActivity. In this class once again, the first thing we need to do is to check if the user is logged-in, if they are then we can send them back to the DiaryListActivity if not then we should show the login screen like this.

public class AuthUiActivity extends AppCompatActivity {

private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/";
private static final int RC_SIGN_IN = 100;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth_ui);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

FirebaseAuth auth = FirebaseAuth.getInstance();
if (auth.getCurrentUser() != null) {
startActivity(new Intent(this, DiaryListActivity.class));
finish();
}else {
showSignInScreen();
}

}

private void showSignInScreen() {
startActivityForResult(
AuthUI.getInstance().createSignInIntentBuilder()
.setTheme(R.style.GreenTheme)
.setProviders(getSelectedProviders())
.setTosUrl(GOOGLE_TOS_URL)
.setIsSmartLockEnabled(false)
.build(),
RC_SIGN_IN);
}
}

And with the above lines of code you are almost done with implementing Firebase Authentication. This code will present the user with this login screen.

Firebase UI Auth Login

If the user clicks in either of the login options, the Firebase UI Auth library handles the whole authentication workflow all you app has to do is wait for the authentication to complete. And once it completes the result of that authentication is delivered to the onActivityResult() which you have to handle. All you have to do it to check if the authentication is successful and if it is you can go back to your launcher Activity. You do not have to pass a reference to the user to the next Activity, the Firebase Authentication client handles that. Here is the onActivityResult code.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
handleSignInResponse(resultCode, data);
return;
}

showSnackbar(R.string.unknown_response);
}

@MainThread
private void handleSignInResponse(int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
Intent in = new Intent(this, DiaryListActivity.class);
in.putExtra(EXTRA_IDP_RESPONSE, IdpResponse.fromResultIntent(data));
startActivity(in);
finish();
return;
}

if (resultCode == RESULT_CANCELED) {
showSnackbar(R.string.sign_in_cancelled);
return;
}

if (resultCode == ResultCodes.RESULT_NO_NETWORK) {
showSnackbar(R.string.no_internet_connection);
return;
}

showSnackbar(R.string.unknown_sign_in_response);
}

Summary

That completes this tutorial on Firebase Authentication. The actual implementation of the Authentication workflow is made super simple with the Firebase Auth UI. Think of it like implementing in-app purchase, you decide at which point in your app, where a login is required. When a user reaches that point you send them off to be authenticated. And once that process completes, you can let them continue to use your app. If for some reason, you have some custom requirement that you want to implement the authentication workflow yourself, this tutorial provides a good walk-through. Alternatively, I will be glad to implement that feature for you, use my contact form to request a quote. If you have not done so already, please support me by sharing this post to your social media feed.


Originally published at Val Okafor.