Xác thực bằng Firebase bằng cách sử dụng đường liên kết qua email trong Android

Bạn có thể sử dụng tính năng Xác thực Firebase để đăng nhập cho người dùng bằng cách gửi cho họ email chứa đường liên kết mà họ có thể nhấp vào để đăng nhập. Trong quá trình này, địa chỉ email của người dùng cũng được xác minh.

Có rất nhiều lợi ích khi đăng nhập bằng email:

  • Quy trình đăng ký và đăng nhập dễ dàng.
  • Giảm rủi ro sử dụng lại mật khẩu trên các ứng dụng, điều này có thể làm giảm tính bảo mật của những mật khẩu được chọn kỹ lưỡng.
  • Khả năng xác thực người dùng trong khi cũng xác minh rằng người dùng là chủ sở hữu hợp pháp của địa chỉ email.
  • Người dùng chỉ cần một tài khoản email có thể truy cập để đăng nhập. Không yêu cầu quyền sở hữu số điện thoại hoặc tài khoản mạng xã hội.
  • Người dùng có thể đăng nhập an toàn mà không cần cung cấp (hoặc ghi nhớ) mật khẩu. Mật khẩu này có thể rườm rà trên thiết bị di động.
  • Người dùng hiện tại từng đăng nhập bằng mã nhận dạng email (mật khẩu hoặc liên kết) có thể được nâng cấp để đăng nhập chỉ bằng email đó. Ví dụ: người dùng đã quên mật khẩu vẫn có thể đăng nhập mà không cần đặt lại mật khẩu.

Trước khi bắt đầu

Thiết lập dự án Android

  1. Thêm Firebase vào dự án Android của bạn nếu bạn chưa thực hiện.

  2. Trong tệp Gradle mô-đun (cấp ứng dụng) (thường là <project>/<app-module>/build.gradle.kts hoặc <project>/<app-module>/build.gradle), hãy thêm phần phụ thuộc cho thư viện Xác thực Firebase dành cho Android. Bạn nên sử dụng Firebase Android BoM để kiểm soát việc tạo phiên bản thư viện.

    Ngoài ra, trong quá trình thiết lập tính năng Xác thực Firebase, bạn cần thêm SDK Dịch vụ Google Play vào ứng dụng của mình.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:33.1.0"))
    
        // Add the dependency for the Firebase Authentication library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation("com.google.firebase:firebase-auth")
    // Also add the dependency for the Google Play services library and specify its version implementation("com.google.android.gms:play-services-auth:21.2.0")
    }

    Bằng cách sử dụng Firebase Android BoM, ứng dụng của bạn sẽ luôn sử dụng các phiên bản tương thích của thư viện Android Firebase.

    (Thay thế) Thêm các phần phụ thuộc thư viện Firebase mà không sử dụng BoM

    Nếu chọn không sử dụng BoM của Firebase, bạn phải chỉ định từng phiên bản thư viện Firebase trong dòng phần phụ thuộc tương ứng.

    Xin lưu ý rằng nếu sử dụng nhiều thư viện Firebase trong ứng dụng của mình, bạn nên sử dụng BoM để quản lý các phiên bản thư viện nhằm đảm bảo rằng mọi phiên bản đều tương thích.

    dependencies {
        // Add the dependency for the Firebase Authentication library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation("com.google.firebase:firebase-auth:23.0.0")
    // Also add the dependency for the Google Play services library and specify its version implementation("com.google.android.gms:play-services-auth:21.2.0")
    }
    Bạn đang tìm một mô-đun thư viện dành riêng cho Kotlin? Kể từ tháng 10 năm 2023 (Firebase BoM 32.5.0), cả nhà phát triển Kotlin và Java đều có thể sử dụng mô-đun thư viện chính (để biết thông tin chi tiết, hãy xem phần Câu hỏi thường gặp về sáng kiến này).

