Đăng nhập cho người dùng bằng thông tin xác thực mà họ đã lưu

Sử dụng ứng dụng đăng nhập bằng một lần chạm để yêu cầu người dùng cấp quyền truy xuất một trong các thông tin xác thực họ đã dùng trước đó để đăng nhập vào ứng dụng của bạn. Những thông tin đăng nhập này có thể là Tài khoản Google hoặc tổ hợp tên người dùng và mật khẩu mà họ đã lưu vào Google bằng Chrome, tính năng tự động điền của Android hoặc Smart Lock cho mật khẩu.

Giao diện người dùng đăng nhập bằng một lần nhấn

Khi truy xuất thành công thông tin xác thực, bạn có thể sử dụng thông tin đó để đăng nhập người dùng vào ứng dụng một cách dễ dàng.

Nếu người dùng chưa lưu thông tin xác thực, thì sẽ không có giao diện người dùng nào hiển thị và bạn có thể cung cấp trải nghiệm đăng xuất thông thường.

Tôi nên dùng tính năng đăng nhập bằng một lần chạm ở đâu?

Nếu ứng dụng của bạn yêu cầu người dùng đăng nhập, hãy hiển thị giao diện người dùng Một lần chạm trên màn hình đăng nhập. Thao tác này có thể hữu ích ngay cả khi bạn đã có nút "Đăng nhập bằng Google": vì giao diện người dùng Một lần chạm có thể được định cấu hình để chỉ hiển thị thông tin xác thực mà người dùng đã dùng để đăng nhập trước đó, nên đây có thể là lời nhắc cho những người dùng không thường xuyên đăng nhập vào lần đăng nhập gần đây nhất và ngăn họ vô tình tạo tài khoản mới trong ứng dụng của bạn.

Nếu ứng dụng của bạn không bắt buộc phải đăng nhập, hãy cân nhắc sử dụng tính năng đăng nhập bằng một lần chạm trên bất kỳ màn hình nào mang đến trải nghiệm tốt hơn nhờ việc đăng nhập. Ví dụ: nếu người dùng có thể duyệt xem nội dung bằng ứng dụng của bạn khi đã đăng xuất, nhưng chỉ có thể đăng bình luận hoặc thêm mặt hàng vào giỏ hàng sau khi đăng nhập, thì đó chính là ngữ cảnh phù hợp cho tính năng đăng nhập bằng một lần chạm.

Các ứng dụng không bắt buộc đăng nhập cũng nên sử dụng tính năng đăng nhập bằng một lần chạm trên màn hình đăng nhập vì những lý do nêu trên.

Trước khi bắt đầu

1. Định cấu hình ứng dụng đăng nhập bằng một lần chạm

Bạn có thể định cấu hình ứng dụng đăng nhập bằng một lần chạm để đăng nhập người dùng bằng mật khẩu đã lưu, Tài khoản Google đã lưu, v.v. (Bạn nên hỗ trợ cả hai thành phần này, bật tính năng tạo tài khoản bằng một lần nhấn cho người dùng mới và bật tính năng đăng nhập tự động hoặc bằng một lần nhấn cho nhiều người dùng cũ nhất có thể.)

Nếu ứng dụng của bạn dùng tính năng đăng nhập dựa trên mật khẩu, hãy dùng setPasswordRequestOptions() để bật yêu cầu thông tin xác thực mật khẩu.

