background-shape

Many apps require their users to be authenticated. So for the purpose of authenticating the apps uses phone number authentication inside their apps.

When you use phone number authentication, you have many benefits like

  • Preventing Fake Users: When you use phone authentication, the user can’t be able to register for multiple accounts, as for each account a unique phone number is needed.
  • Increase User Value: When you have all the users verified by a phone number the value of your user base increases.
  • Increased Security and User Experience: Nowadays more people using apps and remembering passwords are a headache for many users, so they end up using weak passwords. Using phone authentication increases security and user experience, as the user does not need to create and remember passwords, they will enter their number and then they can receive a temporary authentication code by SMS.

Why Firebase Phone Authentication?

For implementing phone authentication you need to pay for SMS service, but with firebase, you can do it for FREE, isn’t it awesome? The free plan of firebase has Ten Thousand Verification per month.  That’s enough for the starter apps I guess, but yes if you exceed this limit, you need to pay.

Before we begin, we need to setup firebase for our android project

  1. Add Firebase to your Android Project. If not familiar visit here.

  2. Using the Firebase Android BoM, declare the dependency for the Firebase Authentication Android library in your module (app-level) Gradle file (usually app/build.gradle).

    dependencies {
        // **Import the BoM for the Firebase platform**
        implementation platform('com.google.firebase:firebase-bom:28.0.1')
    
        // **Declare the dependency for the Firebase Authentication library**
        implementation 'com.google.firebase:firebase-auth'
    }
    

    When using the BoM, you don’t specify versions in Firebase library dependencies

  3. Add your SHA-1 and SHA-256 to your firebase project.

Enabling Phone Authentication for Firebase Project

  1. To sign in users by SMS, you must first enable the Phone Number sign-in method for your Firebase project:

    • In the Firebase console, open the Authentication section.
    • On the Sign-in Method page, enable the Phone Number sign-in method.
  2. Enable App Verification

    Firebase must be able to verify that phone number sign-in requests are coming from your app, and to verify that enable SafetyNet which automatically verifies the device legitimate if google play services is installed. To enable it visit this link.

Send Verification code to User’s Phone

To initiate phone number sign-in, present the user an interface that prompts them to type their phone number.

Step 1

After you have user’s phone number, pass their phone number to the PhoneAuthProvider.verifyPhoneNumber method to request that Firebase verify the user’s phone number.

private void sendOtp(String mobile_no) {
      PhoneAuthOptions options =
              PhoneAuthOptions.newBuilder(mAuth)
                      .setPhoneNumber(mobile_no)// Phone number to verify
                      .setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
                      .setActivity(this)          // Activity (for callback binding)
                      .setCallbacks(mCallbacks)   // OnVerificationStateChangedCallbacks
                      .build();
      PhoneAuthProvider.verifyPhoneNumber(options);
}

Step 2

When you call PhoneAuthProvider.verifyPhoneNumber, you must also provide an instance of OnVerificationStateChangedCallbacks, which contains implementations of the callback functions that handle the results of the request.

String mVerificationId;
PhoneAuthProvider.ForceResendingToken mResendToken;
private final PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
        @Override
        public void onVerificationCompleted(PhoneAuthCredential credential) {
            //Getting the code sent by SMS
            String code = credential.getSmsCode();
            //sometime the code is not detected automatically
            //in this case the code will be null
            //so user has to manually enter the code
            if (code != null) {
                verifyVerificationCode(code); // we shall se this implementation next
            }
        }

        @Override
        public void onVerificationFailed(FirebaseException e) {
            // This callback is invoked in an invalid request for verification is made,
            // for instance if the the phone number format is not valid.
            if (e instanceof FirebaseAuthInvalidCredentialsException) {
                // Invalid request
                // ...
            } else if (e instanceof FirebaseTooManyRequestsException) {
                // The SMS quota for the project has been exceeded
                // ...
            }
            // Show a message and update the UI
            // ...
        }

        @Override
        public void onCodeSent(@NonNull String verificationId,@NonNull PhoneAuthProvider.ForceResendingToken token) {
            super.onCodeSent(verificationId, token);
            // Save verification ID and resending token so we can use them later
            mVerificationId = verificationId;
            mResendToken = token;
        }
    };

Step 3

To check that OTP entered by user matches the one sent by Firebase, we are creating a PhoneAuthCredential object.

private void verifyVerificationCode(String code) {
        try {
            //creating the credential
            PhoneAuthCredential credential = PhoneAuthProvider.getCredential(mVerificationId, code);
            //signing the user
            signInWithPhoneAuthCredential(credential);
        } catch (Exception e) {
            myApp.showSnackBarForErrors(EmployerOTP.this,getString(R.string.if_no_auth));
        }
    }

Step 4

Sign in the user to your App.

private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
      mAuth.signInWithCredential(credential)
              .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                  @Override
                  public void onComplete(@NonNull Task<AuthResult> task) {
                      if (task.isSuccessful()) {
                          // Sign in success, update UI with the signed-in user's information
                                     FirebaseUser user = task.getResult().getUser();
                      } else {
                          String message = getString(R.string.error_message);
                          if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                              message = getString(R.string.invalid_code);
                          }
                          Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG);
                          snackbar.setAction(R.string.dismiss, new View.OnClickListener() {
                              @Override
                              public void onClick(View v) {
                              }
                          });
                          snackbar.show();
                      }
                  }
              });
  }

To get User’s instance and Log out User

// get current user signed in
FirebaseAuth.getInstance().getCurrentUser();
// log out current user
FirebaseAuth.getInstance().signOut();

There is a Tip!

Yes, you read it right. During your development process to do verification of user you might see frequent crashes during the verification process of your app.

This crash is something like this which says

Crash Auth

This error or crash (apparently 😅), created problems for users during authentication and and after researching a while, I got to know that this happens due to either low memory on phone or low network connection. When you maintain log with verification id i.e. Log.d(TAG, verificationId) it will show null in verificationId and as per the error it says cannot create PhoneAuthCredential without verification proof and verificationId is our verification proof.

Solution:

  1. Clear phone cache and try signing again.

  2. Go to app info and clear cache and Restore Instance State like the code programmatically.

    private String verificationId;
    private static final String KEY_VERIFICATION_ID = "key_verification_id";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_otp);
    
        // Restore instance state
        // put this code after starting phone number verification
        if (verificationId == null && savedInstanceState != null) {
            onRestoreInstanceState(savedInstanceState);
        }
    }//end of onCreate
    
    // over-ride these 2 methods
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(KEY_VERIFICATION_ID,verificationId);
    }
    
    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        verificationId = savedInstanceState.getString(KEY_VERIFICATION_ID);
    }
    

    What happens is the verificationId is stored locally and it is lost sometimes due to less memory. So to tackle this situation we have to restore Instance State which won’t let the verificationId to be lost.

Some Helpful points to implement effective authentication

  • Use fictional numbers instead of your real ones for testing as Firebase to save your project from abuse blocks that number for a time period from signing in again.

  • Use PinText view for better User Experience to autofill the OTP.

    https://camo.githubusercontent.com/609e136fd2579a1b900053b92b99b16ce521c03ba8bbb380b37e7329c8953805/68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f31724c325746597563793641463159344c342f67697068792e676966

Hope it helped. Happy Coding 😀