المصادقة مع Firebase باستخدام رابط البريد الإلكتروني في JavaScript

يمكنك استخدام "مصادقة Firebase" لتسجيل دخول مستخدم عن طريق إرسال رسالة إلكترونية إليه تحتوي على رابط يمكنه النقر عليه لتسجيل الدخول. وفي هذه العملية، يتم أيضًا إثبات عنوان البريد الإلكتروني للمستخدم.

تتوفّر مزايا عديدة لتسجيل الدخول عن طريق البريد الإلكتروني:

  • تسجيل الدخول والاشتراك بسهولة
  • انخفاض خطر إعادة استخدام كلمة المرور نفسها في تطبيقات مختلفة، ما قد يؤدي إلى تقويض أمان كلمات المرور حتى إذا كانت مختارة بعناية
  • إمكانية مصادقة مستخدم مع التحقّق أيضًا من أنّه المالك الشرعي لعنوان بريد إلكتروني
  • لا يحتاج المستخدم إلا إلى حساب بريد إلكتروني يمكن الوصول إليه لتسجيل الدخول. ولا يُطلب منه امتلاك رقم هاتف أو حساب على وسائل التواصل الاجتماعي.
  • يمكن للمستخدم تسجيل الدخول بأمان بدون الحاجة إلى تقديم كلمة مرور (أو تذكّرها)، ما قد يكون أمرًا مزعجًا على جهاز جوّال.
  • يمكن ترقية مستخدم حالي سبق له تسجيل الدخول باستخدام معرّف بريد إلكتروني (كلمة مرور أو حساب موحّد) لتسجيل الدخول باستخدام البريد الإلكتروني فقط. على سبيل المثال، يمكن للمستخدم الذي نسي كلمة المرور تسجيل الدخول بدون الحاجة إلى إعادة ضبطها.

قبل البدء

إذا لم يسبق لك إجراء ذلك، انسخ مقتطف التهيئة من الـ Firebase console إلى مشروعك كما هو موضّح في إضافة Firebase إلى مشروع JavaScript.

لتسجيل دخول المستخدمين باستخدام رابط البريد الإلكتروني، يجب أولاً تفعيل موفّر خدمة البريد الإلكتروني وطريقة تسجيل الدخول باستخدام رابط البريد الإلكتروني لمشروعك على Firebase:

  1. في وحدة التحكّم Firebase، انتقِل إلى الأمان > المصادقة.

  2. في علامة التبويب طريقة تسجيل الدخول ، فعِّل طريقة تسجيل الدخول البريد الإلكتروني/كلمة المرور. يُرجى العِلم أنّه يجب تفعيل تسجيل الدخول باستخدام البريد الإلكتروني/كلمة المرور لاستخدام تسجيل الدخول باستخدام رابط البريد الإلكتروني.

  3. في القسم نفسه، فعِّل موفّر تسجيل الدخول رابط البريد الإلكتروني (تسجيل الدخول بدون كلمة مرور).

  4. انقر على حفظ.

لبدء عملية المصادقة، اعرض على المستخدم واجهة تطلب منه تقديم عنوان بريده الإلكتروني، ثم استدعِ الدالة sendSignInLinkToEmail لطلب إرسال رابط المصادقة إلى عنوان البريد الإلكتروني للمستخدم من Firebase.

  1. أنشئ الكائن ActionCodeSettings الذي يزوّد Firebase بتعليمات حول كيفية إنشاء رابط البريد الإلكتروني. اضبط الحقول التالية:

    • url: الرابط لصفحة معيّنة في التطبيق الذي سيتم تضمينه وأي حالة إضافية سيتم تمريرها. أضِف نطاقك إلى قائمة النطاقات المفوّضة إذا لم يسبق لك إجراء ذلك:

      1. في وحدة التحكّم Firebase، انتقِل إلى علامة التبويب الأمان > المصادقة > الإعدادات.

      2. في قسم النطاقات المفوّضة ، انقر على إضافة نطاق وأضِف نطاقك.

    • android وios: يساعدان Firebase Authentication في تحديد ما إذا كان يجب إنشاء رابط للويب فقط أو رابط للأجهزة الجوّالة يتم فتحه على جهاز Android أو جهاز Apple.
    • handleCodeInApp: اضبط القيمة على "صحيح". يجب دائمًا إكمال عملية تسجيل الدخول في التطبيق على عكس الإجراءات الأخرى التي يتم اتّخاذها خارج نطاق البريد الإلكتروني (إعادة ضبط كلمة المرور وعمليات إثبات ملكية البريد الإلكتروني). ويرجع ذلك إلى أنّه من المتوقّع أن يكون المستخدم مسجّلاً الدخول في نهاية العملية وأن تظل حالة المصادقة محفوظة في التطبيق.
    • linkDomain: عند تحديد نطاقات روابط مخصّصة Hosting لمشروع، حدِّد النطاق الذي سيتم استخدامه عند فتح الرابط من خلال تطبيق جوّال محدّد. وإلا، سيتم اختيار النطاق التلقائي تلقائيًا (على سبيل المثال، PROJECT_ID.firebaseapp.com).
    • dynamicLinkDomain: تم إيقاف هذه المَعلمة نهائيًا. لا تحدِّد هذه المَعلمة.

      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'
        },
        // The domain must be configured in Firebase Hosting and owned by the project.
        linkDomain: 'custom-domain.com'
      };

      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'
      };

    لمزيد من المعلومات عن ActionCodeSettings، يُرجى الرجوع إلى قسم تمرير الحالة في إجراءات البريد الإلكتروني.

  2. اطلب من المستخدم عنوان بريده الإلكتروني.

  3. أرسِل رابط المصادقة إلى عنوان البريد الإلكتروني للمستخدم، واحفظ عنوان البريد الإلكتروني للمستخدم في حال أكمل تسجيل الدخول باستخدام البريد الإلكتروني على الجهاز نفسه.

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

