The Firebase Auth Quickstart provides a great tutorial on how to add authentication to your application, but without two-factor authentication (2FA), it's a bit insecure! Adding an extra layer of security to your application is simple using the Nexmo Verify SDK. Let's get started!
Grab the sample app
Start by cloning the GitHub repo in your Terminal. The original repo is constantly changing, so you can clone the starting branch for the tutorial from a forked repo from nexmo-community.
DT API Account
To complete this tutorial, you will need a DT API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the DT API Dashboard.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Create a new project in Firebase console
Login to Firebase with your Google account and go to the console. Create a new project by giving it a name and updating the country you reside in. Once created, click the option 'Add Firebase to your Android app". Enter your package name (default for demo app is: com.google.firebase.quickstart.auth) and the debug signing certificate SHA-1. Disregard the ‘google-services.json’ file that is download as you will need to download a new version once we add the OAuth login methods to the app. You don’t need to edit the build.gradle files, because this has already been done for you in the sample project.
Set up login methods
The sample app comes with the following login methods: Email/Password login, Anonymous login, and OAuth logins. Both OAuth providers (Google and Twitter) need to be set up and the Twitter Sign In API key/secret should be entered in 'ids.xml'. If you have trouble finding this, search for the file via the search tool on the top right corner of Android Studio. Once the authentication methods been set up, navigate to your project settings and download the ‘google-services.json’ file and add it to the app folder in the Project view.
Email & Anonymous Login: Enable both methods in the Authentication section of the Firebase console.
Google: Google Sign In should be set up for your Android app after you add the debug SHA-1 signing certificate in the Firebase console. Enable the Google OAuth method in the Authentication section of the console.
Twitter: In the Firebase console, enable Twitter for authentication. Next, create a new application in the Twitter Developer Portal. Copy the callback URL (available in the Twitter Authentication section of the Firebase console) and enter it in the corresponding field to finish creating the Twitter app. Lastly, go to the Twitter Application Settings and click on ‘Manage Keys and Access Tokens’. Copy the consumer key (API Key) and consumer secret (API Secret) into the Firebase console and 'ids.xml'.
Open and update project
In Android Studio, choose the option to open an existing project. Navigate to where the repo was cloned and select the 'auth' located inside the 'quickstart-android' folder. You will need to update the Android SDK to version 25, the build tools to version 25.0.0, and also update the JDK version to 1.8 as you will need these update in order to successfully compile the gradle files. Once the project is loaded in Android Studio, install any missing dependencies and sync the project to clear errors. Create an AVD that is using API 24 to properly run the app at the end of the tutorial. Lastly, update your project’s Google Play Services under Tools > Android > SDK Manager > SDK Tools to version 38.
Create Nexmo Application
After creating a Nexmo account, you will be redirected to the Nexmo dashboard. Click the 'Verify' tab and under 'SDK', click the 'Your apps' link. Create a new application (with the max Idle time set to Instant) and note down the Application ID and Shared Secret. The max idle time is a setting that dictates how long the user will stay verified for.
Add the Nexmo credentials (as well as the OAuth if you didn't in the step above) to 'ids.xml'.
Add Nexmo dependency to the app's Build.gradle
Add the following line of code to your app-level Build.gradle file to bring the Nexmo Verify SDK in your application as a dependency. Sync the gradle file to resolve any missing dependencies.
compile 'com.nexmo:verify:4.0.0'
Add a new class for the Nexmo Client
Create a new class to your project called ‘TwoFactorApplication.java’ and enter the following code in the file. Here you create the Nexmo client and provide it with the values you stored in 'ids.xml' previously.
package com.google.firebase.quickstart.auth;
import android.app.Application;
import android.content.Context;
Import android.util.Log;
import com.nexmo.sdk.NexmoClient;
import com.nexmo.sdk.core.client.ClientBuilderException;
import com.nexmo.sdk.verify.client.VerifyClient;
public class TwoFactorApplication extends Application {
private VerifyClient verifyClient;
private NexmoClient nexmoClient;
private boolean verified;
private static final String TAG = “TWOFACTORAPPLICATION”;
public VerifyClient getVerifyClient(boolean verifiedValue) {
verified = verifiedValue;
return this.verifyClient;
}
@Override
public void onCreate() {
super.onCreate();
acquireVerifyClient();
}
public void acquireVerifyClient() {
Context context = getApplicationContext();
try {
this.nexmoClient = new NexmoClient.NexmoClientBuilder()
.context(context)
.applicationId(getResources().getString(R.string.nexmo_application_id))
.sharedSecretKey(getResources().getString(R.string.nexmo_shared_secret))
.build();
} catch (ClientBuilderException e) {
e.printStackTrace();
Log.d(TAG, e.toString());
return;
}
this.verifyClient = new VerifyClient(nexmoClient);
}
}
In the ‘AndroidManifest.xml’ file, add the necessary permissions for the Verify SDK and the application name to the application tag:
<application android:name="com.google.firebase.quickstart.auth.TwoFactorApplication" <="" code=""></application>
Add the following code to the BaseActivity ------------------------------------------ Declare the following variables in the BaseActivity: * application (of type: TwoFactorApplication) * verified (of type: boolean) In a ‘onCreate()’ method cast the application context returned by calling 'this.getApplication();'. This returns the application that owns the activity (TwoFactorApplication). Then we create a method called ‘addVerificationListener’ to act as listener in order to continue the app workflow on a successful 2FA verification. Inside this method we get the VerifyClient provided by the Nexmo library and append addVerifyListener() method. ```blok {"type":"codeBlock","props":{"lang":"text","code":"//Import%20Statements%0Aimport%20java.io.IOException;%0Aimport%20com.nexmo.sdk.verify.client.VerifyClient;%0Aimport%20com.nexmo.sdk.verify.event.UserObject;%0Aimport%20com.nexmo.sdk.verify.event.VerifyClientListener;%0Aimport%20com.nexmo.sdk.verify.event.VerifyError;%0AImport%20android.os.Bundle;%0A%0A//Declaration%0Apublic%20TwoFactorApplication%20twoFactorApp;%0Aprotected%20boolean%20verified;%0A%0A@Override%0Aprotected%20void%20onCreate(Bundle%20savedInstanceState)%20%7B%0Asuper.onCreate(savedInstanceState);%0AtwoFactorApp%20=%20(TwoFactorApplication)%20this.getApplication();%0A%7D%0A%0Aprotected%20void%20addVerificationListener()%20%7B%0AtwoFactorApp.getVerifyClient(verified).addVerifyListener(new%20VerifyClientListener()%20%7B%0A@Override%0Apublic%20void%20onVerifyInProgress(VerifyClient%20verifyClient,%20UserObject%20user)%20%7B%0A%7D%0A@Override%0Apublic%20void%20onUserVerified(VerifyClient%20verifyClient,%20UserObject%20user)%20%7B%0Averified%20=%20true;%0A%7D%0A@Override%0Apublic%20void%20onError(VerifyClient%20verifyClient,%20VerifyError%20errorCode,%20UserObject%20user)%20%7B%0A%7D%0A@Override%0Apublic%20void%20onException(IOException%20exception)%20%7B%0A%7D%0A%7D);%0A%7D%0A"}} ``` Update the GoogleSignInActivity code ------------------------------------ Move the 'signIn()' method call from the onClick function to the ‘onStart()' method if the value of ‘verified’ is equal to true. Instead, when the user clicks the sign in button, launch the Verify SDK managed UI and call the 'addVerificationListener()' method. Lastly, change the value of ‘verified’ to false when the Activity is stopped to ensure the user has to log in each time. ```blok {"type":"codeBlock","props":{"lang":"text","code":"@Override%0Apublic%20void%20onStart()%20%7B%0Asuper.onStart();%0Aif%20(verified%20==%20true)%20%7B%0AsignIn();%0A%7D%0AmAuth.addAuthStateListener(mAuthListener);%0A%7D%0A%0A@Override%0Apublic%20void%20onStop()%20%7B%0Asuper.onStop();%0Averified%20=%20false;%0AmAuth.signOut();%0Aif%20(mAuthListener%20!=%20null)%20%7B%0AmAuth.removeAuthStateListener(mAuthListener);%0A%7D%0A%7D%0A%0A@Override%0Apublic%20void%20onClick(View%20v)%20%7B%0Aif%20(i%20==%20R.id.sign_in_button)%20%7B%0AtwoFactorApp.getVerifyClient(verified).getVerifiedUserFromDefaultManagedUI();%0AaddVerificationListener();%0A%7D%20else%20if%20(i%20==%20R.id.sign_out_button)%20%7B%0AsignOut();%0A%7D%20else%20if%20(i%20==%20R.id.disconnect_button)%20%7B%0ArevokeAccess();%0A%7D%0A%7D%0A"}} ``` Update the EmailPasswordActivity code ------------------------------------- Just like the last step, move the sign in method call from the onClick function to the ‘onStart()' method. Instead, Launch the Verify SDK managed UI and call the 'verificationCallback()' method on user clicking the login button. Once again, change ‘verified’ to false in the ‘onStop()’ method. ```blok {"type":"codeBlock","props":{"lang":"text","code":"@Override%0Apublic%20void%20onStart()%20%7B%0Asuper.onStart();%0Aif%20(verified%20==%20true)%20%7B%0AsignIn(mEmailField.getText().toString(),%20mPasswordField.getText().toString());%0A%7D%0AmAuth.addAuthStateListener(mAuthListener);%0A%7D%0A%0A@Override%0Apublic%20void%20onStop()%20%7B%0Asuper.onStop();%0Averified%20=%20false;%0AmAuth.signOut();%0Aif%20(mAuthListener%20!=%20null)%20%7B%0AmAuth.removeAuthStateListener(mAuthListener);%0A%7D%0A%7D%0A%0A@Override%0Apublic%20void%20onClick(View%20v)%20%7B%0Aif%20(i%20==%20R.id.sign_in_button)%20%7B%0AtwoFactorApp.getVerifyClient(verified).getVerifiedUserFromDefaultManagedUI();%0AaddVerificationListener();%0A%7D%20else%20if%20(i%20==%20R.id.sign_out_button)%20%7B%0AsignOut();%0A%7D%20else%20if%20(i%20==%20R.id.disconnect_button)%20%7B%0ArevokeAccess();%0A%7D%0A%7D%0A%0Aprivate%20void%20signOut()%20%7B%0AmAuth.signOut();%0Averified%20=%20false;%0AupdateUI(null);%0A%7D%0A"}} ``` Update the AnonymousAuthActivity code ------------------------------------- Again, we move the sign in method call from ‘onClick’ to the ‘onStart()' method to be called if the user has been verified. When the user presses the Sign In button, trigger the Verify managed UI and call the 'addVerificationListener()' method on user clicking the login button. ```blok {"type":"codeBlock","props":{"lang":"java","code":"@Override%0Apublic%20void%20onStart()%20%7B%0Asuper.onStart();%0AmAuth.addAuthStateListener(mAuthListener);%0Aif%20(verified)%20%7B%0AsignInAnonymously();%0A%7D%0A%7D%0A%0A@Override%0Apublic%20void%20onStop()%20%7B%0Asuper.onStop();%0AmAuth.signOut();%0Averified%20=%20false;%0Aif%20(mAuthListener%20!=%20null)%20%7B%0AmAuth.removeAuthStateListener(mAuthListener);%0A%7D%0A%7D%0A%0Aprivate%20void%20signOut()%20%7B%0AmAuth.signOut();%0Averified%20=%20false;%0AupdateUI(null);%0A%7D%0A%0A@Override%0Apublic%20void%20onClick(View%20v)%20%7B%0Aint%20i%20=%20v.getId();%0Aif%20(i%20==%20R.id.button_anonymous_sign_in)%20%7B%0AtwoFactorApp.getVerifyClient(verified).getVerifiedUserFromDefaultManagedUI();%0AaddVerificationListener();%0A%7D%20else%20if%20(i%20==%20R.id.button_anonymous_sign_out)%20%7B%0AsignOut();%0A%7D%20else%20if%20(i%20==%20R.id.button_link_account)%20%7B%0AlinkAccount();%0A%7D%0A%7D%0A"}} ``` Update the TwitterLoginActivity code ------------------------------------ The login flow for the TwitterLoginActivity gets a little tricker than the previous login activities. The Twitter login button initializes the Twitter OAuth login sequence. Here you allow a user to continue with the login flow after they have provided the correct PIN code (onUserVerified). ```blok {"type":"codeBlock","props":{"lang":"java","code":"@Override%0Apublic%20void%20onStart()%20%7B%0Asuper.onStart();%0AmAuth.addAuthStateListener(mAuthListener);%0Aif(!verified)%20%7B%0AtwoFactorApp.getVerifyClient(verified).getVerifiedUserFromDefaultManagedUI();%0AaddVerificationListener();%0A%7D%0A%7D%0A%0A@Override%0Apublic%20void%20onStop()%20%7B%0Asuper.onStop();%0Averified%20=%20false;%0AmAuth.signOut();%0Aif%20(mAuthListener%20!=%20null)%20%7B%0AmAuth.removeAuthStateListener(mAuthListener);%0A%7D%0A%7D%0A%0Aprivate%20void%20signOut()%20%7B%0AmAuth.signOut();%0Averified%20=%20false;%0ATwitter.logOut();%0AupdateUI(null);%0A%7D%0A%0AmLoginButton.setCallback(new%20Callback()%20%7B%0A@Override%0Apublic%20void%20success(Result%20result)%20%7B%0ALog.d(TAG,%20%22twitterLogin:success%22%20+%20result);%0Averified%20=%20true;%0AhandleTwitterSession(result.data);%0A%7D%0A@Override%0Apublic%20void%20failure(TwitterException%20exception)%20%7B%0ALog.w(TAG,%20%22twitterLogin:failure%22,%20exception);%0AupdateUI(null);%0A%7D%0A%7D);%0A"}} ``` Now all the OAuth providers along with the email/password and anonymous login methods have a flow that ensures the user completes the second factor of authentication to access the account. The 2FA enabled version of this tutorial is available in the ['final' branch of the repo](https://github.com/nexmo-community/quickstart-android/tree/final). ![Chooser Activity](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/add-2fa-to-an-android-app-with-nexmo-and-firebase-login/4-1.png) ![Google Sign In Screen](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/add-2fa-to-an-android-app-with-nexmo-and-firebase-login/5-1.png) ![Twitter Login Screen](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/add-2fa-to-an-android-app-with-nexmo-and-firebase-login/6-1.png) ![Email Login Screen](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/add-2fa-to-an-android-app-with-nexmo-and-firebase-login/7-1.png) ![Anonymous Login Screen](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/add-2fa-to-an-android-app-with-nexmo-and-firebase-login/8-1.png) The Verify SDK allows you to allocate more time to developing your app by providing a managed UI solution that enables you to make verification requests securely and with ease. I'd love to hear from you if there are questions or thoughts. Tweet me [https://twitter.com/sidsharma_27](@sidsharma_27) or [email me](mailto:sidharth.sharma@nexmo.com) directly.