ให้ผู้ใช้ลงชื่อเข้าใช้ด้วยข้อมูลเข้าสู่ระบบที่บันทึกไว้

ใช้ไคลเอ็นต์การลงชื่อเข้าใช้ด้วย One Tap เพื่อขอสิทธิ์จากผู้ใช้เพื่อเรียกข้อมูลเข้าสู่ระบบที่เคยใช้ลงชื่อเข้าใช้แอปของคุณมาก่อน ข้อมูลเข้าสู่ระบบเหล่านี้อาจเป็นบัญชี Google หรือชุดชื่อผู้ใช้-รหัสผ่านที่บันทึกไว้กับ Google โดยใช้ Chrome, การป้อนข้อความอัตโนมัติของ Android หรือ Smart Lock สำหรับรหัสผ่าน

UI การลงชื่อเข้าใช้ด้วยการแตะเพียงครั้งเดียว

เมื่อดึงข้อมูลเข้าสู่ระบบเรียบร้อยแล้ว คุณจะใช้ข้อมูลนั้นเพื่อลงชื่อเข้าใช้ให้ผู้ใช้ในแอปได้อย่างราบรื่น

หากผู้ใช้ไม่ได้บันทึกข้อมูลเข้าสู่ระบบใดๆ ไว้ ระบบจะไม่แสดง UI และคุณจะให้ประสบการณ์การออกจากระบบตามปกติได้

ฉันควรใช้การลงชื่อเข้าใช้ด้วย One Tap ที่ใดบ้าง

หากแอปของคุณกำหนดให้ผู้ใช้ลงชื่อเข้าใช้ ให้แสดง UI ของ One Tap บนหน้าจอลงชื่อเข้าใช้ การดำเนินการนี้จะเป็นประโยชน์แม้ว่าคุณจะมีปุ่ม "ลงชื่อเข้าใช้ด้วย Google" อยู่แล้ว: เนื่องจากคุณสามารถกำหนดค่า UI ของ One Tap ให้แสดงเฉพาะข้อมูลเข้าสู่ระบบที่ผู้ใช้เคยลงชื่อเข้าใช้เท่านั้น จึงเป็นการช่วยเตือนผู้ใช้ที่ลงชื่อเข้าใช้ด้วยวิธีที่ไม่ค่อยได้ลงชื่อเข้าใช้ครั้งล่าสุด และป้องกันไม่ให้ผู้ใช้สร้างบัญชีใหม่ด้วยแอปของคุณโดยไม่ได้ตั้งใจ

หากแอปเป็นแบบไม่บังคับ ลองใช้การลงชื่อเข้าใช้ด้วย One Tap ในหน้าจอใดก็ได้ที่มีประสบการณ์ที่ดีขึ้นโดยการลงชื่อเข้าใช้ ตัวอย่างเช่น หากผู้ใช้เรียกดูเนื้อหาที่มีแอปของคุณในขณะที่ออกจากระบบ แต่สามารถโพสต์ความคิดเห็นหรือเพิ่มรายการลงในรถเข็นช็อปปิ้งหลังจากลงชื่อเข้าใช้ได้เท่านั้น นั่นน่าจะเป็นบริบทที่สมเหตุสมผลสำหรับการลงชื่อเข้าใช้ด้วย One Tap

แอปที่ไม่บังคับการลงชื่อเข้าใช้ควรใช้การลงชื่อเข้าใช้ด้วย One Tap ในหน้าจอลงชื่อเข้าใช้ด้วย สำหรับเหตุผลที่ระบุไว้ข้างต้น

ก่อนเริ่มต้น

1. กำหนดค่าไคลเอ็นต์การลงชื่อเข้าใช้ด้วย One Tap

คุณจะกำหนดค่าไคลเอ็นต์การลงชื่อเข้าใช้ด้วย One Tap เพื่อลงชื่อเข้าใช้ให้ผู้ใช้ด้วยรหัสผ่านที่บันทึกไว้ บัญชี Google ที่บันทึกไว้ หรืออย่างใดอย่างหนึ่งก็ได้ (เราขอแนะนำให้รองรับทั้ง 2 ฟีเจอร์ คือเปิดใช้การสร้างบัญชีด้วยการแตะเพียงครั้งเดียวสำหรับผู้ใช้ใหม่ และการลงชื่อเข้าใช้โดยอัตโนมัติหรือการแตะครั้งเดียวสำหรับผู้ใช้ที่กลับมาให้มากที่สุดเท่าที่จะเป็นไปได้)