Nếu ứng dụng của bạn sử dụng tính năng Đăng nhập bằng Google, hãy dùng setGoogleIdTokenRequestOptions() để bật và định cấu hình các yêu cầu mã thông báo giá trị nhận dạng của Google:

  • Đặt mã ứng dụng khách của máy chủ thành mã mà bạn đã tạo trong bảng điều khiển API của Google. Xin lưu ý rằng đây là mã ứng dụng khách của máy chủ, không phải mã ứng dụng khách Android của bạn.

  • Định cấu hình máy khách để lọc theo các tài khoản được uỷ quyền. Khi bạn bật tuỳ chọn này, ứng dụng Một lần chạm chỉ nhắc người dùng đăng nhập vào ứng dụng của bạn bằng Tài khoản Google mà họ đã sử dụng trước đây. Làm như vậy có thể giúp người dùng đăng nhập thành công khi họ không chắc liệu mình đã có tài khoản hay chưa hay đã sử dụng Tài khoản Google nào, đồng thời ngăn người dùng vô tình tạo tài khoản mới bằng ứng dụng của bạn.

  • Nếu bạn muốn tự động đăng nhập người dùng khi có thể, hãy bật tính năng này bằng setAutoSelectEnabled(). Người dùng có thể tự động được đăng nhập khi đáp ứng các tiêu chí sau đây:

    • Người dùng đã lưu chính xác một thông tin đăng nhập cho ứng dụng của bạn. Tức là người dùng đã lưu một mật khẩu hoặc một Tài khoản Google.
    • 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.
  • Mặc dù không bắt buộc, nhưng bạn nên cân nhắc sử dụng số chỉ dùng một lần để 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. Sử dụng setNonce để đưa số chỉ dùng một lần vào mỗi yêu cầu. Hãy xem phần Lấy số chỉ dùng một lần trong SafetyNet để biết các đề xuất và thông tin chi tiết bổ sung về cách tạo số chỉ dùng một lần.

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. Kiểm tra xem có người dùng đã đăng nhập hay không

Nếu người dùng đã đăng nhập hoặc người dùng đã đăng xuất có thể sử dụng Hoạt động của bạn, hãy kiểm tra trạng thái của người dùng đó trước khi hiển thị giao diện người dùng đăng nhập bằng một lần chạm.

Bạn cũng nên theo dõi xem người dùng đã từ chối sử dụng tính năng Đăng nhập bằng một lần chạm hay chưa bằng cách đóng lời nhắc hoặc nhấn vào bên ngoài lời nhắc. Việc này có thể đơn giản như một thuộc tính boolean của Hoạt động. (Xem phần Dừng hiển thị giao diện người dùng Một lần chạm ở bên dưới.)

3. Hiển thị giao diện người dùng đăng nhập bằng một lần chạm

Nếu người dùng chưa đăng nhập và chưa từ chối sử dụng tính năng đăng nhập bằng một lần chạm, hãy gọi phương thức beginSignIn() của đối tượng ứng dụng và đính kèm trình nghe vào Task mà phương thức này trả về. Các ứng dụng thường thực hiện việc này trong phương thức onCreate() của Hoạt động hoặc sau khi chuyển đổi màn hình khi sử dụng cấu trúc Hoạt động đơn.

Ứng dụng One Tap sẽ gọi trình nghe thành công nếu người dùng có bất kỳ thông tin đăng nhập nào đã lưu cho ứng dụng của bạn. Trong trình nghe thành công, hãy lấy ý định đang chờ xử lý từ kết quả Task và truyền ý định đó đến startIntentSenderForResult() để bắt đầu giao diện người dùng đăng nhập bằng một lần chạm.

Nếu người dùng chưa lưu thông tin xác thực nào, ứng dụng Một lần chạm sẽ gọi trình nghe lỗi. Trong trường hợp này, bạn không cần làm gì cả: bạn chỉ cần tiếp tục hiển thị trải nghiệm khi đã đăng xuất của ứng dụng. Tuy nhiên, nếu hỗ trợ quy trình đăng ký bằng một lần chạm, bạn có thể bắt đầu quy trình đó tại đây để có trải nghiệm tạo tài khoản liền mạch. Hãy xem bài viết Tạo tài khoản mới chỉ bằng một lần nhấn.

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. Xử lý phản hồi của người dùng

Phản hồi của người dùng cho lời nhắc đăng nhập bằng một lần chạm sẽ được báo cáo cho ứng dụng của bạn bằng phương thức onActivityResult() của Hoạt động. Nếu người dùng chọn đăng nhập, kết quả sẽ là thông tin xác thực đã lưu. Nếu người dùng từ chối đăng nhập, bằng cách đóng giao diện người dùng Một lần chạm hoặc nhấn vào bên ngoài giao diện người dùng, kết quả sẽ trả về với mã RESULT_CANCELED. Ứng dụng của bạn cần xử lý cả hai khả năng.

