Request SMS Verification in an Android App

To automatically verify phone numbers, you must implement both the client and server portions of the verification flow. This document describes how to implement the client portion in an Android app.

To start the phone number verification flow in an Android app, you send the phone number to your verification server and call the SMS Retriever API to begin listening for an SMS message containing a one-time code for your app. After you receive the message, you send the one-time code back to your server to complete the verification process.

Prerequisites

The SMS Retriever API is available only on Android devices with Play services version 10.2 and newer.

1. Obtain the user's phone number

You can obtain the user's phone number in whatever way is appropriate for your app. Often, it is the best user experience to use the hint picker to prompt the user to choose from the phone numbers stored on the device and thereby avoid having to manually type a phone number. To use the hint picker:

// 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);
}

// 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();  <-- will need to process phone number string
      }
  }
}

2. Start the SMS retriever

When you are ready to verify the user's phone number, get an instance of the SmsRetrieverClient object, call startSmsRetriever, and attach success and failure listeners to the SMS retrieval task:

// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);

// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
// action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();

// Listen for success/failure of the start Task. If in a background thread, this
// can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // Successfully started retriever, expect broadcast intent
    // ...
  }
});

task.addOnFailureListener(new OnFailureListener() {
  @Override
  public void onFailure(@NonNull Exception e) {
    // Failed to start retriever, inspect Exception for more details
    // ...
  }
});

The SMS retrieval task will listen for up to five minutes for an SMS message that contains a unique string that identifies your app.

3. Send the phone number to your server

After you have obtained the user's phone number and started to listen for SMS messages, send the user's phone number to your verification server using any method (usually with an HTTPS POST request).

Your server generates a verification message and sends it by SMS to the phone number you specified. See Perform SMS Verification on the Server.

4. Receive verification messages

When a verification message is received on the user's device, Play services explicitly broadcasts to your app a SmsRetriever.SMS_RETRIEVED_ACTION Intent, which contains the text of the message. Use a BroadcastReceiver to receive this verification message.

In the BroadcastReceiver's onReceive handler, get the text of the verification message from the Intent's extras:

/**
 * BroadcastReceiver to wait for SMS messages. This can be registered either
 * in the AndroidManifest or at runtime.  Should filter Intents on
 * SmsRetriever.SMS_RETRIEVED_ACTION.
 */
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:
          // Get SMS message contents
          String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
          // Extract one-time code from the message and complete verification
          // by sending the code back to your server.
          break;
        case CommonStatusCodes.TIMEOUT:
          // Waiting for SMS timed out (5 minutes)
          // Handle the error ...
          break;
      }
    }
  }
}

Register this BroadcastReceiver with the intent filter com.google.android.gms.auth.api.phone.SMS_RETRIEVED (the value of the SmsRetriever.SMS_RETRIEVED_ACTION constant) in your app's AndroidManifest.xml file, as in the following example, or dynamically using Context.registerReceiver.

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

5. Send the one-time code from the verification message to your server

Now that you have the text of the verification message, use a regular expression or some other logic to get the one-time code from the message. The format of the one-time code depends on how you implemented them in your server.

Finally, send the one-time code to your server over a secure connection. When your server receives the one-time code, it records that the phone number has been verified.

Optional: Save the phone number with Smart Lock for Passwords

Optionally, after the user has verified their phone number, you can prompt the user to save this phone number account with Smart Lock for Passwords so it will be available automatically in other apps and on other devices without having to type or select the phone number again:

Credential credential = new Credential.Builder(phoneNumberString)
        .setAccountType("https://signin.example.com")  // a URL specific to the app
        .setName(displayName)  // optional: a display name if available
        .build();
Auth.CredentialsApi.save(apiClient, credential).setResultCallback(
            new ResultCallback() {
                public void onResult(Result result) {
                    Status status = result.getStatus();
                    if (status.isSuccess()) {
                        Log.d(TAG, "SAVE: OK");  // already saved
                    } else if (status.hasResolution()) {
                        // Prompt the user to save
                        status.startResolutionForResult(this, RC_SAVE);
                    }
                }
            });

Then, after the user reinstalls the app or installs the app on a new device, you can retrieve the saved phone number without having ask the user again for their phone number:

// On the next install, retrieve the phone number
mCredentialRequest = new CredentialRequest.Builder()
    .setAccountTypes("https://signin.example.com")  // the URL specific to the developer
    .build();
Auth.CredentialsApi.request(apiClient, mCredentialRequest).setResultCallback(
    new ResultCallback<CredentialRequestResult>() {
        public void onResult(CredentialRequestResult credentialRequestResult) {
            if (credentialRequestResult.getStatus().isSuccess()) {
                credentialRequestResult.getCredential().getId();  // this is the phone number
            }
        }
    });

// Then, initiate verification and sign the user in (same as original verification logic)