Xác thực người dùng bằng tính năng Đăng nhập bằng Google

Tính năng Đăng nhập bằng Google giúp bạn nhanh chóng tích hợp tính năng xác thực người dùng với ứng dụng Android. Người dùng có thể sử dụng Tài khoản Google của họ để đăng nhập vào ứng dụng, đồng ý và chia sẻ an toàn thông tin hồ sơ của họ với ứng dụng. Thư viện Jetpack Trình quản lý thông tin xác thực của Android giúp quá trình tích hợp này diễn ra suôn sẻ, mang lại trải nghiệm nhất quán trên các thiết bị Android bằng một API duy nhất.

Tài liệu này hướng dẫn bạn cách triển khai tính năng Đăng nhập bằng Google trong các ứng dụng Android, cách thiết lập giao diện người dùng của nút Đăng nhập bằng Google và cách định cấu hình trải nghiệm đăng ký và đăng nhập bằng một lần chạm được tối ưu hoá cho ứng dụng. Để di chuyển thiết bị một cách suôn sẻ, tính năng Đăng nhập bằng Google hỗ trợ tính năng tự động đăng nhập và tính chất đa nền tảng của tính năng này trên Android, iOS và các nền tảng web giúp bạn cung cấp quyền đăng nhập cho ứng dụng trên mọi thiết bị.

Để thiết lập tính năng Đăng nhập bằng Google, hãy làm theo hai bước chính sau:

Định cấu hình tính năng Đăng nhập bằng Google làm một tuỳ chọn cho giao diện người dùng bảng dưới cùng của Trình quản lý thông tin xác thực. Bạn có thể định cấu hình để tự động nhắc người dùng đăng nhập. Nếu đã triển khai khoá truy cập hoặc mật khẩu, bạn có thể yêu cầu đồng thời tất cả các loại thông tin xác thực có liên quan để người dùng không phải nhớ phương thức họ đã sử dụng trước đó để đăng nhập.

Bảng dưới cùng của Trình quản lý thông tin xác thực
Hình 1. Giao diện người dùng chọn thông tin xác thực trên bảng dưới cùng của Trình quản lý thông tin xác thực

Thêm nút Đăng nhập bằng Google vào giao diện người dùng của ứng dụng. Nút Đăng nhập bằng Google giúp người dùng sử dụng Tài khoản Google hiện có để đăng ký hoặc đăng nhập vào các ứng dụng Android một cách nhanh chóng. Người dùng sẽ nhấp vào nút Đăng nhập bằng Google nếu họ đóng giao diện người dùng của bảng dưới cùng hoặc nếu họ rõ ràng muốn sử dụng Tài khoản Google của mình để đăng ký và đăng nhập. Đối với nhà phát triển, điều này có nghĩa là người dùng dễ dàng làm quen hơn và giảm bớt sự phiền hà trong quá trình đăng ký.

Ảnh động minh hoạ quy trình Đăng nhập bằng Google
Hình 2. Giao diện người dùng của nút Đăng nhập bằng Google trong Trình quản lý thông tin xác thực

Tài liệu này giải thích cách tích hợp nút Đăng nhập bằng Google và hộp thoại bảng dưới cùng với API Trình quản lý thông tin xác thực bằng thư viện trình trợ giúp Mã nhận dạng trên Google.

