Xác thực bằng Firebase bằng đường liên kết email trong Android

Bạn có thể sử dụng Firebase Authentication để đăng nhập cho người dùng bằng cách gửi cho họ một 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.

Việc đăng nhập bằng email mang lại nhiều lợi ích:

  • Đăng ký và đăng nhập dễ dàng.
  • Giảm nguy cơ 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 ngay cả 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 một địa chỉ email.
  • Người dùng chỉ cần có một tài khoản email có thể truy cập để đăng nhập. Bạn không cần phải 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 một cách an toàn mà không cần cung cấp (hoặc nhớ) mật khẩu, điều này có thể gây phiền hà trên thiết bị di động.
  • Người dùng hiện tại từng đăng nhập bằng giá trị nhận dạng email (mật khẩu hoặc liên kết) có thể được nâng cấp để chỉ đăng nhập 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. Nếu bạn chưa thực hiện, hãy thêm Firebase vào dự án Android.

  2. Trong tệp Gradle (ở cấp ứng dụng) của mô-đun (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 Firebase Authentication 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 Firebase Authentication, bạn cần thêm SDK Dịch vụ Google Play vào ứng dụng.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:33.8.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.3.0")
    }

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

    (Phương án thay thế)  Thêm 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 Firebase BoM, bạn phải chỉ định từng phiên bản thư viện Firebase trong dòng phần phụ thuộc của thư viện đó.

    Xin lưu ý rằng nếu bạn sử dụng nhiều thư viện Firebase trong ứng dụng, 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 tất cả các 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.1.0")
    // Also add the dependency for the Google Play services library and specify its version implementation("com.google.android.gms:play-services-auth:21.3.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ể phụ thuộc vào mô-đun thư viện chính (để biết thông tin chi tiết, hãy xem Câu hỏi thường gặp về sáng kiến này).

Bật tính năng Đăng nhập bằng đường liên kết email cho dự án Firebase

