Đă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 đăng nhập trước đây họ đã dùng để đă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-mật khẩu mà họ đã lưu bằng Google thông qua 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 suôn sẻ.

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 đăng nhập mà người dùng đã dùng trước đó để đăng nhập, 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ề cách họ đăng nhập lần gần đây nhất và ngăn họ vô tình tạo tài khoản mới bằng ứ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 có trải nghiệm được cải thiện bằng cách đă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ì đó sẽ là ngữ cảnh hợp lý cho đăng nhập bằng một lần chạm.

Các ứng dụng không bắt buộc có thể đă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 hoặc một trong hai cách sau. (Bạn nên hỗ trợ cả hai để cho phép tạo tài khoản bằng một lần nhấn cho người dùng mới và đă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 sử dụng setGoogleIdTokenRequestOptions() để bật và định cấu hình các yêu cầu liên quan đến 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ã nhận dạng bạn đã tạo trong bảng điều khiển API của Google. Xin lưu ý đây là mã ứng dụng khách của máy chủ chứ không phải mã ứng dụng khách Android của bạn.

  • Định cấu hình ứng dụng để lọc theo 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 trong quá khứ. 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 mình đã có tài khoản hay chưa hoặc đã 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 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 của SafetyNet để biết các đề xuất và thông tin chi tiết khác 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 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 liệu 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. (Hãy 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 Một lần chạm 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 nhận ý định đang chờ xử lý từ kết quả Task và chuyể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 đăng nhập nào, thì ứ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 trình bày trải nghiệm khi đã đăng xuất của ứng dụng. Tuy nhiên, nếu hỗ trợ tính năng đă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 về 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 đăng nhập đã 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, thì kết quả sẽ trả về cùng 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 này 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ác rỗng nếu người dùng chia sẻ mật khẩu đã lưu.

Sử dụng thông tin đăng nhập để xác thực với phần phụ trợ của ứng dụng.

  • Nếu một 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 các cặp này theo cách thủ công.
  • Nếu thông tin đăng nhập của Tài khoản Google đã được truy xuất, hãy sử dụng mã thông báo mã nhận dạng để 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ợ bằ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 bằng một lần chạm

Nếu người dùng từ chối đăng nhập, thì lệnh gọi đến getSignInCredentialFromIntent() sẽ gửi một ApiException chứa mã trạng thái CommonStatusCodes.CANCELED. Khi điều này xảy ra, bạn nên tạm thời vô hiệu hoá giao diện người dùng đăng nhập bằng một lần chạm để không làm phiền người dùng bằng những lời nhắc lặp lại. Ví dụ sau giúp bạn thực hiện điều này bằng cách đặt một thuộc tính cho Hoạt động. Thuộc tính này sẽ sử dụng thuộc tính để xác định xem có cho phép người dù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 một 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 giới hạn số lượng yêu cầu đăng nhập bằng một lần chạm của riêng bạn. Nếu bạn không thực hiện việc nà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. Việc gọi signOut() sẽ vô hiệu hoá chế độ 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 quan trọng vì đả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ụ 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ã thông báo giá trị nhận dạng của 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 suôn sẻ vào ứng dụng của mình.