Thiết lập dự án trên Bảng điều khiển API của Google

  1. Mở dự án của bạn trong Bảng điều khiển API hoặc tạo dự án nếu bạn chưa có dự án.
  2. Trên trang màn hình xin phép bằng OAuth, hãy đảm bảo tất cả thông tin đều đầy đủ và chính xác.
    1. Đảm bảo rằng ứng dụng của bạn đã được chỉ định Tên ứng dụng, Biểu trưng ứng dụng và Trang chủ ứng dụng chính xác. Các giá trị này sẽ được trình bày cho người dùng trên màn hình đồng ý sử dụng tính năng Đăng nhập bằng Google khi đăng ký và trên màn hình Ứng dụng và dịch vụ của bên thứ ba.
    2. Hãy đảm bảo bạn đã chỉ định các URL cho chính sách quyền riêng tư và điều khoản dịch vụ của ứng dụng.
  3. Trên trang Thông tin xác thực, hãy tạo một mã ứng dụng khách Android cho ứng dụng của bạn nếu bạn chưa có mã này. Bạn sẽ cần chỉ định tên gói và chữ ký SHA-1 của ứng dụng.
    1. Chuyển đến trang Thông tin xác thực.
    2. Nhấp vào Tạo thông tin xác thực > Mã ứng dụng khách OAuth.
    3. Chọn loại ứng dụng Android.
  4. Trên trang Thông tin xác thực, hãy tạo một mã ứng dụng khách "Ứng dụng web" mới nếu bạn chưa có. Hiện tại, bạn có thể bỏ qua các trường "Nguồn gốc JavaScript được cho phép" và "URI chuyển hướng được cho phép". Mã ứng dụng khách này sẽ được dùng để xác định máy chủ phụ trợ của bạn khi máy chủ này giao tiếp với các dịch vụ xác thực của Google.
    1. Chuyển đến trang Thông tin xác thực.
    2. Nhấp vào Tạo thông tin xác thực > Mã ứng dụng khách OAuth.
    3. Chọn loại ứng dụng Web.

Khai báo phần phụ thuộc

Trong tệp build.gradle của mô-đun, hãy khai báo các phần phụ thuộc bằng cách sử dụng phiên bản mới nhất của Trình quản lý thông tin xác thực:

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>"
}

Tạo thực thể cho yêu cầu đăng nhập bằng Google

Để bắt đầu triển khai, hãy tạo thực thể cho một yêu cầu đăng nhập bằng Google. Sử dụng GetGoogleIdOption để truy xuất Mã thông báo cho mã nhận dạng trên Google của người dùng.

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()

Trước tiên, hãy kiểm tra xem người dùng có tài khoản nào từng dùng để đăng nhập vào ứng dụng của bạn hay không bằng cách gọi API với tham số setFilterByAuthorizedAccounts được đặt thành true. Người dùng có thể chọn một trong các tài khoản hiện có để đăng nhập.

Nếu không có Tài khoản Google được uỷ quyền, người dùng sẽ được nhắc đăng ký bằng bất kỳ tài khoản nào hiện có. Để thực hiện việc này, hãy nhắc người dùng bằng cách gọi lại API và đặt setFilterByAuthorizedAccounts thành false. Tìm hiểu thêm về cách đăng ký.

Bật tính năng tự động đăng nhập cho người dùng cũ (nên dùng)

Nhà phát triển nên bật tính năng tự động đăng nhập cho những người dùng đăng ký bằng một tài khoản. Điều này mang lại trải nghiệm liền mạch trên các thiết bị, đặc biệt là trong quá trình di chuyển thiết bị, người dùng có thể nhanh chóng lấy lại quyền truy cập vào tài khoản của họ mà không cần nhập lại thông tin xác thực. Đối với người dùng, việc này sẽ loại bỏ sự phiền toái không cần thiết khi họ đã đăng nhập trước đó.

Để bật tính năng tự động đăng nhập, hãy sử dụng setAutoSelectEnabled(true). Người dùng chỉ có thể tự động đăng nhập khi đáp ứng các tiêu chí sau:

  • Có một thông tin đăng nhập duy nhất khớp với yêu cầu, đó có thể là Tài khoản Google hoặc mật khẩu. Thông tin đăng nhập này khớp với tài khoản mặc định trên thiết bị chạy Android.
  • Người dùng chưa đăng xuất một cách rõ ràng.
  • Người dùng chưa tắt tính năng tự động đăng nhập trong phần Cài đặt Tài khoản 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()

Hãy nhớ xử lý quy trình đăng xuất đúng cách khi triển khai tính năng tự động đăng nhập để người dùng luôn có thể chọn đúng tài khoản sau khi họ thể hiện rõ việc đăng xuất khỏi ứng dụng của bạn.

Đặt số chỉ dùng một lần để tăng cường bảo mật

Để cải thiện khả năng bảo mật khi đăng nhập và tránh các cuộc tấn công phát lại, hãy thêm setNonce để đưa số chỉ dùng một lần vào mỗi yêu cầu. Tìm hiểu thêm về cách tạo số chỉ dùng một lần.

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()

Tạo quy trình Đăng nhập bằng Google

