با Firebase در یک برنامه افزودنی Chrome احراز هویت

این سند به شما نشان می‌دهد که چگونه از Firebase Authentication برای وارد کردن کاربران به یک برنامه افزودنی Chrome استفاده کنید که از Manifest V3 استفاده می‌کند.

Firebase Authentication چندین روش احراز هویت را برای ورود کاربران از طریق یک برنامه افزودنی Chrome ارائه می‌کند، که برخی از آنها به تلاش بیشتری نسبت به سایرین نیاز دارند.

برای استفاده از روش‌های زیر در افزونه Manifest V3 Chrome، فقط باید آنها را از firebase/auth/web-extension وارد کنید :

  • با ایمیل و رمز عبور وارد شوید ( createUserWithEmailAndPassword و signInWithEmailAndPassword )
  • با پیوند ایمیل وارد شوید ( sendSignInLinkToEmail ، isSignInWithEmailLink و signInWithEmailLink )
  • به صورت ناشناس وارد شوید ( signInAnonymously )
  • با یک سیستم احراز هویت سفارشی وارد شوید ( signInWithCustomToken )
  • ورود ارائه دهنده را به طور مستقل مدیریت کنید سپس از signInWithCredential استفاده کنید

روش‌های ورود به سیستم زیر نیز پشتیبانی می‌شوند اما نیاز به کار اضافی دارند:

  • با یک پنجره پاپ آپ وارد شوید ( signInWithPopup ، linkWithPopup ، و reauthenticateWithPopup )
  • با تغییر مسیر به صفحه ورود به سیستم وارد شوید ( signInWithRedirect ، linkWithRedirect و reauthenticateWithRedirect )
  • با reCAPTCHA با شماره تلفن وارد شوید
  • احراز هویت چند عاملی پیامکی با reCAPTCHA
  • حفاظت از سازمان reCAPTCHA

برای استفاده از این روش‌ها در افزونه Manifest V3 Chrome، باید از اسناد خارج از صفحه استفاده کنید.

از نقطه ورود firebase/auth/web-extension استفاده کنید

وارد کردن از firebase/auth/web-extension باعث می‌شود که کاربران از یک برنامه افزودنی Chrome مشابه یک برنامه وب به سیستم وارد شوند.

firebase/auth/web-extension فقط در نسخه‌های Web SDK نسخه 10.8.0 و بالاتر پشتیبانی می‌شود.

import { getAuth, signInWithEmailAndPassword } from 'firebase/auth/web-extension';

const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
  });

از اسناد خارج از صفحه استفاده کنید

برخی از روش‌های احراز هویت، مانند signInWithPopup ، linkWithPopup ، و reauthenticateWithPopup ، مستقیماً با برنامه‌های افزودنی Chrome سازگار نیستند، زیرا برای بارگیری کد از خارج از بسته برنامه افزودنی نیاز دارند. با شروع در Manifest V3، این کار مجاز نیست و توسط پلتفرم افزونه مسدود خواهد شد. برای دور زدن این موضوع، می‌توانید با استفاده از یک سند خارج از صفحه ، آن کد را در یک iframe بارگیری کنید. در سند خارج از صفحه، جریان عادی احراز هویت را اجرا کنید و نتیجه را از سند خارج از صفحه به برنامه افزودنی بازگردانید.

این راهنما از signInWithPopup به عنوان مثال استفاده می کند، اما همین مفهوم در مورد سایر روش های احراز هویت نیز صدق می کند.

قبل از شروع

این تکنیک از شما می‌خواهد یک صفحه وب را راه‌اندازی کنید که در وب در دسترس است و در iframe بارگذاری می‌کنید. هر میزبانی برای این کار کار می کند، از جمله میزبانی Firebase . یک وب سایت با محتوای زیر ایجاد کنید:

<!DOCTYPE html>
<html>
  <head>
    <title>signInWithPopup</title>
    <script src="signInWithPopup.js"></script>
  </head>
  <body><h1>signInWithPopup</h1></body>
</html>

ورود به سیستم فدرال

اگر از ورود به سیستم فدرال استفاده می‌کنید، مانند ورود با Google، Apple، SAML، یا OIDC، باید شناسه برنامه افزودنی Chrome خود را به فهرست دامنه‌های مجاز اضافه کنید:

  1. پروژه خود را در کنسول Firebase باز کنید.
  2. در بخش Authentication صفحه تنظیمات را باز کنید.
  3. یک URI مانند زیر به لیست دامنه های مجاز اضافه کنید:
    chrome-extension://CHROME_EXTENSION_ID

در فایل مانیفست افزونه Chrome خود مطمئن شوید که URL های زیر را به لیست مجاز content_security_policy اضافه کرده اید:

  • https://apis.google.com
  • https://www.gstatic.com
  • https://www.googleapis.com
  • https://securetoken.googleapis.com

اجرای احراز هویت

در سند HTML شما، signInWithPopup.js کد جاوا اسکریپت است که احراز هویت را مدیریت می کند. دو روش مختلف برای پیاده سازی روشی وجود دارد که مستقیماً در افزونه پشتیبانی می شود:

  • از firebase/auth به جای firebase/auth/web-extension استفاده کنید. نقطه ورود web-extension برای کدهای در حال اجرا در برنامه افزودنی است. در حالی که این کد در نهایت در برنامه افزودنی اجرا می شود (در iframe، در سند خارج از صفحه شما)، زمینه ای که در آن اجرا می شود، وب استاندارد است.
  • منطق احراز هویت را در شنونده postMessage بپیچید تا درخواست و پاسخ احراز هویت را پراکسی کنید.
import { signInWithPopup, GoogleAuthProvider, getAuth } from'firebase/auth';
import { initializeApp } from 'firebase/app';
import firebaseConfig from './firebaseConfig.js'

