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