Demander la validation par SMS dans une application Android

Pour valider automatiquement les numéros de téléphone, vous devez mettre en œuvre les parties client et serveur du processus de validation. Ce document explique comment implémenter la partie client dans une application Android.

Pour lancer le flux de validation du numéro de téléphone dans une application Android, envoyez le numéro de téléphone à votre serveur de validation et appelez l'API SMS Recovery pour commencer à écouter un SMS contenant un code à usage unique pour votre application. Une fois que vous avez reçu le message, envoyez le code à usage unique à votre serveur pour terminer le processus de validation.

Avant de commencer

Pour préparer votre application, suivez les étapes décrites dans les sections suivantes.

Prérequis pour les applications

Assurez-vous que le fichier de compilation de votre application utilise les valeurs suivantes:

  • minSdkVersion 19 ou supérieur
  • Compiler compiléSdkVersion 28 ou version ultérieure

Configurer votre application

Dans le fichier build.gradle au niveau du projet, incluez le dépôt Maven Google et le dépôt central Maven dans les sections buildscript et allprojects:

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

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

Ajoutez la dépendance des services Google Play pour l'API SMS Fetcher à votre fichier de compilation Gradle de votre module, qui est généralement 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. Obtenir le numéro de téléphone de l'utilisateur

Vous pouvez obtenir le numéro de téléphone de l'utilisateur de la manière qui convient à votre application. Bien souvent, nous vous recommandons d'utiliser le sélecteur de conseils pour inviter l'utilisateur à choisir parmi les numéros de téléphone stockés sur l'appareil, et ainsi éviter de saisir manuellement un numéro de téléphone. Pour utiliser le sélecteur d'indications:

// 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. Démarrer l'outil de récupération de SMS

Lorsque vous êtes prêt à vérifier le numéro de téléphone de l'utilisateur, obtenez une instance de l'objet SmsRetrieverClient, appelez startSmsRetriever et associez des écouteurs de réussite et d'échec à la tâche de récupération des 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 tâche de récupération de SMS écoutera jusqu'à cinq minutes pour un SMS contenant une chaîne unique qui identifie votre application.

3. Envoyer le numéro de téléphone à votre serveur

Une fois que vous avez obtenu le numéro de téléphone de l'utilisateur et commencé à écouter les SMS, envoyez ce numéro à votre serveur de validation via n'importe quelle méthode (généralement avec une requête HTTPS POST).

Votre serveur génère un message de validation et l'envoie par SMS au numéro de téléphone spécifié. Pour en savoir plus, consultez l'article Effectuer la validation par SMS sur le serveur.

4. Recevoir des messages de validation

Lorsqu'un message de validation est reçu sur l'appareil de l'utilisateur, les services Play diffusent explicitement dans un intent SmsRetriever.SMS_RETRIEVED_ACTION un intent contenant le texte du message. Utilisez un BroadcastReceiver pour recevoir ce message de validation.

Dans le gestionnaire onReceive de BroadcastReceiver, récupérez le texte du message de validation à partir des bonus d'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;
      }
    }
  }
}

Enregistrez cette BroadcastReceiver avec le filtre d'intent com.google.android.gms.auth.api.phone.SMS_RETRIEVED (la valeur de la constante SmsRetriever.SMS_RETRIEVED_ACTION) et l'autorisation com.google.android.gms.auth.api.phone.permission.SEND (la valeur de la constante SmsRetriever.SEND_PERMISSION) dans le fichier AndroidManifest.xml de votre application, comme dans l'exemple suivant, ou à l'aide de 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. Envoyer le code à usage unique du message de validation vers votre serveur

Maintenant que vous avez le texte du message de validation, utilisez une expression régulière ou une autre logique pour obtenir le code à usage unique du message. Le format du code à usage unique dépend de la façon dont vous les avez mis en œuvre sur votre serveur.

Enfin, envoyez le code à usage unique à votre serveur via une connexion sécurisée. Lorsque votre serveur reçoit le code à usage unique, il indique que le numéro de téléphone a été validé.

Facultatif: Enregistrez le numéro de téléphone avec Smart Lock pour les mots de passe

Une fois que l'utilisateur a validé son numéro de téléphone, vous pouvez l'inviter à enregistrer ce compte avec Smart Lock pour les mots de passe. Il sera ainsi automatiquement disponible dans d'autres applications et sur d'autres appareils sans avoir à saisir ni sélectionner à nouveau le numéro de téléphone:

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

Ensuite, après que l'utilisateur a réinstallé l'application ou installé celle-ci sur un nouvel appareil, vous pouvez récupérer le numéro de téléphone enregistré sans avoir à demander à nouveau son numéro de téléphone:

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