How to Integrate OTP in Android & PHP – Auto SMS Verification

Integrating OTP is not harder as it was before. Now lot’s of tools available around web or app. Android Developers recently introduced SMS Retriever or Verification API, it was introduced to make more secure , because previously some important receiver did this job in android, but there was a problem, with that permission “SMS READ, SMS RETRIVE” some fraud apps were receiving some confidential data of users. So without wasting time on the entire story, we can proceed with the new method of Android OTP Verification:

So First of all you need these things to proceed with this process:

  1. Android Studio (Latest Version)
  2. Xampp (A Tool Which Allows to create Apache server environment)
  3. VSCode (A Tool to create php code)
  4. SMS Service (Twillio Is Best SMS Service Ever)
  5. Firebase for Authentication (You can Use Firebase Authentication, Instead SMS Service)
OTP Integration with Android and PHP
OTP Integration with Android and PHP

Let’s have some understanding on this entire process of verification:

As I previously mentioned the cause why google changed the policy for READ_SMS permission. So in this entire process your app request OTP from your PHP code and then send data to SMS Service where SMS service triggers an SMS to user’s mobile. Once the SMS comes, SMS Retriever API Tells to app about that Text Message. Which makes it easier to detect otp and after receiving it APP call a Verification Request to your server That’s It.

So let’s start with steps to implement the entire Process:

Step 1: You need to add SMS Retriever API in your build.grade file of app under dependencies{….} block

  //Google's Android SMS Retriver API
  implementation 'com.google.android.gms:play-services-auth:17.0.0'
  implementation 'com.google.android.gms:play-services-auth-api-phone:17.0.0'
  //Add Right Before This
  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'com.android.support.test:runner:1.0.2'
  androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

Step 2: If you want to get users phone number, which is connected with it’s Google Play Account. You can easily grab it with GoogleApiClient Builder. Let’s focus on code, but please keep in mind you need to do it onCreate(){} method. So that once user with choose you can easily put the mobile number into EditText of Phone Number or Phone Number Input. Initialize Api Client Builder

// For Activity 
mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .enableAutoManage(this, this)
        .addApi(Auth.CREDENTIALS_API)
        .build();
//For Fragment
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
        .addConnectionCallbacks(this)
        .enableAutoManage(this, this)
        .addApi(Auth.CREDENTIALS_API)
        .build(); // Replace this with getActivity() if needed it's just context here

Step 3: Let’s call this hint request to get the number.

public void getPhoneNumberHint() {
    HintRequest hintRequest =
        new HintRequest.Builder()
            .setPhoneNumberIdentifierSupported(true)
            .build();
    PendingIntent mIntent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
    try {
      startIntentSenderForResult(mIntent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
    } catch (IntentSender.SendIntentException e) {
      e.printStackTrace();
    }
  }

Step 4: Time to get the Hint Request’s Result from Activity (In Simple words time to get the result which is user’s mobile number)

@Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //Result if we want hint number
    // Define it as an constant such as => private int RESOLVE_HINT = 101
    if (requestCode == RESOLVE_HINT) {
      if (resultCode == Activity.RESULT_OK) {
        Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
        MobileNumberInput.setText(credential.getId());
      }
    }

Step 5: Start SMS Retriever / Listener Method, Where Retriever API will start listening for SMS and when it will receive SMS, will send to Broadcast receiver.

public void StartSMSListener() {
    SmsRetrieverClient mClient = SmsRetriever.getClient(this);
    Task<Void> mTask = mClient.startSmsRetriever();
    mTask.addOnSuccessListener(new OnSuccessListener<Void>() {
      @Override public void onSuccess(Void aVoid) {
        layoutInput.setVisibility(View.GONE);
        layoutVerify.setVisibility(View.VISIBLE);
        Toast.makeText(MainActivity.this, "SMS Retriever starts", Toast.LENGTH_LONG).show();
      }
    });
    mTask.addOnFailureListener(new OnFailureListener() {
      @Override public void onFailure(@NonNull Exception e) {
        Toast.makeText(MainActivity.this, "Unable to Find!", Toast.LENGTH_LONG).show();
      }
    });
  }

Step 6: Now it’s time for a Broadcast receiver to receive SMS and send the result to Activity. Create a Java Class “SmsBroadcastReciever.java” under BroadcastReciever Directory or Package in your project.

package com.infotheme.tutorial.smsverification.BroadcastReciever;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.infotheme.tutorial.smsverification.Interfaces.OtpReceivedInterface;
/**
 * Created on : Feb 06, 2020
 * Author     : InfoTheme Inc.
 */
public class SmsBroadcastReciever extends BroadcastReceiver {
  private static final String TAG = "SmsBroadcastReciever";
  private static final String OTP_DELIMITER = "#>";
  OtpReceivedInterface otpReceiveInterface = null;
  // These interfaces will extract the data and status in your activity on request
  public void setOnOtpListeners(OtpReceivedInterface otpReceiveInterface) {
    this.otpReceiveInterface = otpReceiveInterface;
  }
  @Override public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onReceive: ");
    if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      Status mStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
      switch (mStatus.getStatusCode()) {
        case CommonStatusCodes.SUCCESS:
          // Get SMS message contents'
          String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
          Log.d(TAG, "onReceive: failure "+message);
          if (otpReceiveInterface != null) {
            String otp = getVerificationCode(message);
            otpReceiveInterface.onOtpReceived(otp);
          Log.e(TAG, "OTP received: " + otp);
          }
          break;
        case CommonStatusCodes.TIMEOUT:
          // SMS timed out (After 5 minutes)
          Log.d(TAG, "onReceive: failure");
          if (otpReceiveInterface != null) {
            otpReceiveInterface.onOtpTimeout();
          }
          break;
      }
    }
  }


   private String getVerificationCode(String message) {
        String code = null;
        int index = message.indexOf(OTP_DELIMITER);

        if (index != -1) {
            int start = index + 2;
            int length = 4; //OTP Length
            code = message.substring(start, start + length);
            return code;
        }

        return code;
    }
}