Đăng nhập bằng thông tin xác thực đã truy xuất

Nếu người dùng đã chọn chia sẻ thông tin đăng nhập với ứng dụng của bạn, bạn có thể truy xuất thông tin đó bằng cách truyền dữ liệu ý định từ onActivityResult() sang phương thức getSignInCredentialFromIntent() của ứng dụng Một lần chạm. Thông tin đăng nhập sẽ có thuộc tính googleIdToken khác rỗng nếu người dùng chia sẻ thông tin đăng nhập Tài khoản Google với ứng dụng của bạn, hoặc thuộc tính password không rỗng nếu người dùng chia sẻ mật khẩu đã lưu.

Hãy sử dụng thông tin xác thực để xác thực với phần phụ trợ của ứng dụng.

  • Nếu cặp tên người dùng và mật khẩu được truy xuất, hãy sử dụng các cặp này để đăng nhập theo cách tương tự như khi người dùng cung cấp theo cách thủ công.
  • Nếu hệ thống truy xuất thông tin xác thực Tài khoản Google, hãy sử dụng mã thông báo để xác thực với phần phụ trợ của bạn. Nếu bạn đã chọn sử dụng số chỉ dùng một lần để giúp tránh các cuộc tấn công phát lại, hãy kiểm tra giá trị phản hồi trên máy chủ phụ trợ. Hãy xem bài viết Xác thực bằng phần phụ trợ sử dụng mã thông báo giá trị nhận dạng.

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) {
                    // ...
                }
            }
        }
    }
    // ...
}

Dừng hiển thị giao diện người dùng Một lần chạm

Nếu người dùng từ chối đăng nhập, lệnh gọi đến getSignInCredentialFromIntent() sẽ gửi một ApiException kèm theo mã trạng thái CommonStatusCodes.CANCELED. Khi điều này xảy ra, bạn nên tạm thời tắt giao diện người dùng đăng nhập bằng một lần chạm để không gây khó chịu cho người dùng bằng những lời nhắc lặp lại. Ví dụ sau đây thực hiện điều này bằng cách thiết lập thuộc tính trên Hoạt động. Thuộc tính này sẽ sử dụng thuộc tính này để xác định xem có nên cung cấp cho người dùng tính năng đăng nhập bằng một lần chạm hay không; tuy nhiên, bạn cũng có thể lưu giá trị vào SharedPreferences hoặc sử dụng một số phương thức khác.

Bạn cần phải triển khai lời nhắc đăng nhập bằng một lần chạm của riêng mình với giới hạn số lượng yêu cầu. Nếu bạn không làm như vậy và người dùng huỷ nhiều lời nhắc liên tiếp, thì ứng dụng Một lần chạm sẽ không nhắc người dùng trong 24 giờ tiếp theo.

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. Xử lý khi đăng xuất

Khi người dùng đăng xuất khỏi ứng dụng của bạn, hãy gọi phương thức signOut() của ứng dụng Một lần chạm. Khi gọi signOut(), hệ thống sẽ vô hiệu hoá tính năng tự động đăng nhập cho đến khi người dùng đăng nhập lại.

Ngay cả khi bạn không sử dụng tính năng tự động đăng nhập, bước này vẫn rất quan trọng vì giúp đảm bảo rằng khi người dùng đăng xuất khỏi ứng dụng, trạng thái xác thực của mọi API Dịch vụ Google Play mà bạn sử dụng cũng sẽ được đặt lại.

Các bước tiếp theo

Nếu bạn đã định cấu hình ứng dụng Một lần chạm để truy xuất thông tin xác thực Google, thì giờ đây, ứng dụng của bạn có thể nhận mã nhận dạng Google đại diện cho Tài khoản Google của người dùng. Tìm hiểu cách sử dụng các mã thông báo này trên phần phụ trợ.

Nếu hỗ trợ tính năng Đăng nhập bằng Google, bạn cũng có thể sử dụng ứng dụng Một lần chạm để thêm các quy trình tạo tài khoản đơn giản vào ứng dụng của mình.