Richiedere la verifica SMS in un'app Android

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Per verificare automaticamente i numeri di telefono, devi implementare sia la parte client sia quella del server del flusso di verifica. Questo documento descrive come implementare la sezione client in un'app per Android.

Per avviare il flusso di verifica del numero di telefono in un'app Android, invia il numero di telefono al tuo server di verifica e chiama l'API SMS Fetcher per iniziare l'ascolto di un messaggio SMS contenente un codice monouso per la tua app. Dopo aver ricevuto il messaggio, invia nuovamente il codice una tantum al tuo server per completare la procedura di verifica.

Prima di iniziare

Per preparare l'app, completa i passaggi nelle sezioni seguenti.

Prerequisiti dell'app

Assicurati che il file di build della tua app utilizzi i seguenti valori:

  • Una versione minSdk di 19 o superiore
  • Una compilazioneSdkVersion di 28 o superiore

Configura la tua app

Nel file build.gradle a livello di progetto, includi il repository Maven di Google e il repository centrale Maven nelle sezioni buildscript e allprojects:

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

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

Aggiungi la dipendenza Google Play Services per l'API SMS Fetcher al file di build Gradle del tuo modulo, che in genere è app/build.gradle:

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

1. Ottieni il numero di telefono dell'utente

Puoi ottenere il numero di telefono dell'utente come preferisci per la tua app. Spesso è meglio utilizzare il selettore di suggerimento per chiedere all'utente di scegliere tra i numeri di telefono memorizzati sul dispositivo per evitare di dover digitare manualmente un numero di telefono. Per utilizzare il selettore di suggerimenti:

// 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. Avvia il recupero SMS

Quando è tutto pronto per verificare il numero di telefono dell'utente, ottenere un'istanza dell'oggetto SmsRetrieverClient, chiamare il numero startSmsRetriever e associare gli ascoltatori riusciti e non riusciti all'attività di recupero degli 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
    // ...
  }
});

L'attività di recupero degli SMS ascolterà per cinque minuti un messaggio SMS contenente una stringa univoca che identifica la tua app.

3. Invia il numero di telefono al server

Dopo aver ottenuto il numero di telefono dell'utente e aver iniziato ad ascoltare gli SMS, invia il numero di telefono dell'utente al tuo server di verifica utilizzando qualsiasi metodo (di solito con una richiesta POST HTTPS).

Il tuo server genera un messaggio di verifica e lo invia tramite SMS al numero di telefono specificato. Vedi Eseguire la verifica SMS sul server.

4. Ricevi messaggi di verifica

Quando viene ricevuto un messaggio di verifica sul dispositivo dell'utente, Play Services trasmette esplicitamente all'app un intent SmsRetriever.SMS_RETRIEVED_ACTION, contenente il testo del messaggio. Usa un BroadcastReceiver per ricevere il messaggio di verifica.

Nel gestore onReceive di BroadcastReceiver, recupera il testo del messaggio di verifica dagli extra degli 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 questo BroadcastReceiver con il filtro per intent com.google.android.gms.auth.api.phone.SMS_RETRIEVED (il valore della costante SmsRetriever.SMS_RETRIEVED_ACTION) e con l'autorizzazione com.google.android.gms.auth.api.phone.permission.SEND (il valore della costante SmsRetriever.SEND_PERMISSION) nel file AndroidManifest.xml dell'app, come nell'esempio seguente, oppure utilizzando dinamicamente 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. Invia il codice monouso dal messaggio di verifica al tuo server

Ora che hai il testo del messaggio di verifica, utilizza un'espressione regolare o un'altra logica per ottenere il codice monouso dal messaggio. Il formato del codice monouso dipende da come è stato implementato sul server.

Infine, invia il codice monouso al tuo server con una connessione sicura. Quando il server riceve il codice monouso, registra che il numero di telefono è stato verificato.

(Facoltativo) Salva il numero di telefono con Smart Lock per password

In via facoltativa, dopo che l'utente ha verificato il proprio numero di telefono, puoi chiedere all'utente di salvare questo account tramite Smart Lock per password in modo che sia disponibile automaticamente in altre app e su altri dispositivi senza dover digitare o selezionare nuovamente il numero di telefono:

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

Dopo che l'utente ha reinstallato l'app o l'ha installata su un nuovo dispositivo, puoi recuperare il numero di telefono salvato senza chiedere nuovamente all'utente il suo numero di telefono:

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