Step 7: Now time to Create Otp Listener which we included previous file, Create Directory or package named “Interfaces”, create a java class “OtpReceivedInterface.java” and put below code:

public interface OtpReceivedInterface {
  void onOtpReceived(String otp);
  void onOtpTimeout();
}

Step 8: Now it’s time to register your BroadcastReciever to your AndroidManifest, simply put below code under application block <application …..> </application>

<receiver
        android:name=".BroadcastReciever.SmsBroadcastReciever"
        android:exported="true">
      <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVE" />
      </intent-filter>
</receiver>

Step 9: Here is code of Awesome Layout of Your otp_activity.xml

Step 10: Here is code of OTPActivity.java

Step 11: You will be needed Application Hash for verification using Android SMS Retriever Api, use this code but keep in mind debug key and release key will be different. Create A Class ” AppSignatureHelper.java” Anywhere in your project and add this code.

public class AppSignatureHelper extends ContextWrapper {
  private static final String TAG = "AppSignatureHelper";
  private static final String HASH_TYPE = "SHA-256";
  public static final int NUM_HASHED_BYTES = 9;
  public static final int NUM_BASE64_CHAR = 11;
  public AppSignatureHelper(Context context) {
    super(context);
  }
  /**
   * Get the app signatures for the your current package
   */
  public ArrayList<String> getAppSignatures() {
    ArrayList<String> appCodes = new ArrayList<>();
    try {
      // Get all package signatures for the current package
      String packageName = getPackageName();
      PackageManager packageManager = getPackageManager();
      Signature[] signatures = packageManager.getPackageInfo(packageName,
          PackageManager.GET_SIGNATURES).signatures;
      // For each signature create a compatible hash
      for (Signature signature : signatures) {
        String hash = hash(packageName, signature.toCharsString());
        if (hash != null) {
          appCodes.add(String.format("%s", hash));
        }
      }
    } catch (PackageManager.NameNotFoundException e) {
      Log.e(TAG, "Unable to find package to obtain hash.", e);
    }
    return appCodes;
  }
  private static String hash(String packageName, String signature) {
    String appInfo = packageName + " " + signature;
    try {
      MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
      messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
      byte[] hashSignature = messageDigest.digest();
      // truncated into NUM_HASHED_BYTES
      hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
      // encode into Base64
      String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
      base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
      Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
      return base64Hash;
    } catch (NoSuchAlgorithmException e) {
      Log.e(TAG, "hash:NoSuchAlgorithm", e);
    }
    return null;
  }
}

I am still working on this entire post will be back to you in next few days. As soon i will get time, rest please leave some review on this article. I will be back soon with PHP code.

Leave a Reply

Your email address will not be published.