Các bước thiết lập quy trình Đăng nhập bằng Google như sau:

  1. Tạo thực thể cho GetCredentialRequest, sau đó thêm googleIdOption đã tạo trước đó bằng addCredentialOption() để truy xuất thông tin xác thực.
  2. Truyền yêu cầu này đến lệnh gọi getCredential() (Kotlin) hoặc getCredentialAsync() (Java) để truy xuất thông tin đăng nhập có sẵn của người dùng.
  3. Sau khi API thành công, hãy trích xuất CustomCredential chứa kết quả cho dữ liệu GoogleIdTokenCredential.
  4. Loại của CustomCredential phải bằng với giá trị của GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL. Chuyển đổi đối tượng thành GoogleIdTokenCredential bằng phương thức GoogleIdTokenCredential.createFrom.
  5. Nếu quá trình chuyển đổi thành công, hãy trích xuất mã nhận dạng GoogleIdTokenCredential, xác thực mã này và xác thực thông tin đăng nhập trên máy chủ của bạn.

  6. Nếu không chuyển đổi được GoogleIdTokenParsingException, thì bạn có thể cần cập nhật phiên bản thư viện Đăng nhập bằng Google.

  7. Nắm bắt mọi loại thông tin đăng nhập tuỳ chỉnh không được nhận dạng.

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")
    }
  }
}

Kích hoạt quy trình nút Đăng nhập bằng Google

Để kích hoạt quy trình đăng nhập bằng nút Đăng nhập bằng Google, hãy sử dụng GetSignInWithGoogleOption thay vì GetGoogleIdOption:

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

Xử lý GoogleIdTokenCredential được trả về như mô tả trong ví dụ mã sau.

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")
    }
  }
}

Sau khi tạo thực thể cho yêu cầu đăng nhập bằng Google, hãy chạy quy trình xác thực theo cách tương tự như đã đề cập trong phần Đăng nhập bằng Google.

Bật tính năng đăng ký cho người dùng mới (nên dùng)

Tính năng Đăng nhập bằng Google là cách dễ dàng nhất để người dùng tạo tài khoản mới bằng ứng dụng hoặc dịch vụ của bạn chỉ bằng vài thao tác nhấn.

Nếu không tìm thấy thông tin xác thực đã lưu (getGoogleIdOption không trả về Tài khoản Google nào), hãy nhắc người dùng đăng ký. Trước tiên, hãy kiểm tra xem setFilterByAuthorizedAccounts(true) để xem liệu còn tài khoản nào đã dùng trước đó hay không. Nếu không tìm thấy, hãy nhắc người dùng đăng ký bằng Tài khoản Google của họ bằng cách sử dụng setFilterByAuthorizedAccounts(false)

Ví dụ:

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

Sau khi tạo thực thể cho yêu cầu đăng ký bằng Google, hãy chạy quy trình xác thực. Nếu người dùng không muốn sử dụng tính năng Đăng nhập bằng Google để đăng ký, hãy cân nhắc tối ưu hoá ứng dụng của bạn để tự động điền. Sau khi người dùng tạo tài khoản, hãy cân nhắc việc đăng ký khoá truy cập cho họ như một bước cuối cùng để tạo tài khoản.

Xử lý khi đăng xuất

Khi người dùng đăng xuất khỏi ứng dụng, hãy gọi phương thức API clearCredentialState() để xoá trạng thái thông tin đăng nhập của người dùng hiện tại khỏi tất cả các trình cung cấp thông tin xác thực. Thao tác này sẽ thông báo cho tất cả trình cung cấp thông tin xác thực rằng mọi phiên thông tin xác thực đã lưu trữ cho ứng dụng nhất định sẽ bị xoá.

Trình cung cấp thông tin xác thực có thể đã lưu trữ một phiên thông tin xác thực đang hoạt động và sử dụng phiên đó để giới hạn các tuỳ chọn đăng nhập cho các lệnh gọi get-credential trong tương lai. Ví dụ: Hệ thống có thể ưu tiên thông tin đăng nhập đang hoạt động hơn bất kỳ thông tin đăng nhập nào khác hiện có. Khi người dùng đăng xuất khỏi ứng dụng một cách rõ ràng và để có các lựa chọn đăng nhập toàn diện vào lần tiếp theo, bạn nên gọi API này để cho phép nhà cung cấp xoá mọi phiên thông tin xác thực đã lưu trữ.