Introduction
Android firebase phone number authentication tutorial. Learn to implement Phone OTP (One Time Password) based authentication using Firebase and Firebase AuthUI. You can use the Firebase Authentication feature to authenticate a user to your app. The user logs in using one time password or code sent as SMS to user’s mobile.
This tutorial is in continuation to my previous blog on user authentication with Firebase. I’ll try and cover most of the points from scratch just so that I can save your time from juggling between two pages. But I highly recommend you to visit that page once as it’ll help clearing a lot of doubt.
Tech Stack
Following are the tech stack used in this app development. We have used Mike Penz’s material design navigation drawer for its simplicity and ease of implementation. I will definitely encourage you to take look in his github page.
1 2 3 4 5 |
Android Studio 3.1 JAVA for Android programming Mike Penz's material navigation drawer Firebase UI for Authentication Firebase Authentication module |
Create an empty app
- Launch Android Studio and click: File –> New –> New Project…
- A “Create New Project” dialog box will open; we’ll provide the app name and company domain. I have entered following details, you can provide the name/domain as per your choice and preference.
- Application Name:- ItcFirebasePhoneAuthenitcation
- Company Domain:- iteritory.com
- Click on Next button. In the next window, select the minimum SDK version; you can choose as per your preference. In this case, I have chosen API 16: Android 4.1 (Jelly Bean). Click Next button.
- In the next window, select “Empty Activity“. Click Next button.
- In the next window, let’s keep the Activity Name and Layout Name to default. Click Finish button.
- I’ll change the app name a bit here, traverse and open res–> values –> strings.xml. Find the line with app_name attribute. Change the value to ““My Phone Auth App”.
Add navigation drawer dependency
- Now, open up the build.gradle file (app level) and in the dependencies section add –
123implementation("com.mikepenz:materialdrawer:6.0.7@aar") {transitive = true} - Ensure that there are other dependencies declared as per the official github page.
- Post modification, the dependency section of build.gradle (App level) looks like below –
1234567891011121314151617181920212223242526272829303132333435apply plugin: 'com.android.application'android {compileSdkVersion 27defaultConfig {applicationId "com.iteritory.itcfirebasephoneauthenitcation"minSdkVersion 16targetSdkVersion 27versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}}dependencies {implementation("com.mikepenz:materialdrawer:6.0.7@aar") {transitive = true}implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'com.android.support:appcompat-v7:27.1.1'implementation 'com.android.support.constraint:constraint-layout:1.0.2'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test:runner:1.0.1'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'implementation 'com.android.support:recyclerview-v7:27.1.1'implementation 'com.android.support:support-annotations:27.1.1'implementation 'com.android.support:design:27.1.1'}
Theme Modification
- Traverse and open app–>res –> values –>styles.xml
- We’ll change the default parent attribute value “Theme.AppCompat.Light.DarkActionBar” with “Theme.AppCompat.Light.NoActionBar“.
- Then we’ll create a new backward compatible custom theme with name “FirebaseMaterialDesignTheme” that will inherit default AppTheme. Post modification, my default styles.xml looks like –
123456789101112<resources><style name="FirebaseMaterialDesignTheme" parent="AppTheme"></style><!-- Base application theme. --><style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"><!-- Customize your theme here. --><item name="colorPrimary">@color/colorPrimary</item><item name="colorPrimaryDark">@color/colorPrimaryDark</item><item name="colorAccent">@color/colorAccent</item></style></resources>
- Right click on res folder and click New –> Android Resource File. A new dialog box will open up. Provide the File name as styles.xml and Directory name as values-v21. A new file will be created; populate the file with below content –
123456<?xml version="1.0" encoding="utf-8"?><resources><style name="FirebaseMaterialDesignTheme" parent="AppTheme"><!-- customize theme for newer device --></style></resources>
- Now, traverse and open app –> manifests –> AndroidManifest.xml.
- Replace the value of android:theme with “@style/FirebaseMaterialDesignTheme”. Post modificaton, the file will look like –
123456789101112131415161718192021<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.iteritory.itcfirebasephoneauthenitcation" ><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/FirebaseMaterialDesignTheme" ><activity android:name=".MainActivity" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
- This modification is for material design theme’s backward compatibility. Here we didn’t modify or used the theme extensively. But I have shown it as a placeholder. Should there a need, make modification in these files and represent the theme as you need.
- Now, if you run the app, you will see an app with “Hello World!”
- Cool, so good so far, let’s move forward
Add some string constants
Traverse and open res –> values –> strings.xml and add some required strings. Post modification, the file will look like –
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<resources> <string name="app_name">My Phone Auth App</string> <string name="login_menu_item">Log in</string> <string name="logout_menu_item">Log out</string> <string name="welcome_on_signin">Congratulations! you have successfully signed in.</string> <string name="default_nouser_signin">No user signed in currently.</string> <string name="login_failed">Log in cancelled!</string> <string name="login_success">Log in successful.</string> <string name="logout_success">Successfully logged out</string> <string name="logout_failed">Failed to log out</string> <string name="login_unknown_Error">Unknown error while logging in. Please Try again</string> <string name="no_connectivity">There is no internet connection. Please try again</string> </resources> |
Download required icon
As part of this app’s development, we will need few icons. Let’s download those –
Ensure you have selected Project view from the explorer. Then, extract the downloaded zip files and traverse to android subfolder for each of these. You will see multiple folders named like “drawable-hdpi” etc. In each of this folder, there will be one version of the icon. We’ll copy each icon from each folder individually and paste to res–>mipmap folder in the studio. While pasting, ensure that you are selecting the right size subfolder inside mipmap folder. That means, if you are copying icon from drawable-hdpi, you will paste it to mipmap-hdpi folder.
Add toolbar
- In order to add toolbar, we will make some changes in res –> layout –> activity_main.xml file. Change the layout to relative layout, add toolbar and modify the text view. Post modification, the file content will be like –
123456789101112131415161718192021222324<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><android.support.v7.widget.Toolbarandroid:layout_width="match_parent"android:layout_height="?android:attr/actionBarSize"android:background="@color/primary"android:id="@+id/idToolbarMain"/><TextViewandroid:id="@+id/idContent"android:text="@string/default_nouser_signin"android:textSize="23dp"android:layout_centerInParent="true"android:layout_width="wrap_content"android:layout_height="wrap_content" /></RelativeLayout> - We’ll add a toolbar to our app using following function. We’ll call this function from onCreate() –
1234private void setupToolbar(){mToolbar = (Toolbar) findViewById(R.id.toolbarMain);setSupportActionBar(mToolbar);}
Mike Penz’s Navigation Drawer
Here are the basic concepts/steps to create the navigation drawer.
- Let’s download a background picture from Mike’s github page and place it in res –> drawable folder.
- We’ll instantiate required navigation drawer menu items using another function instantiateMenuItems().
- While using many apps, you must have noticed that after logging in, the user name and some other information along profile picture is set on top of the navigation drawer. In order to achieve similar feature, we’ll also create a Profile Drawer. However, we won’t use much of this feature much in this tutorial. This will be there as more of a placeholder only.
- Next, we will use this Profile Drawer object (check the previous step) to create an account header.
- Next, we will setup the navigation drawer with Account Header. We’ll also need execute some actions when user clicks on each of these menu items.
- Refer to MainActivity.java towards the end of the post and relate back to above points.
SHA-1 Setup
In order to enable the phone based authentication, we need to setup our App’s SHA-1 fingerprint in the firebase console. There are more than one methods of getting the SHA-1 fingerprint. In here, we’ll use an easy method. Please follow the instructions.
- Locate Gradle icon on the right of the IDE. Check the below screenshot. Depending on how you have set the different windows, it may be present in other location as well. However, in my case (default), it’s present on top-right –
- Click on Gradle –> :app –> task –> android. You’ll notice “signingReport”. Double click on that and in a few seconds, you will have SHA1 fingerprint. Take a look in the below screenshot. You will see both MD5 and SHA-1 fingerprints. I have masked both the values here.
- Note the SHA-1 fingerprint; we will need it in next step
Setup Firebase project
As we’ll use Firebase Authentication, we first need to setup a project in the firebase console –
- Use your gmail id to login to Firebase Console
- In the console, you will see an “Add Project” button, let’s click on this and a new popup will open up. I have given the project name as “My Phone Auth Project“. It’ll automatically generate a project id. You can keep the default. In my case also, I have kept the default.
- Change the country as per your choice and click on “CREATE PROJECT” button. It’ll take a few moments and then firebase makes the project ready for you.
- A new window will open up, there will be several options. As we are here to develop an android app, let’s select “Add Firebase to your Android app” option.
- A new window will pop-up. We’ll provide exactly the same package name that we used while creating the app in Android Studio. In my case, it is com.iteritory.itcfirebasephoneauthenitcation.
- Let’s Provide an app nick name as well.
- Pate the SHA-1 fingerprint in the last field. I have masked my app’s SHA-1 fingerprint in below image. After populating these info, my window looks like below –
- Click on register app button. A new window will open up.
- On the new window click on the Download google-services.json button and a .json file will be downloaded in your local system.
- As described in the above screen, switch to project view if you have not already done. Copy the downloaded googl-services.json file and paste inside app folder. If you notice, it will be a sibling of build.gradle file inside app folder.
- Click on the Continue.
- Click on Finish button.
- Next, click on the DEVELOP menu on the left and then select Authentication sub-menu. A new window will open up on your right; click on the “SIGN-IN METHOD” tab –
- In this tutorial, we will use only phone based authentication method from the provided list.
- Click on Phone, a window will pop-up. Click on Enable and then click Save button
- So, now that’s it. We are done with all the setup thingy! Next, we do some coding again ?
Add Firebase Dependency
Open the root level build.grade file and add following classpath
- classpath ‘com.google.gms:google-services:3.2.0’ // google-services plugin
- Post modification the file will look like –
123456789101112131415161718192021222324252627// Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript {repositories {google()jcenter()}dependencies {classpath 'com.android.tools.build:gradle:3.1.0'classpath 'com.google.gms:google-services:3.2.0' // google-services plugin// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}}allprojects {repositories {google()jcenter()}}task clean(type: Delete) {delete rootProject.buildDir}
- Open app level build.gradle file and add following two dependencies. Note that, at the time of writing this blog, these are the latest versions. You may need to use appropriate versions depending on when you are writing and whether any of these libraries got latest updates.
- implementation ‘com.google.firebase:firebase-auth:12.0.1’
- implementation ‘com.firebaseui:firebase-ui-auth:3.3.0’
- implementation ‘com.google.firebase:firebase-core:12.0.1’
- At the end of depdencies, add following –
- apply plugin: ‘com.google.gms.google-services’
- Post modification, the app level build.gradle will look like –
1234567891011121314151617181920212223242526272829303132333435363738394041apply plugin: 'com.android.application'android {compileSdkVersion 27defaultConfig {applicationId "com.iteritory.itcfirebasephoneauthenitcation"minSdkVersion 16targetSdkVersion 27versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}}dependencies {implementation("com.mikepenz:materialdrawer:6.0.7@aar") {transitive = true}implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'com.android.support:appcompat-v7:27.1.1'implementation 'com.android.support.constraint:constraint-layout:1.0.2'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test:runner:1.0.1'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'implementation 'com.android.support:recyclerview-v7:27.1.1'implementation 'com.android.support:support-annotations:27.1.1'implementation 'com.android.support:design:27.1.1'implementation 'com.google.firebase:firebase-core:12.0.1'implementation 'com.google.firebase:firebase-auth:12.0.1'implementation 'com.firebaseui:firebase-ui-auth:3.3.0'}apply plugin: 'com.google.gms.google-services'
Instantiate User in MainActivity
Declare an instance of FirebaseAuth and do a getUser() on that instance to check who is the current user. If the getUser returns NULL, we know that there is no user session, i.e. no user is currently logged in. Let’s take a look at the simple function –
1 2 3 4 |
private void intstantiateUser(){ mFirebaseAuth = FirebaseAuth.getInstance(); mFirebaseUser = mFirebaseAuth.getCurrentUser(); } |
We’ll also write a utility function to tell us if the user is logged in or not –
1 2 3 4 5 6 7 |
private boolean isUserSignedIn(){ if (mFirebaseUser == null){ return false; }else{ return true; } } |
App UI Workflow
- While the app is launched, it will check if there is any user already logged in.
- If there is no user logged in, it will show “Log in” menu item. It’ll also show a message in the app text view that there is no user currently logged in to the app.
- When a user selects “Login” menu, Firebase AuthUI kicks in and it takes the user through a very intuitive UI sequence, for inputting phone number. After you input your phone number, Firebase will send a SMS to your number. The message will be auto-read by the app and it’ll verify your number. As simple as that
- Notice that, we won’t write a single line of code for all these. We’ll just kick start the AuthUI with require parameters and it will take its own course. This is the beauty of Firebase Authentication with AuthUI. You don’t code the complex logic; you have everything ready for you.
- If there is any user already logged in –
- In the navigation drawer, we won’t see any Login menu item. Instead, we will have Logout menu item.
- We’ll also print a message stating that user is logged in.
- After stitching together all the checks and balances, my MainActivity.java looks like and yes run the app and experience the power of Firebase engine and Firebase UI. 🙂
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199package com.iteritory.itcfirebasephoneauthenitcation;import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.Toolbar;import android.view.View;import android.widget.TextView;import android.widget.Toast;import com.firebase.ui.auth.AuthUI;import com.firebase.ui.auth.ErrorCodes;import com.firebase.ui.auth.IdpResponse;import com.google.firebase.auth.FirebaseAuth;import com.google.firebase.auth.FirebaseUser;import com.mikepenz.materialdrawer.AccountHeader;import com.mikepenz.materialdrawer.AccountHeaderBuilder;import com.mikepenz.materialdrawer.Drawer;import com.mikepenz.materialdrawer.DrawerBuilder;import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;import com.mikepenz.materialdrawer.model.ProfileDrawerItem;import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;import com.mikepenz.materialdrawer.model.interfaces.IProfile;import java.util.Arrays;public class MainActivity extends AppCompatActivity {FirebaseAuth mFirebaseAuth;FirebaseUser mFirebaseUser;Toolbar mToolbar;Drawer mDrawerResult;AccountHeader mHeaderResult;ProfileDrawerItem mProfileDrawerItem;PrimaryDrawerItem mItemLogin, mItemLogout;private static final int RC_SIGN_IN = 123;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);setupToolbar();intstantiateUser();instantiateMenuItems();setupNavigationDrawerWithHeader();}private void setupToolbar(){mToolbar = (Toolbar) findViewById(R.id.idToolbarMain);setSupportActionBar(mToolbar);}private void instantiateMenuItems(){mItemLogin = new PrimaryDrawerItem().withIdentifier(1).withName(R.string.login_menu_item).withIcon(getResources().getDrawable(R.mipmap.ic_login_black_48dp));mItemLogout = new PrimaryDrawerItem().withIdentifier(2).withName(R.string.logout_menu_item).withIcon(getResources().getDrawable(R.mipmap.ic_logout_black_48dp));;}private AccountHeader setupAccountHeader(){mProfileDrawerItem = new ProfileDrawerItem().withIcon(getResources().getDrawable(R.mipmap.ic_account_circle_black_48dp));mHeaderResult = new AccountHeaderBuilder().withActivity(this).withHeaderBackground(R.drawable.header).addProfiles(mProfileDrawerItem).withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {@Overridepublic boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {return false;}}).withSelectionListEnabledForSingleProfile(false).build();return mHeaderResult;}private void setupNavigationDrawerWithHeader(){//Depending on user is logged in or not, decide whether to show Log In menu or Log Out menuif (!isUserSignedIn()){((TextView)findViewById(R.id.idContent)).setText(R.string.default_nouser_signin);mDrawerResult = new DrawerBuilder().withActivity(this).withAccountHeader(setupAccountHeader()).withToolbar(mToolbar).addDrawerItems(mItemLogin).withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {@Overridepublic boolean onItemClick(View view, int position, IDrawerItem drawerItem) {onNavDrawerItemSelected((int)drawerItem.getIdentifier());return true;}}).build();mDrawerResult.deselect(mItemLogin.getIdentifier());}else{((TextView)findViewById(R.id.idContent)).setText(R.string.welcome_on_signin);mDrawerResult = new DrawerBuilder().withActivity(this).withAccountHeader(setupAccountHeader()).withToolbar(mToolbar).addDrawerItems(mItemLogout).withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {@Overridepublic boolean onItemClick(View view, int position, IDrawerItem drawerItem) {onNavDrawerItemSelected((int)drawerItem.getIdentifier());return true;}}).build();}mDrawerResult.closeDrawer();}private void onNavDrawerItemSelected(int drawerItemIdentifier){switch (drawerItemIdentifier){//Sign Incase 1:Toast.makeText(this, "Login menu selected", Toast.LENGTH_LONG).show();startActivityForResult(AuthUI.getInstance().createSignInIntentBuilder().setAvailableProviders(Arrays.asList(new AuthUI.IdpConfig.PhoneBuilder().setDefaultCountryIso("in").build())).setLogo(R.mipmap.ic_account_circle_black_48dp).setIsSmartLockEnabled(true).build(), RC_SIGN_IN);break;//Sign Outcase 2:signOutUser();Toast.makeText(this, "Logout menu selected", Toast.LENGTH_LONG).show();break;}}private void intstantiateUser(){mFirebaseAuth = FirebaseAuth.getInstance();mFirebaseUser = mFirebaseAuth.getCurrentUser();}private boolean isUserSignedIn(){if (mFirebaseUser == null){return false;}else{return true;}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == RC_SIGN_IN) {IdpResponse response = IdpResponse.fromResultIntent(data);// Successfully signed inif (resultCode == RESULT_OK) {Toast.makeText(this, R.string.login_success, Toast.LENGTH_LONG).show();signInUser();return;}else{//User pressed back buttonif (response == null) {Toast.makeText(this, R.string.login_failed, Toast.LENGTH_LONG).show();mDrawerResult.deselect(mItemLogin.getIdentifier());return;}//No internet connection.if (response.getErrorCode() == ErrorCodes.NO_NETWORK) {Toast.makeText(this, R.string.no_connectivity, Toast.LENGTH_LONG).show();return;}//Unknown errorif (response.getErrorCode() == ErrorCodes.UNKNOWN_ERROR) {Toast.makeText(this, R.string.login_unknown_Error, Toast.LENGTH_LONG).show();return;}}}}private void signInUser(){intstantiateUser();//mCurrentProfile = checkCurrentProfileStatus();mDrawerResult.updateItemAtPosition(mItemLogout,1);mDrawerResult.deselect(mItemLogout.getIdentifier());((TextView)findViewById(R.id.idContent)).setText(R.string.welcome_on_signin);mDrawerResult.closeDrawer();}private void signOutUser(){//Sign outmFirebaseAuth.signOut();mFirebaseUser = mFirebaseAuth.getCurrentUser();if (!isUserSignedIn()) {mDrawerResult.updateItemAtPosition(mItemLogin,1);mDrawerResult.deselect(mItemLogin.getIdentifier());((TextView)findViewById(R.id.idContent)).setText(R.string.default_nouser_signin);mDrawerResult.closeDrawer();}else{//check if internet connectivity is there or any other error handling}}}
GitHub
I have pushed my entire project in git hub and you can get it here.
Conclusion
This post has been quite long. Hope it helps you. If you have question, feel free to reach out in the comment box below. ?