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

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

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

  • Quy trình đăng ký và đăng nhập ít phiền hà.
  • Giảm nguy cơ dùng lại mật khẩu trên các ứng dụng, từ đó làm giảm khả năng bảo mật thậm chí là những mật khẩu được chọn kỹ.
  • 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. Không có quyền sở hữu đối với 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ã mật khẩu. Mật khẩu này có thể rườm rà trên thiết bị di động.
  • Một 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ụ: một người dùng quên mật khẩu của mình vẫn có thể đăng nhập mà không cần đặt lại mật khẩu của họ.

Trước khi bắt đầu

Sao chép đoạn mã khởi chạy từ bảng điều khiển của Firebase cho dự án của bạn theo mô tả trong Thêm Firebase vào dự án JavaScript của bạn.

Để đăng nhập cho người dùng bằng đường liên kết qua 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 của bạn:

  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. Ghi chú 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 qua đường liên kết email.
  3. Cũng trong phần này, hãy bật tính năng Đăng nhập bằng đườ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 trình bày 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 cho Firebase hướng dẫn về cách tạo 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 khác cần truyền. Bạn phải thêm miền của liên kết vào danh sách Bảng điều khiển của Firebase các miền được uỷ quyền. Bạn có thể tìm thấy những miền này bằng cách chuyển đến thẻ Phương thức đăng nhập (Xác thực -> Cài đặt).
    • androidios: Các ứng dụng sẽ dùng khi đường liên kết đăng nhập được mở trên Thiết bị Android hoặc Apple. Tìm hiểu thêm về cách định cấu hình Liên kết động của Firebase để mở đường liên kết hành động qua email thông qua ứng dụng dành cho thiết bị 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 hành động email khác ngoài nhóm (mật khẩu đặt lại và xác minh email). Điều này là do, ở cuối luồng, người dùng cần đă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 có nhiều miền đường liên kết động tuỳ chỉnh được xác định đối với một dự án, chỉ định sử dụng dự án nào khi liên kết được mở qua một ứng dụng di động được chỉ định (ví dụ: example.page.link). Nếu không, miền đầu tiên được chọn tự động.

      Web

      const actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

      Web

      var actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

    Để tìm hiểu thêm về ActionCodeSettings, hãy tham khảo 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 đó khi người dùng hoàn tất quá trình đăng nhập email trên cùng một thiết bị.

    Web

    import { getAuth, sendSignInLinkToEmail } from "firebase/auth";
    
    const auth = getAuth();
    sendSignInLinkToEmail(auth, email, actionCodeSettings)
      .then(() => {
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        const errorCode = error.code;
        const errorMessage = error.message;
        // ...
      });

    Web

    firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
      .then(() => {
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        var errorCode = error.code;
        var errorMessage = error.message;
        // ...
      });

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

Để ngăn hành vi sử dụng đường liên kết đăng nhập để đăng nhập với tư cách người dùng ngoài ý muốn hoặc bật một thiết bị ngoài ý muốn, tính năng Xác thực Firebase yêu cầu địa chỉ email của người dùng phải khi hoàn tất quy trình đăng nhập. Để đăng nhập thành công, email này phải khớp với địa chỉ được dùng để gửi đườ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 trang thiết bị 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 tính - cho bằng localStorage hoặc cookie - khi bạn gửi email đăng nhập. Sau đó: sử dụng địa chỉ này để hoàn tất quy trình. Không chuyển địa chỉ email của người dùng vào các tham số URL chuyển hướng và sử dụng lại dưới dạng thao tác này có thể cho phép chèn phiên.

Sau khi hoàn tất đă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ó đều sẽ mất hiệu lực. Ví dụ: nếu trước đây ai đó đã tạo một tài khoản chưa được xác minh cho 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 đó từ đăng nhập lại bằng email và mật khẩu chưa được xác minh.

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

Hoàn tất quy trình đăng nhập trên một trang web

Định dạng của đường liên kết sâu đến đường liên kết qua email giống với định dạng dùng cho các thao tác gửi email ngoài nhóm (xác minh email, đặt lại mật khẩu và thu hồi thay đổi email). Tính năng Xác thực Firebase đơn giản hoá quy trình kiểm tra này bằng cách cung cấp API isSignInWithEmailLink để kiểm tra xem một đường liên kết có phải là yêu cầu đăng nhập bằng đường liên kết qua email hay không.

Để hoàn tất quá trình đăng nhập trên trang đích, hãy gọi signInWithEmailLink bằng email của người dùng và đường liên kết thực tế trong email chứa mã dùng một lần.

Web