المخاوف المرتبطة بالأمان

لمنع استخدام رابط تسجيل الدخول لتسجيل الدخول كمستخدم غير مقصود أو على جهاز غير مقصود، تطلب "مصادقة Firebase" تقديم عنوان البريد الإلكتروني للمستخدم عند إكمال عملية تسجيل الدخول. ولكي تنجح عملية تسجيل الدخول، يجب أن يتطابق عنوان البريد الإلكتروني هذا مع العنوان الذي تم إرسال رابط تسجيل الدخول إليه في الأصل.

يمكنك تبسيط هذه العملية للمستخدمين الذين يفتحون رابط تسجيل الدخول على الجهاز نفسه الذي يطلبون الرابط عليه، وذلك من خلال تخزين عنوان بريدهم الإلكتروني محليًا، مثلاً باستخدام localStorage أو ملفات تعريف الارتباط، عند إرسال رسالة تسجيل الدخول الإلكترونية. بعد ذلك، استخدِم هذا العنوان لإكمال العملية. لا تمرِّر عنوان البريد الإلكتروني للمستخدم في مَعلمات عنوان URL لإعادة التوجيه وأعِد استخدامه، لأنّ ذلك قد يتيح عمليات إدخال الجلسات.

بعد إكمال تسجيل الدخول، ستتم إزالة أي آلية تسجيل دخول سابقة لم يتم إثباتها من المستخدم وسيتم إبطال أي جلسات حالية. على سبيل المثال، إذا سبق لأحد المستخدمين إنشاء حساب لم يتم إثباته باستخدام عنوان البريد الإلكتروني وكلمة المرور نفسَيهما، ستتم إزالة كلمة مرور المستخدم لمنع المنتحل الذي ادّعى ملكية هذا الحساب الذي لم يتم إثباته وأنشأه من تسجيل الدخول مرة أخرى باستخدام عنوان البريد الإلكتروني وكلمة المرور اللذين لم يتم إثباتهما.

تأكَّد أيضًا من استخدام عنوان URL لبروتوكول HTTPS في بيئة الإنتاج لتجنُّب اعتراض الخوادم الوسيطة المحتمل لرابطك.

إكمال تسجيل الدخول في صفحة ويب

يكون تنسيق الرابط لصفحة معيّنة في التطبيق في رابط البريد الإلكتروني هو نفسه التنسيق المستخدَم للإجراءات التي يتم اتّخاذها خارج نطاق البريد الإلكتروني (إثبات ملكية البريد الإلكتروني وإعادة ضبط كلمة المرور وإبطال تغيير البريد الإلكتروني). تُبسّط "مصادقة Firebase" عملية التحقّق هذه من خلال توفير واجهة برمجة التطبيقات isSignInWithEmailLink للتحقّق مما إذا كان الرابط هو رابط تسجيل الدخول باستخدام البريد الإلكتروني.

لإكمال تسجيل الدخول على الصفحة المقصودة، استدعِ الدالة signInWithEmailLink باستخدام عنوان البريد الإلكتروني للمستخدم ورابط البريد الإلكتروني الفعلي الذي يحتوي على الرمز لمرة واحدة.

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.
    });
}

إكمال تسجيل الدخول في تطبيق جوّال

Firebase Authentication تستخدم Firebase Hosting لإرسال رابط البريد الإلكتروني إلى جهاز جوّال. لإكمال تسجيل الدخول من خلال تطبيق جوّال، يجب ضبط التطبيق على رصد رابط التطبيق الوارد وتحليل الرابط لصفحة معيّنة في التطبيق الأساسي، ثم إكمال تسجيل الدخول كما يتم من خلال عملية الويب.

