ตรวจสอบสิทธิ์ผู้ใช้ด้วยฟีเจอร์ลงชื่อเข้าใช้ด้วย Google

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

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

หากต้องการตั้งค่าฟีเจอร์ลงชื่อเข้าใช้ด้วย Google ให้ทำตาม 2 ขั้นตอนหลักต่อไปนี้

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

Bottom Sheet ของเครื่องมือจัดการข้อมูลเข้าสู่ระบบ
รูปที่ 1 ชีตด้านล่างของเครื่องมือจัดการข้อมูลเข้าสู่ระบบ UI การเลือกข้อมูลเข้าสู่ระบบ

เพิ่มปุ่มลงชื่อเข้าใช้ด้วย Google ลงใน UI ของแอป ปุ่มลงชื่อเข้าใช้ด้วย Google เป็นวิธีที่สะดวกสำหรับผู้ใช้ในการใช้บัญชี Google ที่มีอยู่เพื่อลงชื่อสมัครใช้หรือลงชื่อเข้าใช้แอป Android ผู้ใช้จะคลิกปุ่มลงชื่อเข้าใช้ด้วย Google หากปิด UI ของชีตด้านล่าง หรือหากต้องการใช้บัญชี Google เพื่อลงชื่อสมัครใช้และลงชื่อเข้าใช้อย่างชัดเจน สําหรับนักพัฒนาแอป การเปลี่ยนแปลงนี้จะช่วยให้ผู้ใช้เริ่มต้นใช้งานได้ง่ายขึ้นและลดปัญหาที่เกิดขึ้นระหว่างการลงชื่อสมัครใช้

ภาพเคลื่อนไหวแสดงขั้นตอนลงชื่อเข้าใช้ด้วย Google
รูปที่ 2 UI ปุ่มลงชื่อเข้าใช้ด้วย Google ของ Credential Manager

เอกสารนี้อธิบายวิธีผสานรวมปุ่มลงชื่อเข้าใช้ด้วย Google และกล่องโต้ตอบในชีตด้านล่างกับ Credential Manager API โดยใช้ไลบรารีตัวช่วย GoogleID

ตั้งค่าโปรเจ็กต์คอนโซล Google API

  1. เปิดโปรเจ็กต์ในคอนโซล API หรือสร้างโปรเจ็กต์หากยังไม่มี
  2. ในหน้าหน้าจอขอความยินยอม OAuth ให้ตรวจสอบว่าข้อมูลทั้งหมดถูกต้องและครบถ้วน
    1. ตรวจสอบว่าแอปของคุณมีชื่อแอป โลโก้แอป และหน้าแรกของแอปที่ถูกต้อง ค่าเหล่านี้จะแสดงต่อผู้ใช้ในหน้าจอขอความยินยอมของฟีเจอร์ลงชื่อเข้าใช้ด้วย Google เมื่อลงชื่อสมัครใช้และในหน้าจอแอปและบริการของบุคคลที่สาม
    2. ตรวจสอบว่าคุณได้ระบุ URL ของนโยบายความเป็นส่วนตัวและข้อกำหนดในการให้บริการของแอปแล้ว
  3. ในหน้าข้อมูลเข้าสู่ระบบ ให้สร้างรหัสไคลเอ็นต์ Android สําหรับแอปหากยังไม่มี คุณจะต้องระบุชื่อแพ็กเกจและลายเซ็น SHA-1 ของแอป
    1. ไปที่หน้าข้อมูลเข้าสู่ระบบ
    2. คลิกสร้างข้อมูลเข้าสู่ระบบ > รหัสไคลเอ็นต์ OAuth
    3. เลือกประเภทแอปพลิเคชัน Android
  4. ในหน้าข้อมูลเข้าสู่ระบบ ให้สร้างรหัสไคลเอ็นต์ "เว็บแอปพลิเคชัน" ใหม่หากยังไม่ได้สร้าง คุณไม่ต้องสนใจช่อง "ต้นทาง JavaScript ที่ได้รับอนุญาต" และ "URI การเปลี่ยนเส้นทางที่ได้รับอนุญาต" ในตอนนี้ ระบบจะใช้รหัสไคลเอ็นต์นี้เพื่อระบุเซิร์ฟเวอร์แบ็กเอนด์เมื่อสื่อสารกับบริการตรวจสอบสิทธิ์ของ Google
    1. ไปที่หน้าข้อมูลเข้าสู่ระบบ
    2. คลิกสร้างข้อมูลเข้าสู่ระบบ > รหัสไคลเอ็นต์ OAuth
    3. เลือกประเภทเว็บแอปพลิเคชัน

