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

شما می‌توانید از احراز هویت فایربیس برای ورود کاربر استفاده کنید، به این صورت که ایمیلی حاوی یک لینک برای او ارسال می‌کنید که می‌تواند روی آن کلیک کند تا وارد سیستم شود. در این فرآیند، آدرس ایمیل کاربر نیز تأیید می‌شود.

مزایای بی‌شماری برای ورود از طریق ایمیل وجود دارد:

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

قبل از اینکه شروع کنی

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

برای ورود کاربران از طریق لینک ایمیل، ابتدا باید متد ورود به سیستم ارائه دهنده ایمیل و لینک ایمیل را برای پروژه Firebase خود فعال کنید:

  1. در کنسول Firebase ، به بخش امنیت > احراز هویت بروید.

  2. در تب روش ورود ، روش ورود با ایمیل/رمز عبور را فعال کنید. توجه داشته باشید که برای استفاده از ورود با لینک ایمیل، باید ورود با ایمیل/رمز عبور فعال باشد.

  3. در همان بخش، گزینه‌ی «ایمیل لینک (ورود بدون رمز عبور)» را برای ورود به سیستم فعال کنید.

  4. روی ذخیره کلیک کنید.

برای شروع جریان احراز هویت، رابطی را به کاربر ارائه دهید که از او می‌خواهد آدرس ایمیل خود را ارائه دهد و سپس تابع sendSignInLinkToEmail را فراخوانی کنید تا از Firebase بخواهید لینک احراز هویت را به ایمیل کاربر ارسال کند.

  1. شیء ActionCodeSettings را بسازید، که دستورالعمل‌هایی در مورد نحوه ساخت لینک ایمیل در Firebase ارائه می‌دهد. فیلدهای زیر را تنظیم کنید:

    • url : لینک عمیق برای جاسازی و هر وضعیت اضافی که باید منتقل شود. اگر قبلاً این کار را نکرده‌اید، دامنه خود را به لیست دامنه‌های مجاز اضافه کنید:

      1. در کنسول Firebase ، به تب Security > Authentication > Settings بروید.

      2. در بخش دامنه‌های مجاز ، روی افزودن دامنه کلیک کنید و دامنه خود را اضافه کنید.

    • android و ios : به Firebase Authentication کمک می‌کند تا تعیین کند که آیا باید یک لینک فقط وب یا موبایل ایجاد کند که روی دستگاه اندروید یا اپل باز شود.
    • handleCodeInApp : روی true تنظیم شده است. عملیات ورود به سیستم برخلاف سایر اقدامات ایمیل خارج از باند (تنظیم مجدد رمز عبور و تأیید ایمیل) باید همیشه در برنامه انجام شود. دلیل این امر این است که در پایان جریان، انتظار می‌رود کاربر وارد سیستم شده باشد و وضعیت احراز هویت او در برنامه باقی بماند.
    • 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 یا کوکی‌ها - هنگام ارسال ایمیل ورود، ساده کنید. سپس، از این آدرس برای تکمیل جریان استفاده کنید. ایمیل کاربر را در پارامترهای URL تغییر مسیر قرار ندهید و دوباره از آن استفاده نکنید زیرا این کار ممکن است تزریق جلسه را فعال کند.

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

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

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

قالب لینک عمیق لینک ایمیل همان قالبی است که برای اقدامات ایمیل خارج از باند (تأیید ایمیل، تنظیم مجدد رمز عبور و لغو تغییر ایمیل) استفاده می‌شود . Firebase Auth با ارائه API 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 برای ارسال لینک ایمیل به دستگاه تلفن همراه استفاده می‌کند. برای تکمیل ورود به سیستم از طریق برنامه تلفن همراه، برنامه باید طوری پیکربندی شود که لینک برنامه ورودی را شناسایی کند، لینک عمیق زیرین را تجزیه کند و سپس ورود به سیستم را همانطور که از طریق جریان وب انجام می‌شود، تکمیل کند.

برای کسب اطلاعات بیشتر در مورد نحوه مدیریت ورود به سیستم با لینک ایمیل در یک برنامه اندروید، به راهنمای اندروید مراجعه کنید.

برای کسب اطلاعات بیشتر در مورد نحوه مدیریت ورود به سیستم با لینک ایمیل در یک برنامه اپل، به راهنمای پلتفرم‌های اپل مراجعه کنید.

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

تفاوت در نیمه دوم عملیات خواهد بود:

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() را غیرفعال می‌کند، که قبلاً برای پیاده‌سازی جریان‌های شناسه-اول توصیه می‌کردیم.

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

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

قالب ایمیل پیش‌فرض برای ورود با لینک

قالب ایمیل پیش‌فرض شامل یک مهر زمانی در موضوع و بدنه ایمیل است تا ایمیل‌های بعدی در یک رشته ایمیل واحد ادغام نشوند و لینک پنهان نشود .

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

کد زبان
آر عربی
zh-CN چینی (ساده‌شده)
zh-TW چینی (سنتی)
ان ال هلندی
انگلیسی انگلیسی
انگلیسی-بریتانیایی انگلیسی (بریتانیا)
فر فرانسوی
د آلمانی
شناسه اندونزیایی
آن ایتالیایی
جا ژاپنی
کو کره ای
پل لهستانی
پی تی-بی آر پرتغالی (برزیل)
پی تی-پی تی پرتغالی (پرتغال)
انگشت های دست فاصله روسی
ها اسپانیایی
es-419 اسپانیایی (آمریکای لاتین)
هفتم تایلندی

مراحل بعدی

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

  • در برنامه‌های شما، روش توصیه‌شده برای اطلاع از وضعیت احراز هویت کاربر، تنظیم یک ناظر (observer) روی شیء Auth است. سپس می‌توانید اطلاعات اولیه پروفایل کاربر را از شیء User ) دریافت کنید. به بخش مدیریت کاربران (Manage Users) مراجعه کنید.

  • در قوانین امنیتی پایگاه داده و Cloud Storage Firebase Realtime Database ، می‌توانید شناسه کاربری منحصر به فرد کاربر وارد شده را از متغیر 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.
});