ใช้ไคลเอ็นต์การลงชื่อเข้าใช้ด้วย One Tap เพื่อขอสิทธิ์จากผู้ใช้ในการดึงข้อมูลเข้าสู่ระบบรายการใดรายการหนึ่งที่ผู้ใช้เคยใช้ลงชื่อเข้าใช้แอป ข้อมูลเข้าสู่ระบบเหล่านี้อาจเป็นบัญชี Google หรือการผสมผสานระหว่างชื่อผู้ใช้กับรหัสผ่านที่ผู้ใช้บันทึกไว้กับ Google โดยใช้ Chrome, การป้อนข้อความอัตโนมัติของ Android หรือ Smart Lock สำหรับรหัสผ่าน
เมื่อดึงข้อมูลเข้าสู่ระบบได้สำเร็จ คุณจะใช้ข้อมูลดังกล่าวเพื่อลงชื่อเข้าใช้แอปให้ผู้ใช้ได้อย่างราบรื่น
หากผู้ใช้ไม่ได้บันทึกข้อมูลเข้าสู่ระบบไว้ ระบบจะไม่แสดง UI และคุณสามารถมอบประสบการณ์การใช้งานตามปกติเมื่อออกจากระบบ
ฉันควรใช้การลงชื่อเข้าใช้ด้วย One Tap ที่ใด
หากแอปกำหนดให้ผู้ใช้ต้องลงชื่อเข้าใช้ ให้แสดง UI ของ One Tap ในหน้าจอลงชื่อเข้าใช้ ซึ่งอาจเป็นประโยชน์แม้ว่าคุณจะมีปุ่ม "ลงชื่อเข้าใช้ด้วย Google" อยู่แล้วก็ตาม เนื่องจากคุณกำหนดค่า UI ของ One Tap ให้แสดงเฉพาะข้อมูลเข้าสู่ระบบที่ผู้ใช้เคยใช้ลงชื่อเข้าใช้ได้ จึงเป็นการช่วยเตือนผู้ใช้ที่ลงชื่อเข้าใช้ไม่บ่อยนักว่าครั้งล่าสุดลงชื่อเข้าใช้ด้วยวิธีใด และป้องกันไม่ให้ผู้ใช้สร้างบัญชีใหม่กับแอปของคุณโดยไม่ได้ตั้งใจ
หากแอปของคุณให้ผู้ใช้เลือกได้ว่าจะลงชื่อเข้าใช้หรือไม่ ให้ลองใช้การลงชื่อเข้าใช้ด้วย One Tap ในหน้าจอใดก็ตามที่การลงชื่อเข้าใช้ช่วยยกระดับประสบการณ์การใช้งาน เช่น หากผู้ใช้เรียกดูเนื้อหาด้วยแอปของคุณได้ขณะออกจากระบบ แต่จะโพสต์ความคิดเห็นหรือเพิ่มสินค้าลงในรถเข็นช็อปปิ้งได้หลังจากลงชื่อเข้าใช้เท่านั้น กรณีนี้จะเป็นบริบทที่เหมาะสมสำหรับการลงชื่อเข้าใช้ด้วย One Tap
แอปที่ให้ผู้ใช้เลือกได้ว่าจะลงชื่อเข้าใช้หรือไม่ควรใช้การลงชื่อเข้าใช้ด้วย One Tap ในหน้าจอลงชื่อเข้าใช้ด้วยเหตุผลที่กล่าวไว้ข้างต้น
ก่อนเริ่มต้น
- ตั้งค่าโปรเจ็กต์คอนโซล Google API และโปรเจ็กต์ Android ตามที่อธิบายไว้ใน หัวข้อเริ่มต้นใช้งานการลงชื่อเข้าใช้ด้วย One Tap
- หากรองรับการลงชื่อเข้าใช้ด้วยรหัสผ่าน ให้เพิ่มประสิทธิภาพแอปสำหรับการ ป้อนข้อความอัตโนมัติ (หรือใช้ Smart Lock สำหรับรหัสผ่าน) เพื่อให้ผู้ใช้บันทึกข้อมูลเข้าสู่ระบบด้วย รหัสผ่านได้หลังจากลงชื่อเข้าใช้
1. กำหนดค่าไคลเอ็นต์การลงชื่อเข้าใช้ด้วย One Tap
คุณกำหนดค่าไคลเอ็นต์การลงชื่อเข้าใช้ด้วย One Tap เพื่อลงชื่อเข้าใช้ให้ผู้ใช้ด้วยรหัสผ่านที่บันทึกไว้ บัญชี Google ที่บันทึกไว้ หรือทั้ง 2 อย่างได้ (เราขอแนะนำให้รองรับทั้ง 2 อย่างเพื่อเปิดใช้การสร้างบัญชีด้วยการแตะเพียงครั้งเดียวสำหรับผู้ใช้ใหม่ และการลงชื่อเข้าใช้ด้วยการแตะเพียงครั้งเดียวหรืออัตโนมัติสำหรับผู้ใช้ที่กลับมาให้ได้มากที่สุด)
หากแอปใช้การลงชื่อเข้าใช้ด้วยรหัสผ่าน ให้ใช้ setPasswordRequestOptions() เพื่อเปิดใช้คำขอข้อมูลเข้าสู่ระบบด้วยรหัสผ่าน
หากแอปใช้การลงชื่อเข้าใช้ด้วย Google ให้ใช้ setGoogleIdTokenRequestOptions() เพื่อเปิดใช้และกำหนดค่าคำขอโทเค็นรหัส Google ดังนี้
ตั้งค่ารหัสไคลเอ็นต์ของเซิร์ฟเวอร์เป็น รหัสที่คุณสร้างในคอนโซล Google API โปรดทราบว่านี่คือรหัสไคลเอ็นต์ของเซิร์ฟเวอร์ ไม่ใช่รหัสไคลเอ็นต์ของ Android
กำหนดค่าไคลเอ็นต์ให้กรองตามบัญชีที่ได้รับอนุญาต เมื่อเปิดใช้ตัวเลือกนี้ ไคลเอ็นต์ One Tap จะแจ้งให้ผู้ใช้ลงชื่อเข้าใช้แอปด้วยบัญชี Google ที่เคยใช้ในอดีตเท่านั้น การดำเนินการนี้จะช่วยให้ผู้ใช้ลงชื่อเข้าใช้ได้สำเร็จเมื่อไม่แน่ใจว่ามีบัญชีอยู่แล้วหรือใช้บัญชี Google ใด และป้องกันไม่ให้ผู้ใช้สร้างบัญชีใหม่กับแอปของคุณโดยไม่ได้ตั้งใจ
หากต้องการลงชื่อเข้าใช้ให้ผู้ใช้โดยอัตโนมัติเมื่อเป็นไปได้ ให้เปิดใช้ฟีเจอร์ด้วย
setAutoSelectEnabled()การลงชื่อเข้าใช้โดยอัตโนมัติจะทำได้เมื่อเป็นไปตามเกณฑ์ต่อไปนี้- ผู้ใช้มีข้อมูลเข้าสู่ระบบที่บันทึกไว้สำหรับแอปของคุณเพียงรายการเดียว นั่นคือรหัสผ่านที่บันทึกไว้ 1 รายการหรือบัญชี Google ที่บันทึกไว้ 1 บัญชี
- ผู้ใช้ไม่ได้ปิดใช้การลงชื่อเข้าใช้โดยอัตโนมัติในการตั้งค่า บัญชี Google
แม้ว่าจะเป็นตัวเลือก แต่เราขอแนะนำให้คุณพิจารณาใช้ Nonce เพื่อปรับปรุงความปลอดภัยในการลงชื่อเข้าใช้และหลีกเลี่ยงการโจมตีแบบ Replay ใช้ setNonce เพื่อรวม Nonce ในคำขอแต่ละรายการ ดูส่วน รับ 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 ที่สำเร็จ ให้รับ PendingIntent จากผลลัพธ์ 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 ที่ไม่ใช่ Null หากผู้ใช้แชร์ข้อมูลเข้าสู่ระบบของบัญชี Google กับแอป หรือมีพร็อพเพอร์ตี้ password ที่ไม่ใช่ Null หากผู้ใช้แชร์รหัสผ่านที่บันทึกไว้
ใช้ข้อมูลเข้าสู่ระบบเพื่อตรวจสอบสิทธิ์กับแบ็กเอนด์ของแอป
- หากดึงคู่ชื่อผู้ใช้และรหัสผ่านมาได้ ให้ใช้ข้อมูลดังกล่าวเพื่อลงชื่อเข้าใช้ในลักษณะเดียวกับที่คุณจะทำหากผู้ใช้ป้อนข้อมูลด้วยตนเอง
หากดึงข้อมูลเข้าสู่ระบบของบัญชี Google มาได้ ให้ใช้โทเค็นรหัสเพื่อตรวจสอบสิทธิ์กับแบ็กเอนด์ หากเลือกใช้ Nonce เพื่อช่วยหลีกเลี่ยงการโจมตีแบบ Replay ให้ตรวจสอบค่าการตอบกลับในเซิร์ฟเวอร์แบ็กเอนด์ ดูหัวข้อ ตรวจสอบสิทธิ์กับแบ็กเอนด์โดยใช้โทเค็นรหัส
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 ที่แสดงถึงบัญชี Google ของผู้ใช้ได้แล้ว ดูวิธีใช้โทเค็นเหล่านี้ในแบ็กเอนด์
หากรองรับการลงชื่อเข้าใช้ด้วย Google คุณยังใช้ไคลเอ็นต์ One Tap เพื่อเพิ่ม ขั้นตอนการสร้างบัญชีที่ราบรื่นลงในแอปได้ด้วย