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

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

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

לפני שמתחילים

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

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

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

  • minSdkVersion 19 ואילך
  • גרסה 28 ואילך של IntegrationSdkVersion

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

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

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

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

מוסיפים את התלות של Google Play Services של SMS Retriever API לקובץ 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. שולחים את מספר הטלפון לשרת

אחרי שמשיגים את מספר הטלפון של המשתמש ומתחילים להאזין להודעות SMS, צריך לשלוח את מספר הטלפון שלו לשרת האימות בכל שיטה (בדרך כלל באמצעות בקשת HTTPS POST).

השרת יוצר הודעת אימות ושולח אותה ב-SMS למספר הטלפון שציינתם. למידע נוסף, ראו בצע אימות באמצעות SMS בשרת.

4. קבלת הודעות אימות

כשמתקבלת הודעת אימות במכשיר של המשתמש, אפליקציית Play Services משדרת לאפליקציה באופן מפורש Intent SmsRetriever.SMS_RETRIEVED_ACTION, שמכיל את הטקסט של ההודעה. השתמשו בכתובת BroadcastReceiver כדי לקבל את הודעת האימות הזו.

ב-handler של 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 הזה עם מסנן Intent 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)