Phone Number Authentication using the Firebase SDK

From Techotopia
Revision as of 18:11, 14 August 2017 by Neil (Talk | contribs) (Designing the User Interface)

Jump to: navigation, search

As previously outlined in the Phone Number Sign-in Authentication using FirebaseUI Auth chapter of this book, Firebase Phone Number Authentication allows users to sign into an app by providing a phone number. Before the user can sign in, a one-time code is sent via SMS to the provided number which must be entered into the app to gain access.

Having covered Phone number authentication using FirebaseUI Auth in the earlier chapter, this chapter will explain how to achieve similar results using the Firebase SDK.


Contents


Phone Number Authentication

Phone authentication using the Firebase SDK involves asking the user for a phone number, sending a 6-digit verification code via SMS message, and then verifying that the code is valid. If the code entered is valid, the app can take the usual steps to sign the user in. In the event that the code is not received by the user, the app should typically provide the user the option to resend the code.

All of the work involved in generating the code and sending the SMS message is handled by a call to the verifyPhoneNumber() method of the PhoneAuthProvider instance. This will trigger a call to one of the following callback methods:

onVerificationCompleted() – Called only on certain device configurations (typically devices containing a SIM card) where the verification code can be verified automatically on the device without the user having to manually enter it.

onVerificationFailed() – Indicates that an error occurred when sending the activation code. This is usually the result of the user entering an invalid or incorrectly formatted phone number.

onCodeSent() – Called after the code has been sent. This method is passed a verification ID and a resend token that should be referenced when making a code resend request.

onCodeAutoRetrievalTimeOut() – When the verifyPhoneNumber() method is called it is passed a timeout value. This method is called if the verification process is not completed within the given timescale.

Once the verification code has been entered by the user, the code and the verification ID provided to the onCodeSent() callback are passed through to the getCredential() method of the PhoneAuthProvider class. This returns a PhoneAuthCredential object which can be used to sign the user into the app in the usual way via a call to the signInWithCredential() method of the FirebaseAuth class.

Creating the Example Project

Launch Android Studio and select the Start a new Android Studio project quick start option from the welcome screen.

Within the new project dialog, enter PhoneAuth into the Application name field and your domain as the Company Domain setting before clicking on the Next button. On the form factors screen, enable the Phone and Tablet option and set the minimum SDK to API 16: Android 4.1 (Jellybean). Continue to proceed through the screens, requesting the creation of an Empty Activity named PhoneAuthActivity with a corresponding layout named activity_phone_auth.


Connecting the Project to Firebase

Within Android Studio, use the Tools -> Firebase menu to display the Firebase assistant panel, locate and click on the Authentication category, select the Email and password authentication link and then click on the Connect to Firebase button to display the Firebase connection dialog.

Choose the option to store the app in an existing Firebase project and select the Firebase Examples project.

With the project’s Firebase connection established, refer to the Firebase assistant panel once again, this time clicking on the Add Firebase Authentication to your app button. A dialog will appear outlining the changes that will be made to the project build configuration to enable Firebase authentication. Click on the Accept Changes button to commit the changes to the project configuration.

Updating the firebase-auth Package

Phone verification makes use of the latest version of the firebase-auth library. At the time of writing, Android Studio is defaulting to an older version of the library in the module level build.gradle file. To resolve this issue, edit the build.gradle (Module: app) file and update the version on the firebase-auth library to version 11.0.1 or newer:

compile 'com.google.firebase:firebase-auth:11.0.1'

After making the change, click on the Sync Now link that appears at the top of the editor window.

Designing the User Interface

The user interface for the main activity is going to consist of four Buttons, two EditText views and a TextView object. Open the activity_phone_auth.xml file in the layout editor, delete the default Hello World TextView and turn off Autoconnect mode.

