בצע אימות באמצעות Firebase בתוסף Chrome

מסמך זה מראה לך כיצד להשתמש באימות Firebase כדי להכניס משתמשים לתוסף 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
  • אימות רב-גורמי SMS עם reCAPTCHA
  • הגנת reCAPTCHA Enterprise

כדי להשתמש בשיטות אלה בתוסף Manifest V3 Chrome, עליך להשתמש במסמכים מחוץ למסך .

השתמש בנקודת הכניסה של Firebase/Auth/Web-extension

ייבוא ​​מ- firebase/auth/web-extension הופכת כניסה למשתמשים מתוסף Chrome לדומה לאפליקציית אינטרנט.

firebase/auth/web-extension נתמך רק בגירסאות Web SDK v10.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, מכיוון שהן דורשות טעינת קוד מחוץ לחבילת ההרחבה. החל ממניפסט 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 שלך, ודא שאתה מוסיף את כתובות האתרים הבאות לרשימת ההיתרים 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. הגדר את המסמך מחוץ למסך מ-background.js Service Worker.
  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() בתוך ה-Service Worker שלך, הוא יצור את המסמך מחוץ למסך ויטען את האתר ב-iframe. ה-iframe הזה יעבד ברקע, ו-Firebase יעבור את זרימת האימות הסטנדרטית. לאחר פתרון או דחייתה, אובייקט האימות יועבר בשרת proxy מה-iframe שלך ​​ל-Service Worker שלך, באמצעות המסמך מחוץ למסך.