Solicita la verificación por SMS en una app para Android

Para verificar automáticamente los números de teléfono, debes implementar las partes del cliente y del servidor del flujo de verificación. En este documento, se describe cómo implementar la parte del cliente en una app para Android.

A fin de iniciar el flujo de verificación del número de teléfono en una app para Android, debes enviar el número al servidor de verificación y llamar a la API de SMS Retriever para comenzar a detectar un mensaje SMS que contiene un código único para tu app. Después de recibir el mensaje, envía el código único a tu servidor para completar el proceso de verificación.

Antes de comenzar

Para preparar tu app, completa los pasos que se indican en las siguientes secciones.

Requisitos previos de la app

Asegúrate de que el archivo de compilación de tu app use los siguientes valores:

  • Una minSdkVersion de 19 o superior
  • Una compileSdkVersion de 28 o posterior

Configurar tu app

En el archivo build.gradle a nivel de proyecto, incluye el repositorio Maven de Google y el repositorio central de Maven en las secciones buildscript y allprojects:

buildscript {
    repositories {
        google()
        mavenCentral()
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

Agrega la dependencia de los Servicios de Google Play para la API de SMS Retriever al archivo de compilación Gradle de tu módulo, que suele ser app/build.gradle:

dependencies {
  implementation 'com.google.android.gms:play-services-auth:20.2.0'
  implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
}

1. Obtén el número de teléfono del usuario

Puedes obtener el número de teléfono del usuario de la manera que resulte apropiada para tu app. A menudo, es mejor usar el selector de sugerencias para solicitarle que elija entre los números de teléfono almacenados en el dispositivo y, por lo tanto, no tenga que escribir manualmente un número de teléfono. Para usar el selector de sugerencias, haz lo siguiente:

// 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. Iniciar la recuperación de SMS

Cuando estés listo para verificar el número de teléfono del usuario, obtén una instancia del objeto SmsRetrieverClient, llama a startSmsRetriever y adjunta objetos de escucha de ejecución correcta y con errores a la tarea de recuperación de SMS:

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

La tarea de recuperación de SMS detectará hasta cinco minutos para un mensaje SMS que contenga una string única que identifique tu app.

3. Envía el número de teléfono a tu servidor

Después de obtener el número de teléfono del usuario y comenzar a detectar los mensajes SMS, envía ese número a tu servidor de verificación mediante cualquier método (por lo general, con una solicitud POST HTTPS).

Tu servidor genera un mensaje de verificación y lo envía por SMS al número de teléfono que especificaste. Consulta Realiza la verificación por SMS en el servidor.

4. Recibir mensajes de verificación

Cuando se recibe un mensaje de verificación en el dispositivo del usuario, los Servicios de Play transmiten de manera explícita a tu app un intent SmsRetriever.SMS_RETRIEVED_ACTION, que contiene el texto del mensaje. Usa un BroadcastReceiver para recibir este mensaje de verificación.

En el controlador onReceive de BroadcastReceiver, obtén el texto del mensaje de verificación de los extras de Intent:

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

Registra este BroadcastReceiver con el filtro de intents com.google.android.gms.auth.api.phone.SMS_RETRIEVED (el valor de la constante SmsRetriever.SMS_RETRIEVED_ACTION) y el permiso com.google.android.gms.auth.api.phone.permission.SEND (el valor de la constante SmsRetriever.SEND_PERMISSION) en el archivo AndroidManifest.xml de tu app, como se muestra en el siguiente ejemplo, o de forma dinámica con Context.registerReceiver.

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

5. Envía el código único del mensaje de verificación a tu servidor.

Ahora que tienes el texto del mensaje de verificación, usa una expresión regular o alguna otra lógica para obtener el código de uso único del mensaje. El formato del código de uso único depende de cómo lo implementaste en tu servidor.

Por último, envía el código único a tu servidor a través de una conexión segura. Cuando el servidor recibe el código único, registra que se verificó el número de teléfono.

Opcional: Guarda el número de teléfono con Smart Lock para contraseñas

De manera opcional, después de que el usuario verifique su número de teléfono, puedes solicitarle que guarde esta cuenta con Smart Lock para contraseñas a fin de que esté disponible automáticamente en otras apps y en otros dispositivos sin tener que volver a escribir o seleccionar el número:

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

Luego, después de que el usuario reinstale la app o la instale en un dispositivo nuevo, podrás recuperar el número de teléfono guardado sin preguntarle de nuevo por su número de teléfono:

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