لمزيد من المعلومات عن كيفية التعامل مع تسجيل الدخول باستخدام رابط البريد الإلكتروني في تطبيق Android ، يُرجى الرجوع إلى دليل Android.

لمزيد من المعلومات عن كيفية التعامل مع تسجيل الدخول باستخدام رابط البريد الإلكتروني في تطبيق Apple ، يُرجى الرجوع إلى دليل منصات Apple.

يمكنك أيضًا ربط طريقة المصادقة هذه بمستخدم حالي. على سبيل المثال، يمكن لمستخدم سبق له المصادقة باستخدام موفّر خدمة آخر، مثل رقم هاتف، إضافة طريقة تسجيل الدخول هذه إلى حسابه الحالي.

سيختلف النصف الثاني من العملية:

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.
  });

يمكن أيضًا استخدام هذه الطريقة لإعادة مصادقة مستخدم رابط البريد الإلكتروني قبل تشغيل عملية حساسة.

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.
  });

ومع ذلك، بما أنّ العملية قد تنتهي على جهاز مختلف لم يكن المستخدم الأصلي مسجّلاً الدخول إليه، قد لا يتم إكمال هذه العملية. في هذه الحالة، يمكن عرض رسالة خطأ للمستخدم لإجباره على فتح الرابط على الجهاز نفسه. يمكن تمرير بعض الحالات في الرابط لتوفير معلومات عن نوع العملية ورقم تعريف المستخدم.

إذا أنشأت مشروعك في 15 سبتمبر 2023 أو بعد هذا التاريخ، تكون ميزة الحماية من تعداد عناوين البريد الإلكتروني مفعّلة تلقائيًا. تُحسِّن هذه الميزة أمان حسابات المستخدمين في مشروعك، ولكنّها توقف طريقة fetchSignInMethodsForEmail() التي سبق أن نصحنا بتنفيذ عمليات تسجيل الدخول التي تبدأ بالمعرّف.

على الرغم من أنّه يمكنك إيقاف ميزة الحماية من تعداد عناوين البريد الإلكتروني لمشروعك، ننصحك بعدم إجراء ذلك.

لمزيد من التفاصيل، يُرجى الاطّلاع على مستندات الحماية من تعداد عناوين البريد الإلكتروني .

نموذج البريد الإلكتروني التلقائي لتسجيل الدخول باستخدام رابط

يتضمّن نموذج البريد الإلكتروني التلقائي طابعًا زمنيًا في الموضوع ونص الرسالة الإلكترونية حتى لا يتم تصغير الرسائل الإلكترونية اللاحقة في سلسلة محادثات واحدة، ما يؤدي إلى إخفاء الرابط.

ينطبق هذا النموذج على اللغات التالية:

الرمز اللغة
ar العربية
zh-CN الصينية (المبسّطة)
zh-TW الصينية (التقليدية)
nl الهولندية
en الإنجليزية
en-GB الإنجليزية (المملكة المتحدة)
fr الفرنسية
de الألمانية
id الإندونيسية
it الإيطالية
ja اليابانية
ko الكورية
pl البولندية
pt-BR البرتغالية (البرازيل)
pt-PT البرتغالية (البرتغال)
ru الروسية
es الإسبانية
es-419 الإسبانية (أمريكا اللاتينية)
th التايلاندية

الخطوات التالية

بعد أن يسجّل المستخدم الدخول للمرة الأولى، يتم إنشاء حساب مستخدم جديد وربطه ببيانات الاعتماد، أي اسم المستخدم وكلمة المرور أو رقم الهاتف أو معلومات موفّر المصادقة، التي سجّل المستخدم الدخول بها. يتم تخزين هذا الحساب الجديد كجزء من مشروع Firebase، ويمكن استخدامه لتحديد مستخدم في كل تطبيق في مشروعك، بغض النظر عن طريقة تسجيل الدخول.

  • في تطبيقاتك، الطريقة المقترَحة لمعرفة حالة المصادقة للمستخدم هي ضبط مراقب على الكائن Auth. بعد ذلك، يمكنك الحصول على معلومات الملف الشخصي الأساسية للمستخدم من الكائن User. راجِع مقالة إدارة المستخدمين.

  • في Firebase Realtime Database وCloud Storage قواعد الأمان، يمكنك الحصول على رقم تعريف المستخدم الفريد للمستخدم الذي سجّل الدخول من المتغيّر auth، واستخدامه للتحكّم في البيانات التي يمكن للمستخدم الوصول إليها.

يمكنك السماح للمستخدمين بتسجيل الدخول إلى تطبيقك باستخدام عدّة موفّري مصادقة من خلال ربط بيانات اعتماد موفّر المصادقة بحساب مستخدم حالي.

لتسجيل خروج مستخدم، استدعِ الدالة 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.
});