Để 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 Nhà cung cấp email và phương thức Đăng nhập bằng đường liên kết email cho dự án Firebase:

  1. Trong bảng điều khiển Firebase, hãy mở phần Auth (Xác thực).
  2. Trên thẻ Phương thức đăng nhập, hãy bật trì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 để sử dụng tính năng đăng nhập bằng đường liên kết trong email.
  3. Trong cùng phần, hãy bật phương thức đăng nhập Đường liên kết 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 họ cung cấp địa chỉ email, 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 cho Firebase hướng dẫn về cách tạo đường liên kết email. Đặt các trường sau:

    • url: Đường liên kết sâu để nhúng và mọi trạng thái bổ sung sẽ được truyền cùng. Miền của đường liên kết phải có trong danh sách miền được uỷ quyền của Bảng điều khiển Firebase. Bạn có thể tìm thấy danh sách 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 sẽ chuyển hướng người dùng đến URL này nếu ứng dụng chưa được cài đặt trên thiết bị của họ và họ không thể cài đặt ứng dụng.
    • androidPackageNameiOSBundleId: Giúp Firebase Authentication xác định xem có nên tạo đường liên kết chỉ dành cho web hay đường liên kết dành cho thiết bị di động được mở trên thiết bị Android hay Apple hay không.
    • handleCodeInApp: Đặt thành true. Thao tác đăng nhập phải luôn được hoàn tất trong ứng dụng, không giống như các thao tác khác ngoài phạm vi email (đặt lại mật khẩu và xác minh email). Lý do là ở cuối luồng, người dùng dự kiến sẽ đăng nhập và trạng thái xác thực của họ sẽ được duy trì trong ứng dụng.
    • linkDomain: Khi bạn xác định miền liên kết Hosting tuỳ chỉnh cho một dự án, hãy chỉ định miền nào sẽ được sử dụng khi một ứng dụng di động được chỉ định mở đường liên kết. Nếu không, miền mặc định sẽ tự động được chọn (ví dụ: PROJECT_ID.firebaseapp.com).
    • dynamicLinkDomain: Không dùng nữa. Không chỉ định thông số này.

    Kotlin

    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 Chuyển trạng thái trong Hành động 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 trong trường hợp người dùng hoàn tất quy trình đăng nhập bằng email trên cùng một thiết bị.

    Kotlin

    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 việc sử dụng đường liên kết đăng nhập để đăng nhập dưới vai trò là người dùng không mong muốn hoặc trên một thiết bị không mong muốn, Firebase Authentication yêu cầu người dùng cung cấp địa chỉ email 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à bạn đã nhận được đường liên kết đăng nhập 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 đường liên kết, bằng cách lưu trữ địa chỉ email của họ trên thiết bị (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. Khô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 email đó vì việc này có thể cho phép chèn phiên.

Sau khi hoàn tất quy 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 hiện có sẽ mất hiệu lực. Ví dụ: nếu trước đây có người đã tạo một tài khoản chưa được xác minh bằng cùng một email và mật khẩu, thì 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 URL HTTPS trong phiên bản chính thức để tránh trường hợp máy chủ trung gian chặn đường liên kết của bạn.

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

Firebase Authentication sử dụng Firebase Hosting để gửi đường liên kết trong email đến một thiết bị di động. Để hoàn tất quy trình đăng nhập thông qua ứng dụng di động, ứng dụng phải được định cấu hình để phát hiện đường liên kết ứng dụng sắp tới, phân tích cú pháp đường liên kết sâu cơ bản rồi hoàn tất quy trình đăng nhập. Để tìm hiểu thêm, hãy xem tài liệu về Đường liên kết trong ứng dụng Android.

Định cấu hình Firebase Hosting

Firebase Authentication sử dụng các miền Firebase Hosting khi tạo và gửi một đường liên kết được dùng để mở trong ứng dụng di động. Hệ thống đã định cấu hình miền Firebase Hosting mặc định cho bạn.

  1. Định cấu hình miền Firebase Hosting:

    Trong bảng điều khiển Firebase, hãy mở phần Hosting (Lưu trữ).

    • Nếu bạn muốn sử dụng miền mặc định cho đường liên kết email mở trong ứng dụng dành cho thiết bị di động, hãy chuyển đến trang web mặc định và ghi lại miền Hosting mặc định. Miền Hosting mặc định thường có dạng như sau: PROJECT_ID.firebaseapp.com.

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

    • Nếu muốn sử dụng miền tuỳ chỉnh cho đường liên kết trong email, bạn có thể đăng ký miền bằng Firebase Hosting và sử dụng miền đó cho miền của đường liên kết.

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

    Để xử lý các đường liên kết này từ ứng dụng Android, bạn cần chỉ định tên gói của ứng dụng trong phần cài đặt dự án bảng điều khiển Firebase. Ngoài ra, bạn cần cung cấp SHA-1 và SHA-256 của chứng chỉ ứng dụng.

    Nếu muốn các đường liên kết này chuyển hướng đến một hoạt động cụ thể, bạn cần phải định cấu hình bộ lọc ý định trong tệp AndroidManifest.xml. Bộ lọc ý định sẽ phát hiện các đường liên kết email của miền của bạn. Trong AndroidManifest.xml:

    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.BROWSABLE" />
      <category android:name="android.intent.category.DEFAULT" />
      <data
        android:scheme="https"
        android:host="<PROJECT_ID>.firebaseapp.com or your custom domain"
        android:pathPrefix="/__/auth/links" />
    </intent-filter>
    

    Khi người dùng mở một đường liên kết lưu trữ có đường dẫn /__/auth/links và giao thức cũng như máy chủ lưu trữ mà bạn chỉ định, ứng dụng của bạn sẽ bắt đầu hoạt động bằng bộ lọc ý định này để xử lý đường liên kết.

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

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 = "someemail@domain.com"

    // 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 = "someemail@domain.com";

    // 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ý tính năng đăng nhập bằng đường liên kết email trong một ứng dụng của Apple, hãy tham khảo hướng dẫn về các 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, hãy tham khảo Hướng dẫn về web.

Bạn cũng có thể liên kết phương thức xác thực này với một người dùng hiện có. Ví dụ: người dùng đã xác thực trước đó bằng 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 phép toán:

Kotlin

// 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());
                }
            }
        });

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

Kotlin

// 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, vì luồng này có thể kết thúc trên một thiết bị khác mà người dùng ban đầu chưa đăng nhập, nên luồng 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 truyền trong đường liên kết để cung cấp thông tin về loại thao tác và uid người dùng.

Trước đây, tính năng xác thực đường liên kết trong email dựa vào Firebase Dynamic Links. Tính năng này sẽ ngừng hoạt động vào ngày 25 tháng 8 năm 2025.

Chúng tôi đã phát hành một giải pháp thay thế trong SDK Android Firebase Authentication phiên bản 23.2.0 trở lên và Firebase BoM phiên bản 33.9.0 trở lên.

Nếu ứng dụng của bạn sử dụng các đường liên kết kiểu cũ, bạn nên di chuyển ứng dụng sang hệ thống dựa trên Firebase Hosting mới.

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

Mặc dù bạn 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ề cách bảo vệ tính nă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 xác thực (tức là tên người dùng và mật khẩu, số điện thoại hoặc thông tin về nhà cung cấp 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ể được dùng để xác định người dùng trên mọi ứng dụng trong dự án, bất kể người dùng đăng nhập như thế nào.

  • Trong ứng dụng, 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 Firebase Realtime DatabaseCloud Storage, bạn có thể lấy mã nhận dạng người dùng riêng biệt của người dùng đã đăng nhập từ biến auth và sử dụng mã nhận dạng đó để kiểm soát dữ liệu mà 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 bạn bằng 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 trình cung cấp dịch vụ xác thực với tài khoản người dùng hiện có.

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

Kotlin

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();