Để người dùng đăng nhập bằng đường liên kết email, trước tiên, bạn phải bật phương thức đăng nhập bằng Nhà cung cấp email và đường liên kết Email cho dự án Firebase của mình:

  1. Trong bảng điều khiển của Firebase, hãy mở phần Xác thực.
  2. Trên thẻ Phương thức đăng nhập, hãy bật nhà cung cấp Email/mật khẩu. Xin lưu ý rằng bạn phải bật tính năng đăng nhập bằng email/mật khẩu thì mới dùng được tính năng đăng nhập bằng đường liên kết email.
  3. Cũng trong phần này, hãy bật phương thức đăng nhập Email link (passwordless sign-in) (Đường liên kết qua email (đăng nhập không cần mật khẩu).
  4. Nhấp vào Lưu.

Để bắt đầu quy trình xác thực, hãy hiển thị cho người dùng một giao diện nhắc người dùng cung cấp địa chỉ email của họ, sau đó gọi sendSignInLinkToEmail để yêu cầu Firebase gửi đường liên kết xác thực đến email của người dùng.

  1. Tạo đối tượng ActionCodeSettings, đối tượng này cung cấp hướng dẫn cho Firebase về cách tạo đường liên kết email. Đặt các trường sau đây:

    • url: Đường liên kết sâu cần nhúng và mọi trạng thái bổ sung sẽ được chuyển. Bạn phải đưa miền của đường liên kết vào danh sách cho phép trong danh sách miền được uỷ quyền của Firebase Console. Bạn có thể tìm thấy miền này bằng cách chuyển đến thẻ Phương thức đăng nhập (Xác thực -> Phương thức đăng nhập). Đường liên kết này sẽ chuyển hướng người dùng đến URL này nếu ứng dụng không được cài đặt trên thiết bị của họ và không thể cài đặt.
    • androidPackageNameIOSBundleId: Các ứng dụng cần dùng khi đường liên kết đăng nhập được mở trên một thiết bị Android hoặc Apple. Hãy tìm hiểu thêm về cách định cấu hình Đường liên kết động của Firebase để mở đường liên kết hành động trong email thông qua ứng dụng di động.
    • handleCodeInApp: Đặt thành true. Hoạt động đăng nhập phải luôn hoàn tất trong ứng dụng, không giống như các thao tác khác qua email ngoài băng tần (đặt lại mật khẩu và xác minh email). Điều này là do vào cuối quy trình, người dùng dự kiến sẽ đăng nhập và trạng thái Xác thực của họ vẫn tồn tại trong ứng dụng.
    • dynamicLinkDomain: Khi nhiều miền đường liên kết động tuỳ chỉnh được xác định cho một dự án, hãy chỉ định miền cần sử dụng khi mở đường liên kết thông qua một ứng dụng di động cụ thể (ví dụ: example.page.link). Nếu không, miền đầu tiên sẽ tự động được chọn.

    Kotlin+KTX

    val actionCodeSettings = actionCodeSettings {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be whitelisted in the Firebase Console.
        url = "https://www.example.com/finishSignUp?cartId=1234"
        // This must be true
        handleCodeInApp = true
        setIOSBundleId("com.example.ios")
        setAndroidPackageName(
            "com.example.android",
            true, // installIfNotAvailable
            "12", // minimumVersion
        )
    }

    Java

    ActionCodeSettings actionCodeSettings =
            ActionCodeSettings.newBuilder()
                    // URL you want to redirect back to. The domain (www.example.com) for this
                    // URL must be whitelisted in the Firebase Console.
                    .setUrl("https://www.example.com/finishSignUp?cartId=1234")
                    // This must be true
                    .setHandleCodeInApp(true)
                    .setIOSBundleId("com.example.ios")
                    .setAndroidPackageName(
                            "com.example.android",
                            true, /* installIfNotAvailable */
                            "12"    /* minimumVersion */)
                    .build();

    Để tìm hiểu thêm về ActionCodeSettings, hãy tham khảo phần Trạng thái chuyển trong thao tác qua email.

  2. Yêu cầu người dùng cung cấp email của họ.

  3. Gửi đường liên kết xác thực đến email của người dùng và lưu email của người dùng phòng trường hợp họ hoàn tất quy trình đăng nhập email trên cùng một thiết bị.

    Kotlin+KTX

    Firebase.auth.sendSignInLinkToEmail(email, actionCodeSettings)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Email sent.")
            }
        }

    Java

    FirebaseAuth auth = FirebaseAuth.getInstance();
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Email sent.");
                    }
                }
            });

