Запросить одноразовое согласие на чтение проверочного кода по SMS

На этой странице описывается, как использовать API согласия пользователя SMS для запроса согласия пользователя на чтение одного проверочного сообщения SMS. Если пользователь дает согласие, API возвращает текст сообщения, из которого можно получить код подтверждения и завершить процесс проверки.

Установить зависимости

Включите компонент аутентификации Play Services в файл build.gradle вашего приложения:

implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:17.4.0'

1. Получите номер телефона пользователя

Если у вас нет номера телефона пользователя, запросите его перед запуском процесса проверки по SMS.

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

Котлин

private val CREDENTIAL_PICKER_REQUEST = 1  // Set to an unused request code

// Construct a request for phone numbers and show the picker
private fun requestHint() {
    val hintRequest = HintRequest.Builder()
        .setPhoneNumberIdentifierSupported(true)
        .build()
    val credentialsClient = Credentials.getClient(this)
    val intent = credentialsClient.getHintPickerIntent(hintRequest)
    startIntentSenderForResult(
        intent.intentSender,
        CREDENTIAL_PICKER_REQUEST,
        null, 0, 0, 0
    )
}

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        CREDENTIAL_PICKER_REQUEST ->
            // Obtain the phone number from the result
            if (resultCode == Activity.RESULT_OK && data != null) {
                val credential = data.getParcelableExtra<Credential>(Credential.EXTRA_KEY)
                // credential.getId();  <-- will need to process phone number string
            }
        // ...
    }
}

Джава

private static final int CREDENTIAL_PICKER_REQUEST = 1;  // Set to an unused request code

// Construct a request for phone numbers and show the picker
private void requestHint() throws IntentSender.SendIntentException {
    HintRequest hintRequest = new HintRequest.Builder()
            .setPhoneNumberIdentifierSupported(true)
            .build();
    PendingIntent intent = Credentials.getClient(this).getHintPickerIntent(hintRequest);
    startIntentSenderForResult(intent.getIntentSender(),
            RESOLVE_HINT, null, 0, 0, 0);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case CREDENTIAL_PICKER_REQUEST:
            // Obtain the phone number from the result
            if (resultCode == RESULT_OK) {
                Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
                // credential.getId();  <-- will need to process phone number string
            }
            break;
        // ...
    }
}

2. Начните прослушивать входящие сообщения.

Затем вызовите метод startSmsUserConsent() API согласия пользователя SMS, чтобы начать прослушивание входящих сообщений. Если вы знаете номер телефона, с которого придет SMS-сообщение, укажите его (в противном случае передайте null ). Таким образом, API согласия пользователя SMS будет активироваться только для сообщений с этого номера.

Чтобы начать слушать:

Котлин

// Start listening for SMS User Consent broadcasts from senderPhoneNumber
// The Task<Void> will be successful if SmsRetriever was able to start
// SMS User Consent, and will error if there was an error starting.
val task = SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber /* or null */)

Джава

// Start listening for SMS User Consent broadcasts from senderPhoneNumber
// The Task<Void> will be successful if SmsRetriever was able to start
// SMS User Consent, and will error if there was an error starting.
Task<Void> task = SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber /* or null */);

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

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

  • Сообщение содержит буквенно-цифровую строку из 4–10 символов, содержащую хотя бы одну цифру.
  • Если вы указали номер телефона отправителя, сообщение было отправлено по этому номеру.

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

Котлин

private val SMS_CONSENT_REQUEST = 2  // Set to an unused request code
private val smsVerificationReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
            val extras = intent.extras
            val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status

            when (smsRetrieverStatus.statusCode) {
                CommonStatusCodes.SUCCESS -> {
                    // Get consent intent
                    val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
                    try {
                        // Start activity to show consent dialog to user, activity must be started in
                        // 5 minutes, otherwise you'll receive another TIMEOUT intent
                        startActivityForResult(consentIntent, SMS_CONSENT_REQUEST)
                    } catch (e: ActivityNotFoundException) {
                        // Handle the exception ...
                    }
                }
                CommonStatusCodes.TIMEOUT -> {
                    // Time out occurred, handle the error.
                }
            }
        }
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    // ...

    val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
    registerReceiver(smsVerificationReceiver, SmsRetriever.SEND_PERMISSION, intentFilter)
}

Джава

private static final int SMS_CONSENT_REQUEST = 2;  // Set to an unused request code
private final BroadcastReceiver smsVerificationReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            Status smsRetrieverStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

            switch (smsRetrieverStatus.getStatusCode()) {
                case CommonStatusCodes.SUCCESS:
                    // Get consent intent
                    Intent consentIntent = extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT);
                    try {
                        // Start activity to show consent dialog to user, activity must be started in
                        // 5 minutes, otherwise you'll receive another TIMEOUT intent
                        startActivityForResult(consentIntent, SMS_CONSENT_REQUEST);
                    } catch (ActivityNotFoundException e) {
                        // Handle the exception ...
                    }
                    break;
                case CommonStatusCodes.TIMEOUT:
                    // Time out occurred, handle the error.
                    break;
            }
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // ...

    IntentFilter intentFilter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
    registerReceiver(smsVerificationReceiver, SmsRetriever.SEND_PERMISSION, intentFilter);
}

Запуская действие для EXTRA_CONSENT_INTENT , вы запрашиваете у пользователя одноразовое разрешение на чтение содержимого сообщения.

3. Получите код подтверждения из сообщения.

В методе onActivityResult() обработайте ответ пользователя на ваш запрос на разрешение. Если вы получаете код результата RESULT_OK , пользователь предоставил разрешение на чтение содержимого сообщения, и вы можете получить текст сообщения из намерения.

Котлин

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        // ...
        SMS_CONSENT_REQUEST ->
            // Obtain the phone number from the result
            if (resultCode == Activity.RESULT_OK && data != null) {
                // Get SMS message content
                val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
                // Extract one-time code from the message and complete verification
                // `message` contains the entire text of the SMS message, so you will need
                // to parse the string.
                val oneTimeCode = parseOneTimeCode(message) // define this function

                // send one time code to the server
            } else {
                // Consent denied. User can type OTC manually.
            }
    }
}

Джава

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        // ...
        case SMS_CONSENT_REQUEST:
            if (resultCode == RESULT_OK) {
                // Get SMS message content
                String message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE);
                // Extract one-time code from the message and complete verification
                // `sms` contains the entire text of the SMS message, so you will need
                // to parse the string.
                String oneTimeCode = parseOneTimeCode(message); // define this function

                // send one time code to the server
            } else {
                // Consent canceled, handle the error ...
            }
            break;
    }
}

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