Drag and drop components from the palette on the layout canvas and set the text and hint properties on the views so that the layout resembles Figure 16 1. Starting from the top, the widgets selected are a TextView, Phone EditText, two Buttons, Number EditText and, finally, two more Buttons:


Firebase auth phone ui.png

Figure 16-1


Select all seven widgets in the layout, right-click on the TextView widget and choose the Center Horizontally menu option. Repeat this step, this time selecting the Center Vertically menu option.

Using the properties tool window, configure the buttons to call onClick methods named sendCode(), resendCode(), verifyCode() and signOut() respectively. Starting at the top widget and working down, configure the ID property for each view to statusText, phoneText, sendButton, resendButton, codeText, verifyButton and signoutButton.

Performing Initialization Tasks

Before starting work on the onClick and callback handlers, some preparatory initialization work needs to be performed within the PhoneAuthActivity class. Locate the PhoneAuthActivty.java file in the Project tool window and load it into the code editor. Begin by adding some import directives, variable declarations and initialization code:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.PhoneAuthProvider;

public class PhoneAuthActivity extends AppCompatActivity {

    private static final String TAG = "PhoneAuth";

    private EditText phoneText;
    private EditText codeText;
    private Button verifyButton;
    private Button sendButton;
    private Button resendButton;
    private Button signoutButton;
    private TextView statusText;

    private String phoneVerificationId;
    private PhoneAuthProvider.OnVerificationStateChangedCallbacks 
                                             verificationCallbacks;
    private PhoneAuthProvider.ForceResendingToken resendToken;

    private FirebaseAuth fbAuth;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_phone_auth);

        phoneText = (EditText) findViewById(R.id.phoneText);
        codeText = (EditText) findViewById(R.id.codeText);
        verifyButton = (Button) findViewById(R.id.verifyButton);
        sendButton = (Button) findViewById(R.id.sendButton);
        resendButton = (Button) findViewById(R.id.resendButton);
        signoutButton = (Button) findViewById(R.id.signoutButton);
        statusText = (TextView) findViewById(R.id.statusText);

        verifyButton.setEnabled(false);
        resendButton.setEnabled(false);
        signoutButton.setEnabled(false);
        statusText.setText("Signed Out");

        fbAuth = FirebaseAuth.getInstance();
    }
.
.
}

Sending the Verification Code

Code now needs to be added to the project to send the verification code to the user’s phone when the user clicks the Send Code button. Remaining within the PhoneAuthActivity.java file, implement the sendCode() method as follows:

.
.
import com.google.firebase.auth.PhoneAuthCredential;

import java.util.concurrent.TimeUnit;
.
.
public void sendCode(View view) {

    String phoneNumber = phoneText.getText().toString();

    setUpVerificatonCallbacks();

    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,        // Phone number to verify
            60,                 // Timeout duration
            TimeUnit.SECONDS,   // Unit of timeout
            this,               // Activity (for callback binding)
            verificationCallbacks);
}

The sendCode() method calls the verifyPhoneNumber() method of the PhoneAuthProvider instance to send the code to the phone number entered into the phoneText EditText view. Before doing this, however, a call is made to set up the verification callbacks which are referenced in the final argument passed to the verifyPhoneNumber() method call. The next step, therefore, is to implement this method.

Adding the Verification Callbacks

Staying within the PhoneAuthActivity class file, add the setUpVerificationCallbacks() method so that it reads as follows:

import com.google.firebase.FirebaseException;
import com.google.firebase.FirebaseTooManyRequestsException;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;

