ขอรับการยืนยันทาง SMS ในแอป Android

หากต้องการยืนยันหมายเลขโทรศัพท์โดยอัตโนมัติ คุณต้องใช้ทั้งส่วนไคลเอ็นต์และเซิร์ฟเวอร์ของขั้นตอนการยืนยัน เอกสารนี้จะอธิบายวิธีใช้ส่วนไคลเอ็นต์ในแอป Android

หากต้องการเริ่มต้นขั้นตอนการยืนยันหมายเลขโทรศัพท์ในแอป Android ให้ส่งหมายเลขโทรศัพท์ไปยังเซิร์ฟเวอร์การยืนยันและเรียก SMS Fetchr API เพื่อเริ่มฟังข้อความ SMS ที่มีรหัสแบบใช้ครั้งเดียวสําหรับแอปของคุณ หลังจากได้รับข้อความแล้ว ให้ส่งรหัสยืนยันแบบครั้งเดียวไปยังเซิร์ฟเวอร์เพื่อดําเนินการยืนยันให้เสร็จสมบูรณ์

ข้อควรทราบก่อนที่จะเริ่มต้น

ทําตามขั้นตอนต่อไปนี้เพื่อเตรียมแอป

ข้อกําหนดเบื้องต้นของแอป

ตรวจสอบว่าไฟล์บิลด์ของแอปใช้ค่าต่อไปนี้

  • minSdkVersion 19 ขึ้นไป
  • ComposeSdkVersion 28 ขึ้นไป

กําหนดค่าแอป

ในไฟล์ build.gradle ระดับโปรเจ็กต์ ให้ใส่ที่เก็บ Maven ของ Google และที่เก็บส่วนกลางของ Maven ทั้งในส่วน buildscript และ allprojects

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

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

เพิ่มทรัพยากร Dependency บริการ Google Play สําหรับ SMS Fetchr API ไปยัง ไฟล์ Gradle ของโมดูล ซึ่งโดยทั่วไปคือ app/build.gradle

dependencies {
  implementation 'com.google.android.gms:play-services-auth:20.4.1'
  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 และแนบ Listener ที่ไม่สําเร็จและเรียกดูงานการเรียก 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 ที่มีสตริงไม่ซ้ําซึ่งระบุแอปของคุณได้นานสูงสุด 5 นาที

3. ส่งหมายเลขโทรศัพท์ไปยังเซิร์ฟเวอร์ของคุณ

เมื่อได้รับหมายเลขโทรศัพท์ของผู้ใช้และเริ่มฟังข้อความ SMS แล้ว ให้ส่งหมายเลขโทรศัพท์ของผู้ใช้ไปยังเซิร์ฟเวอร์การยืนยันโดยใช้วิธีการใดก็ได้ (โดยปกติจะเป็นคําขอ HTTPS POST)

เซิร์ฟเวอร์จะสร้างข้อความยืนยันและส่ง SMS ไปยังหมายเลขโทรศัพท์ที่คุณระบุ โปรดดูการยืนยันทาง SMS บนเซิร์ฟเวอร์

4. รับข้อความยืนยัน

เมื่อได้รับข้อความยืนยันในอุปกรณ์ของผู้ใช้ บริการ Play จะออกอากาศ Intent 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 นี้กับตัวกรอง 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)