import { getAuth, isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth";

// Confirm the link is a sign-in with email link.
const auth = getAuth();
if (isSignInWithEmailLink(auth, window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  let email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  signInWithEmailLink(auth, email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user by importing getAdditionalUserInfo
      // and calling it with result:
      // getAdditionalUserInfo(result)
      // You can access the user's profile via:
      // getAdditionalUserInfo(result)?.profile
      // You can check if the user is new or existing:
      // getAdditionalUserInfo(result)?.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Web

// Confirm the link is a sign-in with email link.
if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  var email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  firebase.auth().signInWithEmailLink(email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Hoàn tất đăng nhập trong ứng dụng dành cho thiết bị di động

Tính năng Xác thực Firebase sử dụng Liên kết động của Firebase để gửi đường liên kết email đến thiết bị di động của bạn. Để hoàn tất quá trình đăng nhập qua ứng dụng dành cho thiết bị di động, ứng dụng phải được định cấu hình để phát hiện liên kết ứng dụng sắp tới, hãy 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 thông qua luồng web.

Để tìm hiểu thêm về cách xử lý quy trình đăng nhập bằng đường liên kết email trên thiết bị Android hãy tham khảo hướng dẫn dành cho Android.

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

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 đã 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 mình.

Sự chênh lệch sẽ nằm trong nửa sau của thao tác:

Web

import { getAuth, linkWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
const auth = getAuth();
linkWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
firebase.auth().currentUser.linkWithCredential(credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Có thể sử dụng công cụ này để xác thực lại người dùng liên kết email trước khi chạy hoạt động nhạy cảm.

Web

import { getAuth, reauthenticateWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
const auth = getAuth();
reauthenticateWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
firebase.auth().currentUser.reauthenticateWithCredential(credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

Tuy nhiên, vì luồng có thể kết thúc trên một thiết bị khác nơi người dùng ban đầu chưa đăng nhập, thì quy trình này có thể chưa hoàn tất. Trong trường hợp đó, lỗi có thể hiển thị với người dùng để buộc họ mở đường liên kết trên cùng một thiết bị. Hơi nhiều có thể được chuyển trạng thái trong 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, hãy liệt kê email tính năng bảo vệ được bật theo mặc định. Tính năng này cải thiện độ bảo mật của tài khoản người dùng của dự án, nhưng chế độ này sẽ vô hiệu hoá fetchSignInMethodsForEmail() mà trước đây chúng tôi khuyên dùng để triển khai quy trình ư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 chúng tôi không nên làm như vậy.

Xem tài liệu về biện pháp bảo vệ liệt kê email để biết thêm chi tiết.

Mẫu email mặc định để đăng nhập bằng đường liên kết

Mẫu email mặc định có dấu thời gian trong tiêu đề và nội dung email để các email tiếp theo không được thu gọn thành một chuỗi bằng đường liên kết bị ẩn.

Mẫu này áp dụng cho các ngôn ngữ sau:

Ngôn ngữ
ar Tiếng Ả Rập
zh-CN Tiếng Trung (Giản thể)
zh-TW Tiếng Trung (Phồn thể)
ngôn ngữ Tiếng Hà Lan
vi Tiếng Anh
en-GB Tiếng Anh (Anh)
fr Tiếng Pháp
de Tiếng Đức
id Tiếng Indonesia
it Tiếng Ý
ja Tiếng Nhật
ko Tiếng Hàn
pl Tiếng Ba Lan
pt-BR Tiếng Bồ Đào Nha (Brazil)
PTT Tiếng Bồ Đào Nha (Bồ Đào Nha)
ru Tiếng Nga
es Tiếng Tây Ban Nha
es-419 Tiếng Tây Ban Nha (Mỹ Latinh)
thứ Tiếng Thái

Các bước tiếp theo

Sau khi người dùng đăng nhập lần đầu tiên, một tài khoản người dùng mới sẽ được tạo và được 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 số hoặc thông tin của nhà cung cấp dịch vụ xác thực – người dùng đã đăng nhập. Thông tin mới này được lưu trữ như một phần của dự án Firebase và có thể được dùng để xác định một 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 ứng dụng của mình, bạn nên sử dụng cách để biết trạng thái xác thực của người dùng đặt trình quan sát trên đối tượng Auth. Sau đó, bạn có thể lấy thông tin thông tin hồ sơ cơ bản của đối tượng User. Xem Quản lý người dùng.

  • Trong Cơ sở dữ liệu theo thời gian thực của Firebase và Cloud Storage Quy tắc bảo mật, 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, để kiểm soát loại 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 mình bằng nhiều phương thức xác thực bằng cách liên kết thông tin đăng nhập của 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 một người dùng, hãy gọi signOut:

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});