หากแอปใช้การลงชื่อเข้าใช้ด้วยรหัสผ่าน ให้ใช้ setPasswordRequestOptions() เพื่อเปิดใช้คำขอข้อมูลเข้าสู่ระบบสำหรับรหัสผ่าน

หากแอปใช้ Google Sign-In ให้ใช้ setGoogleIdTokenRequestOptions() เพื่อเปิดใช้และกำหนดค่าคำขอโทเค็น Google ID ดังนี้

  • ตั้งค่ารหัสไคลเอ็นต์ของเซิร์ฟเวอร์เป็นรหัสที่คุณสร้างในคอนโซล Google APIs โปรดทราบว่านี่คือรหัสไคลเอ็นต์ของเซิร์ฟเวอร์ ไม่ใช่รหัสไคลเอ็นต์ Android

  • กำหนดค่าไคลเอ็นต์ให้กรองตามบัญชีที่ได้รับอนุญาต เมื่อคุณเปิดใช้ตัวเลือกนี้ ไคลเอ็นต์ One Tap จะแจ้งให้ผู้ใช้ลงชื่อเข้าใช้แอปด้วยบัญชี Google ที่ผู้ใช้เคยใช้ในอดีตเท่านั้น วิธีนี้จะช่วยให้ผู้ใช้ลงชื่อเข้าใช้ได้สำเร็จเมื่อไม่แน่ใจว่ามีบัญชีแล้วหรือไม่ หรือใช้บัญชี Google ใดที่ใช้ และป้องกันไม่ให้ผู้ใช้สร้างบัญชีใหม่กับแอปโดยไม่ตั้งใจ

  • หากคุณต้องการให้ผู้ใช้ลงชื่อเข้าใช้โดยอัตโนมัติเมื่อทำได้ ให้เปิดใช้ฟีเจอร์นี้ด้วย setAutoSelectEnabled() การลงชื่อเข้าใช้อัตโนมัติจะดำเนินการได้เมื่อเป็นไปตามเกณฑ์ต่อไปนี้

    • ผู้ใช้มีข้อมูลรับรองที่บันทึกไว้สำหรับแอปของคุณเพียง 1 รายการ นั่นคือรหัสผ่านที่บันทึกไว้ 1 รายการหรือบัญชี Google ที่บันทึกไว้ 1 บัญชี
    • ผู้ใช้ไม่ได้ปิดใช้การลงชื่อเข้าใช้อัตโนมัติในการตั้งค่าบัญชี Google
  • แม้ว่าจะไม่บังคับ แต่เราขอแนะนำเป็นอย่างยิ่งให้คุณพิจารณาใช้ Nonce เพื่อปรับปรุงความปลอดภัยในการลงชื่อเข้าใช้และหลีกเลี่ยงการโจมตีซ้ำ ใช้ setNonce เพื่อใส่ Nonce ในแต่ละคำขอ ดูส่วน ของ SafetyNet สำหรับคำแนะนำและรายละเอียดเพิ่มเติมเกี่ยวกับการสร้าง Nonce

Java

public class YourActivity extends AppCompatActivity {
  // ...

  private SignInClient oneTapClient;
  private BeginSignInRequest signInRequest;

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState,
                       @Nullable PersistableBundle persistentState) {
      super.onCreate(savedInstanceState, persistentState);

      oneTapClient = Identity.getSignInClient(this);
      signInRequest = BeginSignInRequest.builder()
              .setPasswordRequestOptions(PasswordRequestOptions.builder()
                      .setSupported(true)
                      .build())
              .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                      .setSupported(true)
                      // Your server's client ID, not your Android client ID.
                      .setServerClientId(getString(R.string.default_web_client_id))
                      // Only show accounts previously used to sign in.
                      .setFilterByAuthorizedAccounts(true)
                      .build())
              // Automatically sign in when exactly one credential is retrieved.
              .setAutoSelectEnabled(true)
              .build();
      // ...
  }
  // ...
}

Kotlin

class YourActivity : AppCompatActivity() {
    // ...

