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

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

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

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

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

Предварительные требования приложения

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

  • MinSdkVersion 19 или выше.
  • КомпиляцияSdkVersion 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:21.0.0'
  implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.2'
}

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 (значение константы 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)