public class PhoneAuthActivity extends AppCompatActivity {
.
.
    private void setUpVerificatonCallbacks() {

        verificationCallbacks = 
	    new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            @Override
            public void onVerificationCompleted(
			PhoneAuthCredential credential) {

                signoutButton.setEnabled(true);
                statusText.setText("Signed In");
                resendButton.setEnabled(false);
                verifyButton.setEnabled(false);
                codeText.setText("");
                signInWithPhoneAuthCredential(credential);
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {

                if (e instanceof FirebaseAuthInvalidCredentialsException) {
                    // Invalid request
                    Log.d(TAG, "Invalid credential: " 
				+ e.getLocalizedMessage());
                } else if (e instanceof FirebaseTooManyRequestsException) {
                    // SMS quota exceeded
                    Log.d(TAG, "SMS Quota exceeded.");
                }
            }

            @Override
            public void onCodeSent(String verificationId,
                          PhoneAuthProvider.ForceResendingToken token) {

                phoneVerificationId = verificationId;
                resendToken = token;

                verifyButton.setEnabled(true);
                sendButton.setEnabled(false);
                resendButton.setEnabled(true);
            }
        };
    }
.
.
}

In the event that the verification code sent successfully, the onCodeSent() method stores the verification ID and token for later use before enabling and disabling some of the buttons in the user interface.

If the code was verified automatically, the onVerificationCompleted() method makes some changes to the user interface and calls a method to initiate the sign-in process using the provided credential.

Finally, a failed verification attempt will result in a call to the onVerificationFailed() method where code has been added to differentiate between an invalid credential and the SMS quota being exceeded.

Verifying the Code

After the code arrives via SMS message and has been entered it into the code text field, the user is expected to click on the Verify Code button. When the user interface was designed, this button as configured to call a method named verifyCode() which now needs to be added:

public void verifyCode(View view) {

    String code = codeText.getText().toString();

    PhoneAuthCredential credential = 
       PhoneAuthProvider.getCredential(phoneVerificationId, code);
    signInWithPhoneAuthCredential(credential);
}

The method obtains the code from the EditText view and passes it, along with the verification ID saved within the onCodeSent() method to the getCredential() method of the PhoneAuthProvider instance. The credential returned by this method call is then passed to the signInWithPhoneCredential() method which now also needs to be implemented.

Signing In with the Credential

Regardless of whether the user manually verified the code, or if verification was automatic, the app has been provided with a PhoneAuthCredential object which can be used to sign into the app. The code for this is similar to that required for other authentication providers and should be added as follows:

import android.support.annotation.NonNull;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseUser;
.
.
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
    fbAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        signoutButton.setEnabled(true);
                        codeText.setText("");
                        statusText.setText("Signed In");
                        resendButton.setEnabled(false);
                        verifyButton.setEnabled(false);
                        FirebaseUser user = task.getResult().getUser();

                    } else {
                        if (task.getException() instanceof 
				FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid
                        }
                    }
                }
            });
}

Resending the Verification Code

The next step in this tutorial is to add the resendCode() method. This method is called when the Resend Code button is clicked and simply makes another call to the verifyPhoneNumber() method of the PhoneAuthProvider instance. This time, however, the method is also passed the resend token which was saved within the onCodeSent() callback method:

public void resendCode(View view) {

    String phoneNumber = phoneText.getText().toString();

    setUpVerificatonCallbacks();

    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,
            60,
            TimeUnit.SECONDS,
            this,
            verificationCallbacks,
            resendToken);
}

Signing Out

The final step before testing the project is to implement the signOut() onClick method as follows:

public void signOut(View view) {
    fbAuth.signOut();
    statusText.setText("Signed Out");
    signoutButton.setEnabled(false);
    sendButton.setEnabled(true);
}

Testing the App

Compile and run the app, enter a phone number and click on the Send Code button. After the SMS message arrives, enter it into the code text field and click the Verify Code button. If the code is valid, the status TextView will change to indicate that the user is signed in. Click on the Sign Out button and send another verification code. Make a note of the code, tap the Resend Code button and verify that the provider has resent the previous code instead of generating a new one.

Summary

Firebase Phone Number Authentication allows users to be authenticated via a phone number and a verification code. This chapter has demonstrated the use of the Firebase SDK to implement phone authentication within an Android app, including sending the verification code, performing the verification and handling requests to resend the code.