    private lateinit var oneTapClient: SignInClient
    private lateinit var signInRequest: BeginSignInRequest

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        oneTapClient = Identity.getSignInClient(this)
        signInRequest = BeginSignInRequest.builder()
            .setPasswordRequestOptions(BeginSignInRequest.PasswordRequestOptions.builder()
                .setSupported(true)
                .build())
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    // Your server's client ID, not your Android client ID.
                    .setServerClientId(getString(R.string.your_web_client_id))
                    // Only show accounts previously used to sign in.
                    .setFilterByAuthorizedAccounts(true)
                    .build())
            // Automatically sign in when exactly one credential is retrieved.
            .setAutoSelectEnabled(true)
            .build()
        // ...
    }
    // ...
}

2. ตรวจสอบผู้ใช้ที่ลงชื่อเข้าใช้

หากผู้ใช้ที่ลงชื่อเข้าใช้หรือผู้ใช้ที่ออกจากระบบสามารถใช้กิจกรรมของคุณ ให้ตรวจสอบสถานะของผู้ใช้ก่อนแสดง UI การลงชื่อเข้าใช้ด้วย One Tap

นอกจากนี้คุณควรติดตามด้วยว่าผู้ใช้ปฏิเสธที่จะใช้การลงชื่อเข้าใช้ด้วย One Tap แล้วหรือยังด้วยการปิดข้อความแจ้งหรือแตะนอกข้อความนั้น ซึ่งอาจเป็นอะไรง่ายๆ อย่างพร็อพเพอร์ตี้บูลีนสำหรับกิจกรรมของคุณก็ได้ (ดูหยุดแสดง UI ของ One Tap ด้านล่าง)

3. แสดง UI การลงชื่อเข้าใช้ด้วย One Tap

หากผู้ใช้ไม่ได้ลงชื่อเข้าใช้และยังไม่ได้ปฏิเสธที่จะใช้การลงชื่อเข้าใช้ด้วย One Tap ให้เรียกใช้เมธอด beginSignIn() ของออบเจ็กต์ไคลเอ็นต์ จากนั้นแนบ Listener กับ Task ที่ส่งกลับ โดยทั่วไปแอปจะทำเช่นนี้ในเมธอด onCreate() ของกิจกรรมหรือหลังจากการเปลี่ยนหน้าจอเมื่อใช้สถาปัตยกรรมกิจกรรมเดียว

ไคลเอ็นต์ One Tap จะเรียกใช้ Listener ที่สำเร็จหากผู้ใช้มีข้อมูลรับรองที่บันทึกไว้สำหรับแอป ใน Listener ที่สำเร็จ ให้เรียก Intent ที่รอดำเนินการจากผลลัพธ์ Task แล้วส่งต่อไปยัง startIntentSenderForResult() เพื่อเริ่ม UI การลงชื่อเข้าใช้ด้วย One Tap

หากผู้ใช้ไม่มีข้อมูลเข้าสู่ระบบที่บันทึกไว้ ไคลเอ็นต์ One Tap จะเรียกใช้ Listener ที่ล้มเหลว ในกรณีนี้ คุณไม่จำเป็นต้องดำเนินการใดๆ เพียงนำเสนอประสบการณ์การออกจากระบบของแอปต่อเท่านั้น อย่างไรก็ตาม หากคุณรองรับการลงชื่อสมัครใช้ด้วย One Tap คุณจะเริ่มขั้นตอนนี้ได้ที่นี่เพื่อประสบการณ์การสร้างบัญชีที่ราบรื่น ดูหัวข้อสร้างบัญชีใหม่ด้วยการแตะครั้งเดียว

Java

oneTapClient.beginSignIn(signUpRequest)
        .addOnSuccessListener(this, new OnSuccessListener<BeginSignInResult>() {
            @Override
            public void onSuccess(BeginSignInResult result) {
                try {
                    startIntentSenderForResult(
                            result.getPendingIntent().getIntentSender(), REQ_ONE_TAP,
                            null, 0, 0, 0);
                } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start One Tap UI: " + e.getLocalizedMessage());
                }
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // No saved credentials found. Launch the One Tap sign-up flow, or
                // do nothing and continue presenting the signed-out UI.
                Log.d(TAG, e.getLocalizedMessage());
            }
        });

Kotlin