const app = initializeApp(firebaseConfig);
const auth = getAuth();

// This code runs inside of an iframe in the extension's offscreen document.
// This gives you a reference to the parent frame, i.e. the offscreen document.
// You will need this to assign the targetOrigin for postMessage.
const PARENT_FRAME = document.location.ancestorOrigins[0];

// This demo uses the Google auth provider, but any supported provider works.
// Make sure that you enable any provider you want to use in the Firebase Console.
// https://console.firebase.google.com/project/_/authentication/providers
const PROVIDER = new GoogleAuthProvider();

function sendResponse(result) {
  globalThis.parent.self.postMessage(JSON.stringify(result), PARENT_FRAME);
}

globalThis.addEventListener('message', function({data}) {
  if (data.initAuth) {
    // Opens the Google sign-in page in a popup, inside of an iframe in the
    // extension's offscreen document.
    // To centralize logic, all respones are forwarded to the parent frame,
    // which goes on to forward them to the extension's service worker.
    signInWithPopup(auth, PROVIDER)
      .then(sendResponse)
      .catch(sendResponse)
  }
});

برنامه افزودنی کروم خود را بسازید

بعد از اینکه وب سایت شما فعال شد، می توانید از آن در برنامه افزودنی Chrome خود استفاده کنید.

  1. مجوز offscreen را به فایل manifest.json خود اضافه کنید:
  2.     {
          "name": "signInWithPopup Demo",
          "manifest_version" 3,
          "background": {
            "service_worker": "background.js"
          },
          "permissions": [
            "offscreen"
          ]
        }
        
  3. خود سند خارج از صفحه را ایجاد کنید. این یک فایل حداقلی HTML در بسته برنامه افزودنی شما است که منطق جاوا اسکریپت سند خارج از صفحه شما را بارگیری می کند:
  4.     <!DOCTYPE html>
        <script src="./offscreen.js"></script>
        
  5. offscreen.js در بسته برنامه افزودنی خود قرار دهید. به عنوان پروکسی بین وب سایت عمومی راه اندازی شده در مرحله 1 و برنامه افزودنی شما عمل می کند.
  6.     // This URL must point to the public site
        const _URL = 'https://example.com/signInWithPopupExample';
        const iframe = document.createElement('iframe');
        iframe.src = _URL;
        document.documentElement.appendChild(iframe);
        chrome.runtime.onMessage.addListener(handleChromeMessages);
    
        function handleChromeMessages(message, sender, sendResponse) {
          // Extensions may have an number of other reasons to send messages, so you
          // should filter out any that are not meant for the offscreen document.
          if (message.target !== 'offscreen') {
            return false;
          }
    
          function handleIframeMessage({data}) {
            try {
              if (data.startsWith('!_{')) {
                // Other parts of the Firebase library send messages using postMessage.
                // You don't care about them in this context, so return early.
                return;
              }
              data = JSON.parse(data);
              self.removeEventListener('message', handleIframeMessage);
    
              sendResponse(data);
            } catch (e) {
              console.log(`json parse failed - ${e.message}`);
            }
          }
    
          globalThis.addEventListener('message', handleIframeMessage, false);
    
          // Initialize the authentication flow in the iframed document. You must set the
          // second argument (targetOrigin) of the message in order for it to be successfully
          // delivered.
          iframe.contentWindow.postMessage({"initAuth": true}, new URL(_URL).origin);
          return true;
        }
        
  7. سند خارج از صفحه را از کارمند سرویس background.js خود تنظیم کنید.
  8.     const OFFSCREEN_DOCUMENT_PATH = '/offscreen.html';
    
        // A global promise to avoid concurrency issues
        let creatingOffscreenDocument;
    
        // Chrome only allows for a single offscreenDocument. This is a helper function
        // that returns a boolean indicating if a document is already active.
        async function hasDocument() {
          // Check all windows controlled by the service worker to see if one
          // of them is the offscreen document with the given path
          const matchedClients = await clients.matchAll();
          return matchedClients.some(
            (c) => c.url === chrome.runtime.getURL(OFFSCREEN_DOCUMENT_PATH)
          );
        }
    
        async function setupOffscreenDocument(path) {
          // If we do not have a document, we are already setup and can skip
          if (!(await hasDocument())) {
            // create offscreen document
            if (creating) {
              await creating;
            } else {
              creating = chrome.offscreen.createDocument({
                url: path,
                reasons: [
                    chrome.offscreen.Reason.DOM_SCRAPING
                ],
                justification: 'authentication'
              });
              await creating;
              creating = null;
            }
          }
        }
    
        async function closeOffscreenDocument() {
          if (!(await hasDocument())) {
            return;
          }
          await chrome.offscreen.closeDocument();
        }
    
        function getAuth() {
          return new Promise(async (resolve, reject) => {
            const auth = await chrome.runtime.sendMessage({
              type: 'firebase-auth',
              target: 'offscreen'
            });
            auth?.name !== 'FirebaseError' ? resolve(auth) : reject(auth);
          })
        }
    
        async function firebaseAuth() {
          await setupOffscreenDocument(OFFSCREEN_DOCUMENT_PATH);
    
          const auth = await getAuth()
            .then((auth) => {
              console.log('User Authenticated', auth);
              return auth;
            })
            .catch(err => {
              if (err.code === 'auth/operation-not-allowed') {
                console.error('You must enable an OAuth provider in the Firebase' +
                              ' console in order to use signInWithPopup. This sample' +
                              ' uses Google by default.');
              } else {
                console.error(err);
                return err;
              }
            })
            .finally(closeOffscreenDocument)
    
          return auth;
        }
        

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