اصالت‌سنجی با Firebase بااستفاده از پیوند ایمیل در جاوا اسکریپت

می‌توانید از «اصالت‌سنجی Firebase» برای وارد کردن کاربر به سیستم با ارسال ایمیلی حاوی پیوند استفاده کنید که کاربر می‌تواند روی آن کلیک کند تا وارد سیستم شود. در این فرایند، نشانی ایمیل کاربر نیز درستی‌سنجی می‌شود.

مزایای متعددی برای ورود به سیستم ازطریق ایمیل وجود دارد:

  • ثبت‌نام و ورود به سیستم بدون دردسر.
  • خطر استفاده مجدد از گذرواژه در برنامه‌ها کاهش می‌یابد، که می‌تواند امنیت حتی گذرواژه‌های خوب انتخاب‌شده را تضعیف کند.
  • توانایی اصالت‌سنجی کاربر و درعین‌حال درستی‌سنجی اینکه کاربر مالک قانونی نشانی ایمیل است.
  • کاربر فقط به یک حساب ایمیل دردسترس برای ورود به سیستم نیاز دارد. مالکیت شماره تلفن یا حساب رسانه‌های اجتماعی لازم نیست.
  • کاربر می‌تواند بدون نیاز به ارائه (یا به‌خاطر سپردن) گذرواژه، که در دستگاه همراه می‌تواند دردسرساز باشد، به‌طور ایمن به سیستم وارد شود.
  • کاربر موجودی که قبلاً با شناسه ایمیل (گذرواژه یا فدرال) به سیستم وارد شده است می‌تواند به ورود به سیستم فقط با ایمیل ارتقا یابد. برای مثال، کاربری که گذرواژه‌اش را فراموش کرده است همچنان می‌تواند بدون نیاز به بازنشانی گذرواژه به سیستم وارد شود.

قبل از شروع

اگر قبلاً این کار را نکرده‌اید، تکه‌کد مقداردهی اولیه را از کنسول Firebase به پروژه خود کپی کنید، همان‌طور که در افزودن Firebase به پروژه جاوا اسکریپت توضیح داده شده است.

برای ورود به سیستم کاربران ازطریق پیوند ایمیل، ابتدا باید «ارائه‌دهنده ایمیل» و روش ورود به سیستم «پیوند ایمیل» را برای پروژه 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 Auth نیاز دارد که نشانی ایمیل کاربر هنگام تکمیل جریان ورود به سیستم ارائه شود. برای اینکه ورود به سیستم موفقیت‌آمیز باشد، این نشانی ایمیل باید با نشانی‌ای که پیوند ورود به سیستم در ابتدا به آن ارسال شده است مطابقت داشته باشد.

می‌توانید این جریان را برای کاربرانی که پیوند ورود به سیستم را در همان دستگاهی که پیوند را درخواست کرده‌اند باز می‌کنند ساده‌تر کنید. برای این کار، هنگام ارسال ایمیل ورود به سیستم، نشانی ایمیل آن‌ها را به‌صورت محلی ذخیره کنید (برای نمونه بااستفاده از localStorage یا کوکی‌ها). سپس، از این نشانی برای تکمیل جریان استفاده کنید. نشانی ایمیل کاربر را در پارامترهای نشانی وب هدایتگر ارسال نکنید و از آن مجدداً استفاده نکنید زیرا این کار ممکن است تزریق جلسه را فعال کند.

پس‌از تکمیل ورود به سیستم، هر سازوکار تأییدنشده قبلی برای ورود به سیستم از کاربر برداشته خواهد شد و همه جلسه‌های موجود نامعتبر خواهند شد. برای مثال، اگر فردی قبلاً حسابی تأییدنشده با همان ایمیل و گذرواژه ایجاد کرده باشد، گذرواژه کاربر برداشته خواهد شد تا از ورود مجدد فرد جعل‌کننده‌ای که ادعای مالکیت کرده و آن حساب تأییدنشده را ایجاد کرده است با ایمیل و گذرواژه تأییدنشده جلوگیری شود.

همچنین مطمئن شوید که در مرحله تولید از نشانی وب HTTPS استفاده می‌کنید تا پیوندتان به‌طور بالقوه توسط سرورهای واسطه رهگیری نشود.

تکمیل کردن ورود به سیستم در صفحه وب

قالب پیوند عمیق پیوند ایمیل همانند قالب استفاده‌شده برای کنش‌های ایمیل خارج از باند (درستی‌سنجی ایمیل، بازنشانی گذرواژه، و ابطال تغییر ایمیل) است. «احراز هویت Firebase» با ارائه isSignInWithEmailLink API برای بررسی اینکه آیا پیوند، پیوند ورود به سیستم با ایمیل است یا نه، این بررسی را ساده می‌کند.

برای تکمیل ورود به سیستم در صفحه مقصد، با ایمیل کاربر و پیوند ایمیل واقعی حاوی کد یک‌بارمصرف با 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.
  });

بااین‌حال، ازآنجایی‌که ممکن است جریان در دستگاه دیگری که کاربر اصلی در آن وارد نشده است پایان یابد، این جریان ممکن است تکمیل نشود. در این صورت، می‌توان خطایی به کاربر نشان داد تا او را مجبور کند پیوند را در همان دستگاه باز کند. برخی‌از وضعیت می‌تواند در پیوند منتقل شود تا اطلاعاتی درباره نوع عملیات و شناسه کاربر ارائه دهد.

اگر پروژه خود را در تاریخ ۱۵ سپتامبر ۲۰۲۳ یا پس‌از آن ایجاد کرده‌اید، محافظت از شمارش ایمیل به‌طور پیش‌فرض فعال است. این ویژگی امنیت حساب‌های کاربری پروژه شما را بهبود می‌بخشد، اما روش fetchSignInMethodsForEmail() را که قبلاً برای پیاده‌سازی جریان‌های شناسه-اول توصیه می‌کردیم غیرفعال می‌کند.

اگرچه می‌توانید محافظت از شمارش ایمیل را برای پروژه‌تان غیرفعال کنید، اما توصیه می‌کنیم این کار را انجام ندهید.

برای جزئیات بیشتر، مستندات مربوط به محافظت از شمارش ایمیل را ببینید.

الگوی ایمیل پیش‌فرض برای ورود به سیستم با پیوند

الگوی ایمیل پیش‌فرض شامل مُهر زمان در موضوع و بدنه ایمیل است تا ایمیل‌های بعدی در یک رشته جمع نشوند و پیوند پنهان نشود.

این الگو برای زبان‌های زیر اعمال می‌شود:

کد زبان
ar عربی
zh-CN Chinese (Simplified)
zh-TW Chinese (Traditional)
nl هلندی
fa انگلیسی
en-GB English (UK)
fr فرانسوی
de آلمانی
id اندونزیایی
it ایتالیایی
ja ژاپنی
ko کره‌ای
pl لهستانی
pt-BR پرتغالی (برزیل)
pt-PT پرتغالی (پرتغال)
ru روسی
es اسپانیایی
es-419 Spanish (Latin America)
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.
});