Android Effective phone number verification

To build apps that make use of phone numbers, it’s often crucial to verify that the user owns a number. Doing this can be tricky from a UX perspective, not least in understanding phone number formats in different locales, but also in providing a verification mechanism that isn’t cumbersome or using intrusive device permissions, such as the ability to read all of a user’s SMS.




There are many libraries for efficient pre-built phone authentication, such as Firebase Phone Auth, but if you are an advanced developer and need to build this functionality yourself, Google Play Services has two new APIs that help you obtain a user’s phone number and verify it via SMS without device permissions: the Phone Selector and SMS Retriever. Apps like Flipkart have seen a 12% increase of success rates in phone number sign-up flows using these methods.

The steps for using these with your server can be seen here:

Android Effective phone number verification

In this post we’ll show the code that you need to provide a phone number selector to your users, and then use this with the SMS retriever API to request a verification code from your server that the Android device will automatically receive and parse with no input from the user.

Note: Before you begin you’ll need to build and test this is a device with a phone number that can receive SMS and runs Google Play services 10.2.x and higher.

Using the Phone Selector to get the number

The first step is to have the user initiate SMS verification from within your app. Your app might prompt the user to enter a phone number, and you can use the Phone Selector to make this easier, using code like this:

// Construct a request for phone numbers and show the picker
private void requestHint() {
    HintRequest hintRequest = new HintRequest.Builder()
           .setPhoneNumberIdentifierSupported(true)
           .build();

    PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(
            apiClient, hintRequest);
    startIntentSenderForResult(intent.getIntentSender(),
            RESOLVE_HINT, null, 0, 0, 0);
}

The HintRequest builder tells Play Services that a phone number identifier is needed. This is then used to create and start an intent, which will show a Play Service dialog to the user allowing them to select their phone number to share with the app. This API does not require any permissions, and displays the number(s) available on the phone or Google Account for the user to select.

When the user selects a phone number it will be returned to the application in onActivityResult in E164 format on devices running the latest version of Play Services. Note that in some cases, depending on your phone, you may not get a phone number, so be sure to check if the credential is non-null. If you don’t have a number, you’ll need to provide a way for your user to type it in manually.

// Obtain the phone number from the result
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == RESOLVE_HINT) {
      if (resultCode == RESULT_OK) {
          Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
          // credential.getId(); <-- E.164 format phone number on 10.2.+ devices
      }
  }
}

At this point you’ll have a phone number string for your user. While this is useful, you’ll likely want to verify that the user owns this particular number, for example to allow them to send or retrieve message with other users or identifying themselves with this number.

Using the SMS Verification API to verify the number

A simple way to verify phone number ownership is by sending an SMS to the number, containing a one time verification code, and having them enter that into your app. The SMS Verification API gives you the ability for the app to listen for an incoming SMS from which it can parse the code automatically.




To get started, your app will SmsRetrieverClient with code like this:

SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);

Task<Void> task = client.startSmsRetriever();

task.addOnSuccessListener(new OnSuccessListener<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // successfully started an SMS Retriever for one SMS message
  }
});

task.addOnFailureListener(new OnFailureListener() {
  @Override
  public void onFailure(@NonNull Exception e) {
  });
);

It’s pretty simple — you get an SMS Retriever client and then start a task for it. The task has an on Success listener as well as an on Failure one to override. After starting the SMS Retriever, you’d send the user’s phone number to your server and start it’s workflow for generating the message and sending it to that number.

The message needs to be constructed in a specific way. The message must fit in an SMS message, so it can’t be longer than 140 bytes. It needs to start with a specific prefix: ‘<#>’ or two consecutive zero-width space characters (U+200B). See the documentation for your more information. It must end with an 11-character hash that identifies your app, described below.

Example:

<#> Use 123456 as your verification code in Example App!

FA+9qCX9VSu

The one-time verification code can be any string: you can simply generate a random number. The message needs to end with a hash that is determined according to the procedures here. Google Play services will use this hash to determine which app the verification message is for. You only need to generate this hash once for your app package and signing certificate: it won’t change and shouldn’t be supplied by the client app.

Your server can then send the message to the phone using your existing SMS infrastructure or service. When this message is received, Google Play services broadcasts an intent which contains the text of the message. Here’s the code:

public class MySMSBroadcastReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

      switch(status.getStatusCode()) {
        case CommonStatusCodes.SUCCESS:
          String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
          break;
        case CommonStatusCodes.TIMEOUT:
          break;
      }
    }
  }
}

In the onReceive of the broadcast receiver you get the extras, and pull the status from there. If the status indicates that the message was successfully received, you can pull the message from the extras. From here you can parse out the verification code and send it back to your server to confirm phone number ownership.

For more information, check out the full documentation and this year’s Google I/O talk.

Testimonies of Early Adopters

Our early partners who use this API love it. Here are some testimonials from them:

Twilio observed and blogged that Android SMS Verification has never been easier.

“If you’re a developer building mobile apps on Android that use phone numbers to register and identify user accounts, you should be using Twilio Verification SDK for Android for the quickest way to solve the problem of providing a smooth, secure and easy sign-up flow.” – Simon Thorpe, Product Owner at Twilio

Authy loved the fact that these APIs work with their existing SMS infrastructure without requiring many changes.

“Adding Phone Selector + SMS Retriever into Authy 2FA app delivers magical UX for users while retaining the high security our application requires.” — Serge Kruppa, Head of Authy Engineering

Telesign observed better UX, increased security and higher conversion rates with the same backend framework.

“One significant advantage of this verification mode with lower friction is that customers might be able to see increased conversion rates for user sign-up and registration scenarios.

Enhanced security is also a benefit as Google Play Services only provides access to the SMS message to the targeted application based on the application hash inside the message.” — Priyesh Jain (Post author)

 

Source: android developers blog