oneTapClient.beginSignIn(signInRequest)
    .addOnSuccessListener(this) { result ->
        try {
            startIntentSenderForResult(
                result.pendingIntent.intentSender, REQ_ONE_TAP,
                null, 0, 0, 0, null)
        } catch (e: IntentSender.SendIntentException) {
            Log.e(TAG, "Couldn't start One Tap UI: ${e.localizedMessage}")
        }
    }
    .addOnFailureListener(this) { e ->
        // No saved credentials found. Launch the One Tap sign-up flow, or
        // do nothing and continue presenting the signed-out UI.
        Log.d(TAG, e.localizedMessage)
    }

4. จัดการคำตอบของผู้ใช้

ระบบจะรายงานการตอบสนองของผู้ใช้ต่อข้อความแจ้งให้ลงชื่อเข้าใช้ด้วย One Tap ไปยังแอปของคุณโดยใช้เมธอด onActivityResult() ของกิจกรรม หากผู้ใช้เลือกที่จะลงชื่อเข้าใช้ ผลที่ได้จะเป็นข้อมูลรับรองที่บันทึกไว้ หากผู้ใช้ปฏิเสธการลงชื่อเข้าใช้ด้วยการปิด UI ของ One Tap หรือแตะภายนอก UI ผลลัพธ์จะแสดงขึ้นพร้อมรหัส RESULT_CANCELED แอปของคุณต้องจัดการความเป็นไปได้ทั้ง 2 อย่าง

ลงชื่อเข้าใช้ด้วยข้อมูลเข้าสู่ระบบที่ดึงข้อมูลมา

หากผู้ใช้เลือกแชร์ข้อมูลเข้าสู่ระบบกับแอป คุณจะดึงข้อมูลเหล่านั้นได้โดยส่งข้อมูล Intent จาก onActivityResult() ไปยังเมธอด getSignInCredentialFromIntent() ของไคลเอ็นต์ One Tap ข้อมูลเข้าสู่ระบบจะมีพร็อพเพอร์ตี้ googleIdToken ที่ไม่เป็นค่าว่าง หากผู้ใช้แชร์ข้อมูลรับรองบัญชี Google กับแอปของคุณ หรือพร็อพเพอร์ตี้ password ที่ไม่เป็นค่าว่างหากผู้ใช้แชร์รหัสผ่านที่บันทึกไว้

ใช้ข้อมูลเข้าสู่ระบบเพื่อตรวจสอบสิทธิ์กับแบ็กเอนด์ของแอป

  • หากมีการเรียกคู่ชื่อผู้ใช้และรหัสผ่าน ให้ใช้ชื่อผู้ใช้และรหัสผ่านเหล่านั้นในการลงชื่อเข้าใช้ด้วยวิธีเดียวกับที่คุณทำหากผู้ใช้ระบุด้วยตนเอง
  • หากมีการดึงข้อมูลข้อมูลรับรองบัญชี Google ให้ใช้โทเค็นรหัสเพื่อตรวจสอบสิทธิ์กับแบ็กเอนด์ หากคุณเลือกใช้ Nonce เพื่อช่วยหลีกเลี่ยงการโจมตีซ้ำ ให้ตรวจสอบค่าการตอบกลับในเซิร์ฟเวอร์แบ็กเอนด์ โปรดดูตรวจสอบสิทธิ์ด้วยแบ็กเอนด์โดยใช้โทเค็นรหัส

Java

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(data);
                  String idToken = credential.getGoogleIdToken();
                  String username = credential.getId();
                  String password = credential.getPassword();
                  if (idToken !=  null) {
                      // Got an ID token from Google. Use it to authenticate
                      // with your backend.
                      Log.d(TAG, "Got ID token.");
                  } else if (password != null) {
                      // Got a saved username and password. Use them to authenticate
                      // with your backend.
                      Log.d(TAG, "Got password.");
                  }
              } catch (ApiException e) {
                  // ...
              }
              break;
      }
  }
}

Kotlin

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
             REQ_ONE_TAP -> {
                try {
                    val credential = oneTapClient.getSignInCredentialFromIntent(data)
                    val idToken = credential.googleIdToken
                    val username = credential.id
                    val password = credential.password
                    when {
                        idToken != null -> {
                            // Got an ID token from Google. Use it to authenticate
                            // with your backend.
                            Log.d(TAG, "Got ID token.")
                        }
                        password != null -> {
                            // Got a saved username and password. Use them to authenticate
                            // with your backend.
                            Log.d(TAG, "Got password.")
                        }
                        else -> {
                            // Shouldn't happen.
                            Log.d(TAG, "No ID token or password!")
                        }
                    }
                } catch (e: ApiException) {
                    // ...
                }
            }
        }
    }
    // ...
}