Các mối lo ngại về bảo mật

Để ngăn người dùng sử dụng đường liên kết đăng nhập để đăng nhập với tư cách là người dùng không mong muốn hoặc trên một thiết bị không mong muốn, tính năng Xác thực Firebase yêu cầu bạn phải cung cấp địa chỉ email của người dùng khi hoàn tất quy trình đăng nhập. Để đăng nhập thành công, địa chỉ email này phải khớp với địa chỉ mà đường liên kết đăng nhập được gửi ban đầu.

Bạn có thể đơn giản hoá quy trình này cho những người dùng mở đường liên kết đăng nhập trên cùng một thiết bị mà họ yêu cầu liên kết, bằng cách lưu trữ địa chỉ email của họ trên máy (ví dụ: sử dụng SharedPreferences) khi bạn gửi email đăng nhập. Sau đó, hãy sử dụng địa chỉ này để hoàn tất quy trình. Đừng truyền email của người dùng trong các tham số URL chuyển hướng và sử dụng lại vì điều này có thể kích hoạt thao tác chèn phiên.

Sau khi hoàn tất quá trình đăng nhập, mọi cơ chế đăng nhập chưa được xác minh trước đó sẽ bị xoá khỏi người dùng và mọi phiên hoạt động hiện có sẽ bị vô hiệu hoá. Ví dụ: nếu ai đó trước đây đã tạo một tài khoản chưa được xác minh có cùng email và mật khẩu, mật khẩu của người dùng sẽ bị xoá để ngăn kẻ mạo danh đã xác nhận quyền sở hữu và tạo tài khoản chưa được xác minh đó đăng nhập lại bằng email và mật khẩu chưa được xác minh.

Ngoài ra, hãy đảm bảo bạn sử dụng một URL HTTPS trong phiên bản chính thức để tránh việc máy chủ trung gian có thể chặn đường liên kết của bạn.

Hoàn tất quá trình đăng nhập trong ứng dụng Android

Tính năng Xác thực Firebase sử dụng Đường liên kết động của Firebase để gửi đường liên kết email đến một thiết bị di động. Để hoàn tất quá trình đăng nhập qua ứng dụng dành cho thiết bị di động, bạn phải định cấu hình ứng dụng để phát hiện đường liên kết đến ứng dụng, phân tích cú pháp đường liên kết sâu cơ sở rồi hoàn tất quy trình đăng nhập.