ประกาศทรัพยากร Dependency

ในไฟล์ build.gradle ของโมดูล ให้ประกาศทรัพยากร Dependency โดยใช้ Credential Manager เวอร์ชันล่าสุด ดังนี้

dependencies {
  // ... other dependencies

  implementation "androidx.credentials:credentials:<latest version>"
  implementation "androidx.credentials:credentials-play-services-auth:<latest version>"
  implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}

สร้างอินสแตนซ์คำขอลงชื่อเข้าใช้ด้วย Google

ในการเริ่มต้นใช้งาน ให้สร้างอินสแตนซ์คำขอลงชื่อเข้าใช้ Google ใช้ GetGoogleIdOption เพื่อดึงข้อมูลโทเค็น Google ID ของผู้ใช้

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

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

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

เปิดใช้การลงชื่อเข้าใช้โดยอัตโนมัติสำหรับผู้ใช้ที่กลับมา (แนะนำ)

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

หากต้องการเปิดใช้การลงชื่อเข้าใช้อัตโนมัติ ให้ใช้ setAutoSelectEnabled(true) การลงชื่อเข้าใช้โดยอัตโนมัติจะทําได้ก็ต่อเมื่อมีคุณสมบัติตรงตามเกณฑ์ต่อไปนี้เท่านั้น

  • มีข้อมูลเข้าสู่ระบบรายการเดียวที่ตรงกับคำขอ ซึ่งอาจเป็นบัญชี Google หรือรหัสผ่าน และข้อมูลเข้าสู่ระบบนี้ตรงกับบัญชีเริ่มต้นในอุปกรณ์ Android
  • ผู้ใช้ไม่ได้ออกจากระบบอย่างชัดเจน
  • ผู้ใช้ไม่ได้ปิดใช้การลงชื่อเข้าใช้โดยอัตโนมัติในการตั้งค่าบัญชี Google
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

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

ตั้งค่า Nonce เพื่อปรับปรุงความปลอดภัย

หากต้องการเพิ่มความปลอดภัยในการลงชื่อเข้าใช้และหลีกเลี่ยงการโจมตีด้วยการเล่นซ้ำ ให้เพิ่ม setNonce เพื่อใส่ Nonce ในแต่ละคำขอ ดูข้อมูลเพิ่มเติมเกี่ยวกับการสร้าง Nonce

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

สร้างขั้นตอนลงชื่อเข้าใช้ด้วย Google

ขั้นตอนในการตั้งค่าขั้นตอนการลงชื่อเข้าใช้ด้วย Google มีดังนี้

  1. สร้าง GetCredentialRequest ขึ้นมา แล้วเพิ่ม googleIdOption ที่สร้างขึ้นก่อนหน้านี้โดยใช้ addCredentialOption() เพื่อเรียกข้อมูลเข้าสู่ระบบ
  2. ส่งคําขอนี้ไปยังการเรียกใช้ getCredential() (Kotlin) หรือ getCredentialAsync() (Java) เพื่อดึงข้อมูลเข้าสู่ระบบที่ใช้ได้ของผู้ใช้
  3. เมื่อ API ทำงานเสร็จแล้ว ให้ดึงข้อมูล CustomCredential ที่มีผลลัพธ์สำหรับข้อมูล GoogleIdTokenCredential
  4. ประเภทของ CustomCredential ควรเท่ากับค่าของ GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL แปลงออบเจ็กต์เป็น GoogleIdTokenCredential โดยใช้เมธอด GoogleIdTokenCredential.createFrom
  5. หากแปลงสำเร็จ ให้ดึงรหัส GoogleIdTokenCredential ตรวจสอบ และตรวจสอบสิทธิ์ข้อมูลเข้าสู่ระบบในเซิร์ฟเวอร์

  6. หาก Conversion ล้มเหลวด้วย GoogleIdTokenParsingException คุณอาจต้องอัปเดตเวอร์ชันคลังฟีเจอร์ลงชื่อเข้าใช้ด้วย Google

  7. จับประเภทข้อมูลเข้าสู่ระบบที่กำหนดเองที่ไม่รู้จัก

