المصادقة باستخدام Firebase في امتداد Chrome

يوضح لك هذا المستند كيفية استخدام مصادقة Firebase لتسجيل دخول المستخدمين إلى ملحق Chrome الذي يستخدم Manifest V3 .

توفر مصادقة Firebase طرق مصادقة متعددة لتسجيل دخول المستخدمين من امتداد 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، يجب عليك استخدام Offscreen Documents .

استخدم نقطة دخول 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 Hosting . إنشاء موقع على شبكة الإنترنت بالمحتوى التالي:

<!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. في قسم المصادقة ، افتح صفحة الإعدادات .
  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 هو كود JavaScript الذي يتعامل مع المصادقة. هناك طريقتان مختلفتان لتنفيذ طريقة مدعومة مباشرة في الامتداد:

  • استخدم 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 الخاص بك

بعد أن يصبح موقع الويب الخاص بك نشطًا، يمكنك استخدامه في ملحق Chrome الخاص بك.

  1. أضف الإذن offscreen إلى ملف Manifest.json الخاص بك:
  2.     {
          "name": "signInWithPopup Demo",
          "manifest_version" 3,
          "background": {
            "service_worker": "background.js"
          },
          "permissions": [
            "offscreen"
          ]
        }
        
  3. قم بإنشاء المستند الموجود خارج الشاشة نفسه. هذا هو ملف HTML البسيط الموجود داخل حزمة الامتدادات الخاصة بك والذي يقوم بتحميل منطق مستند JavaScript الموجود خارج الشاشة:
  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. قم بإعداد المستند الموجود خارج الشاشة من عامل خدمة الخلفية.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 الخاص بك إلى عامل الخدمة الخاص بك، باستخدام المستند الموجود خارج الشاشة.