Tính năng Xác thực Firebase sử dụng Liên kết động của Firebase khi gửi đường liên kết sẽ được mở trong ứng dụng dành cho thiết bị di động. Để sử dụng tính năng này, bạn phải định cấu hình Đường liên kết động trong Bảng điều khiển của Firebase.

  1. Bật liên kết động của Firebase:

    1. Trong bảng điều khiển của Firebase, hãy mở mục Đường liên kết động.
    2. Nếu bạn chưa chấp nhận các điều khoản về Đường liên kết động và tạo một miền Đường liên kết động, hãy thực hiện ngay.

      Nếu bạn đã tạo một miền Liên kết động, hãy ghi lại miền này. Miền Liên kết động thường có dạng như ví dụ sau:

      example.page.link

      Bạn sẽ cần giá trị này khi định cấu hình ứng dụng của Apple hoặc Android để chặn đường liên kết đến.

  2. Định cấu hình ứng dụng Android:

    1. Để xử lý những đường liên kết này từ ứng dụng Android, bạn cần chỉ định tên gói Android trong phần cài đặt dự án trên Bảng điều khiển của Firebase. Ngoài ra, bạn cũng cần cung cấp SHA-1 và SHA-256 của chứng chỉ ứng dụng.
    2. Bây giờ, bạn đã thêm một miền liên kết động và đảm bảo rằng ứng dụng Android của bạn được định cấu hình chính xác, đường liên kết động sẽ chuyển hướng đến ứng dụng của bạn, bắt đầu từ hoạt động của trình chạy.
    3. Nếu muốn đường liên kết động chuyển hướng đến một hoạt động cụ thể, bạn cần định cấu hình bộ lọc ý định trong tệp AndroidManifest.xml. Bạn có thể thực hiện việc này bằng cách chỉ định miền liên kết động hoặc trình xử lý thao tác đối với email trong bộ lọc ý định. Theo mặc định, trình xử lý thao tác gửi email được lưu trữ trên một miền như ví dụ sau:
      PROJECT_ID.firebaseapp.com/
    4. Lưu ý:
      1. Đừng chỉ định URL mà bạn đặt trên actionCodeSettings trong bộ lọc ý định.
      2. Khi tạo miền liên kết động, có thể bạn cũng đã tạo một đường liên kết URL ngắn. URL ngắn này sẽ không được chuyển; đừng định cấu hình bộ lọc ý định để nắm bắt nó bằng thuộc tính android:pathPrefix. Điều này có nghĩa là bạn sẽ không thể phát hiện các đường liên kết động khác nhau trong các phần của ứng dụng. Tuy nhiên, bạn có thể kiểm tra tham số truy vấn mode trong đường liên kết để xem thao tác nào đang cố gắng thực hiện, hoặc sử dụng các phương thức SDK như isSignInWithEmailLink để xem đường liên kết mà ứng dụng của bạn nhận được có thực hiện thao tác mà bạn muốn hay không.
    5. Để biết thêm thông tin về cách nhận đường liên kết động, hãy tham khảo bài viết Nhận hướng dẫn về Đường liên kết động của Android.

Sau khi bạn nhận được đường liên kết như mô tả ở trên, hãy xác minh rằng đường liên kết đó dùng để xác thực đường liên kết email và hoàn tất quá trình đăng nhập.

Kotlin+KTX

val auth = Firebase.auth
val intent = intent
val emailLink = intent.data.toString()

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    val email = "[email protected]"

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Successfully signed in with email link!")
                val result = task.result
                // You can access the new user via result.getUser()
                // Additional user info profile *not* available via:
                // result.getAdditionalUserInfo().getProfile() == null
                // You can check if the user is new or existing:
                // result.getAdditionalUserInfo().isNewUser()
            } else {
                Log.e(TAG, "Error signing in with email link", task.exception)
            }
        }
}

Java

FirebaseAuth auth = FirebaseAuth.getInstance();
Intent intent = getIntent();
String emailLink = intent.getData().toString();

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    String email = "[email protected]";

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Successfully signed in with email link!");
                        AuthResult result = task.getResult();
                        // You can access the new user via result.getUser()
                        // Additional user info profile *not* available via:
                        // result.getAdditionalUserInfo().getProfile() == null
                        // You can check if the user is new or existing:
                        // result.getAdditionalUserInfo().isNewUser()
                    } else {
                        Log.e(TAG, "Error signing in with email link", task.getException());
                    }
                }
            });
}

Để tìm hiểu thêm về cách xử lý đường liên kết đăng nhập bằng email trong một ứng dụng của Apple, hãy tham khảo Hướng dẫn về nền tảng của Apple.

Để tìm hiểu cách xử lý việc đăng nhập bằng đường liên kết email trong ứng dụng web, vui lòng tham khảo Hướng dẫn sử dụng web.

Bạn cũng có thể liên kết phương pháp xác thực này với một người dùng hiện có. Ví dụ: người dùng trước đây đã xác thực với một nhà cung cấp khác, chẳng hạn như số điện thoại, có thể thêm phương thức đăng nhập này vào tài khoản hiện có của họ.

Sự khác biệt sẽ nằm ở nửa sau của toán tử:

Kotlin+KTX

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Link the credential to the current user.
Firebase.auth.currentUser!!.linkWithCredential(credential)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d(TAG, "Successfully linked emailLink credential!")
            val result = task.result
            // You can access the new user via result.getUser()
            // Additional user info profile *not* available via:
            // result.getAdditionalUserInfo().getProfile() == null
            // You can check if the user is new or existing:
            // result.getAdditionalUserInfo().isNewUser()
        } else {
            Log.e(TAG, "Error linking emailLink credential", task.exception)
        }
    }

Java

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Link the credential to the current user.
auth.getCurrentUser().linkWithCredential(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Successfully linked emailLink credential!");
                    AuthResult result = task.getResult();
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error linking emailLink credential", task.getException());
                }
            }
        });

Thao tác này cũng có thể được dùng để xác thực lại người dùng có đường liên kết email trước khi thực hiện một thao tác nhạy cảm.

Kotlin+KTX

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Re-authenticate the user with this credential.
Firebase.auth.currentUser!!.reauthenticateAndRetrieveData(credential)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            // User is now successfully reauthenticated
        } else {
            Log.e(TAG, "Error reauthenticating", task.exception)
        }
    }

Java

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Re-authenticate the user with this credential.
auth.getCurrentUser().reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // User is now successfully reauthenticated
                } else {
                    Log.e(TAG, "Error reauthenticating", task.getException());
                }
            }
        });

Tuy nhiên, do luồng có thể kết thúc trên một thiết bị khác mà người dùng ban đầu không đăng nhập, nên quy trình này có thể không hoàn tất. Trong trường hợp đó, người dùng có thể thấy lỗi để buộc họ mở đường liên kết trên cùng một thiết bị. Một số trạng thái có thể được chuyển vào đường liên kết để cung cấp thông tin về loại thao tác và uid của người dùng.

Nếu bạn tạo dự án vào hoặc sau ngày 15 tháng 9 năm 2023, thì tính năng bảo vệ liệt kê email sẽ được bật theo mặc định. Tính năng này giúp tăng cường bảo mật cho tài khoản người dùng trong dự án của bạn, nhưng vô hiệu hoá phương thức fetchSignInMethodsForEmail() (trước đây chúng tôi từng đề xuất triển khai quy trình ưu tiên giá trị nhận dạng).

Mặc dù có thể tắt tính năng bảo vệ liệt kê email cho dự án của mình, nhưng bạn không nên làm như vậy.

Hãy xem tài liệu về tính năng chống liệt kê email để biết thêm thông tin chi tiết.

Các bước tiếp theo

Sau khi người dùng đăng nhập lần đầu, một tài khoản người dùng mới sẽ được tạo và liên kết với thông tin đăng nhập (tức là tên người dùng và mật khẩu, số điện thoại hoặc thông tin nhà cung cấp dịch vụ xác thực) mà người dùng đã đăng nhập. Tài khoản mới này được lưu trữ trong dự án Firebase và có thể dùng để xác định người dùng trên mọi ứng dụng trong dự án của bạn, bất kể người dùng đó đăng nhập bằng cách nào.

  • Trong các ứng dụng của mình, bạn có thể lấy thông tin hồ sơ cơ bản của người dùng từ đối tượng FirebaseUser. Xem phần Quản lý người dùng.

  • Trong Quy tắc bảo mật của Cơ sở dữ liệu theo thời gian thực và Cloud Storage, bạn có thể lấy mã nhận dạng người dùng duy nhất của người dùng đã đăng nhập từ biến auth rồi dùng biến đó để kiểm soát những dữ liệu mà một người dùng có thể truy cập.

Bạn có thể cho phép người dùng đăng nhập vào ứng dụng của mình thông qua nhiều trình cung cấp dịch vụ xác thực bằng cách liên kết thông tin xác thực của nhà cung cấp dịch vụ xác thực với một tài khoản người dùng hiện có.

Để đăng xuất cho một người dùng, hãy gọi signOut:

Kotlin+KTX

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();