หยุดแสดง UI ของ One Tap

หากผู้ใช้ปฏิเสธการลงชื่อเข้าใช้ การติดต่อ getSignInCredentialFromIntent() จะแสดง ApiException ที่มีรหัสสถานะ CommonStatusCodes.CANCELED เมื่อเกิดกรณีนี้ขึ้น คุณควรปิดใช้ UI การลงชื่อเข้าใช้ด้วย One Tap ชั่วคราวเพื่อไม่ให้ผู้ใช้รู้สึกรำคาญด้วยข้อความแจ้งซ้ำๆ ตัวอย่างต่อไปนี้ช่วยในการดำเนินการดังกล่าวได้โดยตั้งค่าพร็อพเพอร์ตี้ในกิจกรรมที่ใช้พิจารณาว่าจะเสนอการลงชื่อเข้าใช้ด้วย One Tap ให้แก่ผู้ใช้หรือไม่ อย่างไรก็ตาม คุณจะบันทึกค่าลงใน SharedPreferences หรือใช้วิธีอื่นก็ได้

คุณจะต้องใช้การจำกัดราคาของคุณเองในข้อความแจ้งให้ลงชื่อเข้าใช้ด้วย One Tap มิเช่นนั้น ผู้ใช้ยกเลิกข้อความแจ้งหลายรายการติดต่อกัน ไคลเอ็นต์ One Tap จะไม่แสดงข้อความแจ้งให้ผู้ใช้ใน 24 ชั่วโมงข้างหน้า

Java

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  // ...
              } catch (ApiException e) {
                  switch (e.getStatusCode()) {
                      case CommonStatusCodes.CANCELED:
                          Log.d(TAG, "One-tap dialog was closed.");
                          // Don't re-prompt the user.
                          showOneTapUI = false;
                          break;
                      case CommonStatusCodes.NETWORK_ERROR:
                          Log.d(TAG, "One-tap encountered a network error.");
                          // Try again or just ignore.
                          break;
                      default:
                          Log.d(TAG, "Couldn't get credential from result."
                                  + e.getLocalizedMessage());
                          break;
                  }
              }
              break;
      }
  }
}

Kotlin

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
            REQ_ONE_TAP -> {
                try {
                    // ...
                } catch (e: ApiException) {
                    when (e.statusCode) {
                        CommonStatusCodes.CANCELED -> {
                            Log.d(TAG, "One-tap dialog was closed.")
                            // Don't re-prompt the user.
                            showOneTapUI = false
                        }
                        CommonStatusCodes.NETWORK_ERROR -> {
                            Log.d(TAG, "One-tap encountered a network error.")
                            // Try again or just ignore.
                        }
                        else -> {
                            Log.d(TAG, "Couldn't get credential from result." +
                                " (${e.localizedMessage})")
                        }
                    }
                }
            }
        }
    }
    // ...
}

5. จัดการการออกจากระบบ

เมื่อผู้ใช้ออกจากระบบแอป ให้เรียกเมธอด signOut() ของไคลเอ็นต์ One Tap การโทร signOut() จะปิดการลงชื่อเข้าใช้โดยอัตโนมัติจนกว่าผู้ใช้จะลงชื่อเข้าใช้อีกครั้ง

แม้ว่าคุณจะไม่ได้ใช้การลงชื่อเข้าใช้โดยอัตโนมัติ ขั้นตอนนี้ก็มีความสำคัญเพราะช่วยให้มั่นใจได้ว่าเมื่อผู้ใช้ออกจากระบบแอป ระบบจะรีเซ็ตสถานะการตรวจสอบสิทธิ์ของ API ของบริการ Google Play ที่คุณใช้ด้วย

ขั้นตอนถัดไป

หากคุณกำหนดค่าไคลเอ็นต์ One Tap ให้ดึงข้อมูลเข้าสู่ระบบ Google แล้ว แอปของคุณจะรับโทเค็น Google ID ที่แสดงถึงบัญชี Google ของผู้ใช้ได้แล้ว ดูวิธีใช้โทเค็นเหล่านี้ในแบ็กเอนด์

หากคุณรองรับ Google Sign-In คุณยังใช้ไคลเอ็นต์ One Tap เพื่อเพิ่มขั้นตอนการสร้างบัญชีที่ราบรื่นลงในแอปได้ด้วย