طلب إثبات الملكية عبر الرسائل القصيرة SMS في تطبيق Android

لإثبات ملكية أرقام الهواتف تلقائيًا، يجب تنفيذ كلٍّ من أجزاء العميل والخادم في عملية إثبات الملكية. يصف هذا المستند كيفية تنفيذ جزء العميل في تطبيق Android.

لبدء عملية إثبات ملكية رقم الهاتف في أحد تطبيقات Android، يمكنك إرسال رقم الهاتف إلى خادم التحقق والاتصال بواجهة برمجة تطبيقات SMS ريتريفر لبدء الاستماع إلى رسالة قصيرة تحتوي على رمز يُستخدم لمرة واحدة لتطبيقك. وبعد تلقّي الرسالة، تُرسِل الرمز لمرة واحدة مجددًا إلى خادمك لإكمال عملية إثبات الملكية.

قبل البدء

لإعداد تطبيقك، أكمِل الخطوات الواردة في الأقسام التالية.

المتطلبات الأساسية للتطبيق

تأكَّد من أنّ ملف إصدار تطبيقك يستخدم القيم التالية:

  • إصدار minSdkVersion من الإصدار 19 أو إصدار أحدث
  • إصدارupSdkVersion من الإصدار 28 أو إصدار أعلى

إعداد تطبيقك

في ملف Build.gradle على مستوى المشروع، يمكنك تضمين مستودع Maven من Google ومستودع Maven المركزي في كل من قسمَي buildscript وallprojects:

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

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

أضِف التبعية لخدمات Google Play لواجهة برمجة تطبيقات SMS ريتريفر إلى ملف إصدار Gradle الخاص بالوحدة، والذي يكون عادةً app/build.gradle:

dependencies {
  implementation 'com.google.android.gms:play-services-auth:20.7.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. إرسال رقم الهاتف إلى خادمك

بعد حصولك على رقم هاتف المستخدم وبدء استماع الرسائل القصيرة، أرسِل رقم هاتف المستخدم إلى خادم التحقق باستخدام أي طريقة (عادةً ما تكون من خلال طلب HTTPS POST).

ينشئ الخادم رسالة تحقق ويرسلها عبر رسالة قصيرة SMS إلى رقم الهاتف الذي حددته. يُرجى الاطّلاع على إجراء إثبات الملكية عبر الرسائل القصيرة SMS على الخادم.

4. تلقّي رسائل تحقّق

عند تلقّي رسالة تحقُّق على جهاز المستخدم، تبث "خدمات Play" رسالة صريح على تطبيقك تتضمّن "SmsRetriever.SMS_RETRIEVED_ACTION Intent" يحتوي على نص الرسالة. استخدم 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)