Запрос SMS-подтверждения в приложении для Android

Для автоматической проверки телефонных номеров необходимо реализовать как клиентскую, так и серверную части потока проверки. В этом документе описывается, как реализовать клиентскую часть в приложении для Android.

Чтобы запустить процесс проверки номера телефона в приложении для Android, вы отправляете номер телефона на свой сервер проверки и вызываете API-интерфейс SMS Retriever, чтобы начать прослушивание SMS-сообщения, содержащего одноразовый код для вашего приложения. После получения сообщения вы отправляете одноразовый код обратно на свой сервер для завершения процесса проверки.

Прежде чем вы начнете

Чтобы подготовить приложение, выполните действия, описанные в следующих разделах.

Требования к приложению

Убедитесь, что в файле сборки вашего приложения используются следующие значения:

  • MinSdkVersion 19 или выше
  • compileSdkVersion 28 или выше

Настройте свое приложение

В файле build.gradle на уровне проекта включите репозиторий Google Maven и центральный репозиторий Maven как в buildscript и в разделы allprojects :

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

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

Добавьте зависимость сервисов Google Play для SMS Retriever API в файл сборки вашего модуля Gradle , обычно это 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. Получите номер телефона пользователя

Вы можете получить номер телефона пользователя любым способом, подходящим для вашего приложения. Часто лучше всего использовать средство выбора подсказок, чтобы предложить пользователю сделать выбор из телефонных номеров, хранящихся на устройстве, и тем самым избежать необходимости вводить номер телефона вручную. Чтобы использовать средство выбора подсказок:

// 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. Запустите средство получения SMS

Когда вы будете готовы проверить номер телефона пользователя, получите экземпляр объекта SmsRetrieverClient , вызовите startSmsRetriever и присоедините обработчики успеха и неудачи к задаче извлечения 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
    // ...
  }
});

Задача извлечения SMS будет прослушивать до пяти минут SMS-сообщение, содержащее уникальную строку, идентифицирующую ваше приложение.

3. Отправьте номер телефона на свой сервер

После того, как вы получили номер телефона пользователя и начали прослушивать SMS-сообщения, отправьте номер телефона пользователя на ваш сервер проверки любым способом (обычно с помощью HTTPS POST-запроса).

Ваш сервер формирует проверочное сообщение и отправляет его по SMS на указанный вами номер телефона. См. Выполнение SMS-проверки на сервере .

4. Получайте сообщения с подтверждением

Когда на устройство пользователя поступает проверочное сообщение, сервисы Play явно передают вашему приложению намерение SmsRetriever.SMS_RETRIEVED_ACTION , содержащее текст сообщения. Используйте BroadcastReceiver для получения этого проверочного сообщения.

В обработчике onReceive BroadcastReceiver получите текст проверочного сообщения из дополнений 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;
      }
    }
  }
}

Зарегистрируйте этот BroadcastReceiver с фильтром намерений com.google.android.gms.auth.api.phone.SMS_RETRIEVED (значение константы SmsRetriever.SMS_RETRIEVED_ACTION ) и разрешением com.google.android.gms.auth.api.phone.permission.SEND . com.google.android.gms.auth.api.phone.permission.SEND (значение константы SmsRetriever.SEND_PERMISSION ) в файле AndroidManifest.xml вашего приложения, как в следующем примере, или динамически с использованием 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. Отправьте одноразовый код из проверочного сообщения на свой сервер.

Теперь, когда у вас есть текст проверочного сообщения, используйте регулярное выражение или другую логику, чтобы получить одноразовый код из сообщения. Формат одноразового кода зависит от того, как вы их реализовали на своем сервере.

Наконец, отправьте одноразовый код на свой сервер через безопасное соединение. Когда ваш сервер получает одноразовый код, он записывает, что номер телефона был подтвержден.

Необязательно: сохраните номер телефона с помощью Smart Lock для паролей.

При желании после того, как пользователь подтвердит свой номер телефона, вы можете предложить пользователю сохранить этот номер телефона с помощью Smart Lock для паролей, чтобы он был автоматически доступен в других приложениях и на других устройствах без необходимости вводить или выбирать номер телефона снова. :

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

Затем, после того как пользователь переустановит приложение или установит приложение на новое устройство, вы можете получить сохраненный номер телефона, не запрашивая у пользователя его номер телефона снова:

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