val request: GetCredentialRequest = Builder()
  .addCredentialOption(googleIdOption)
  .build()

coroutineScope.launch {
  try {
    val result = credentialManager.getCredential(
      request = request,
      context = activityContext,
    )
    handleSignIn(result)
  } catch (e: GetCredentialException) {
    handleFailure(e)
  }
}

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {

    // Passkey credential
    is PublicKeyCredential -> {
      // Share responseJson such as a GetCredentialResponse on your server to
      // validate and authenticate
      responseJson = credential.authenticationResponseJson
    }

    // Password credential
    is PasswordCredential -> {
      // Send ID and password to your server to validate and authenticate.
      val username = credential.id
      val password = credential.password
    }

    // GoogleIdToken credential
    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract the ID to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
          // You can use the members of googleIdTokenCredential directly for UX
          // purposes, but don't use them to store or control access to user
          // data. For that you first need to validate the token:
          // pass googleIdTokenCredential.getIdToken() to the backend server.
          GoogleIdTokenVerifier verifier = ... // see validation instructions
          GoogleIdToken idToken = verifier.verify(idTokenString);
          // To get a stable account identifier (e.g. for storing user data),
          // use the subject ID:
          idToken.getPayload().getSubject()
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      } else {
        // Catch any unrecognized custom credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

ทริกเกอร์ขั้นตอนปุ่มลงชื่อเข้าใช้ด้วย Google

หากต้องการเรียกใช้ขั้นตอนของปุ่มลงชื่อเข้าใช้ด้วย Google ให้ใช้ GetSignInWithGoogleOption แทน GetGoogleIdOption

val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder()
  .setServerClientId(WEB_CLIENT_ID)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

จัดการ GoogleIdTokenCredential ที่แสดงผลตามที่อธิบายในตัวอย่างโค้ดต่อไปนี้

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {
    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract id to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      }
      else -> {
        // Catch any unrecognized credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

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

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

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

หากไม่พบข้อมูลเข้าสู่ระบบที่บันทึกไว้ (ไม่มีบัญชี Google ที่แสดงโดย getGoogleIdOption) ให้แจ้งให้ผู้ใช้ลงชื่อสมัครใช้ ก่อนอื่น ให้ตรวจสอบว่า setFilterByAuthorizedAccounts(true) เพื่อดูว่ามีบัญชีที่เคยใช้อยู่หรือไม่ หากไม่พบ ให้แจ้งให้ผู้ใช้ลงชื่อสมัครใช้ด้วยบัญชี Google โดยใช้ setFilterByAuthorizedAccounts(false)

ตัวอย่าง

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(false)
  .setServerClientId(WEB_CLIENT_ID)
  .build()

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

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

เมื่อผู้ใช้ออกจากระบบแอป ให้เรียกใช้เมธอด API clearCredentialState() เพื่อล้างสถานะข้อมูลเข้าสู่ระบบปัจจุบันของผู้ใช้จากผู้ให้บริการข้อมูลเข้าสู่ระบบทั้งหมด ซึ่งจะแจ้งให้ผู้ให้บริการข้อมูลเข้าสู่ระบบทั้งหมดทราบว่าควรล้างเซสชันข้อมูลเข้าสู่ระบบที่เก็บไว้สำหรับแอปที่ระบุ

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