בקשת אימות באמצעות SMS באפליקציה ל-Android

כדי לאמת מספרי טלפון באופן אוטומטי, צריך להטמיע את החלקים של הלקוח ושל השרת בתהליך האימות. במאמר הזה מוסבר איך להטמיע את החלק של הלקוח באפליקציית Android.

כדי להתחיל את תהליך האימות של מספר הטלפון באפליקציית Android, שולחים את מספר הטלפון לשרת האימות ומפעילים את SMS Retriever API כדי להתחיל להאזין להודעת SMS שמכילה קוד חד-פעמי לאפליקציה. אחרי שמקבלים את ההודעה, שולחים את הקוד החד-פעמי בחזרה לשרת כדי להשלים את תהליך האימות.

כדי להכין את האפליקציה, פועלים לפי השלבים בקטעים הבאים.

דרישות מוקדמות לאפליקציה

מוודאים שקובץ ה-build של האפליקציה משתמש בערכים הבאים:

  • ‫minSdkVersion ‏19 ואילך
  • ‫compileSdkVersion בגרסה 28 ומעלה

הגדרת האפליקציה

בקובץ build.gradle ברמת הפרויקט, צריך לכלול את מאגר Maven של Google ואת מאגר Maven המרכזי גם בקטע buildscript וגם בקטע allprojects:

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

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

מוסיפים את התלות ב-Google Play Services עבור SMS Retriever API לקובץ ה-build של Gradle של המודול, שבדרך כלל נמצא בנתיב app/build.gradle:

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

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 Services משדר באופן מפורש לאפליקציה SmsRetriever.SMS_RETRIEVED_ACTION Intent, שמכיל את הטקסט של ההודעה. משתמשים בBroadcastReceiver כדי לקבל את הודעת האימות הזו.

ב-handler של BroadcastReceiver, מקבלים את הטקסט של הודעת האימות (ואפשר גם את כתובת השולח) מהתוספים של ה-Intent:onReceive

/**
  * 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:
          // (Optional) Get SMS Sender address - only available in
          // GMS version 24.20 onwards, else it will return null
          String senderAddress = extras.getString(SmsRetriever.EXTRA_SMS_ORIGINATING_ADDRESS);
          // Get SMS message contents
          String message = extras.getString(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. שליחת הקוד החד-פעמי מהודעת האימות לשרת

אחרי שמקבלים את הטקסט של הודעת האימות, משתמשים בביטוי רגולרי או בלוגיקה אחרת כדי לחלץ את הקוד החד-פעמי מההודעה. הפורמט של הקוד החד-פעמי תלוי באופן ההטמעה שלו בשרת.

לבסוף, שולחים את הקוד החד-פעמי לשרת דרך חיבור מאובטח. כשהשרת שלכם מקבל את הקוד החד-פעמי, הוא מתעד שמספר הטלפון אומת.