เอกสารนี้แสดงวิธีใช้ Firebase Authentication เพื่อลงชื่อเข้าใช้ Chrome ให้ผู้ใช้ ส่วนขยายที่ใช้ Manifest V3
Firebase Authentication มีวิธีการตรวจสอบสิทธิ์หลายวิธีให้ผู้ใช้ลงชื่อเข้าใช้ได้ ส่วนขยาย Chrome ซึ่งบางรายการต้องใช้ความพยายามในการพัฒนามากกว่าส่วนขยายอื่นๆ
หากต้องการใช้เมธอดต่อไปนี้ในส่วนขยาย Chrome ซึ่งใช้ไฟล์ Manifest V3 ให้ใช้เพียง
นำเข้าจาก firebase/auth/web-extension
:
- ลงชื่อเข้าใช้ด้วยอีเมลและรหัสผ่าน (
createUserWithEmailAndPassword
และsignInWithEmailAndPassword
) - ลงชื่อเข้าใช้ด้วยลิงก์อีเมล (
sendSignInLinkToEmail
,isSignInWithEmailLink
และsignInWithEmailLink
) - ลงชื่อเข้าใช้โดยไม่ระบุชื่อ (
signInAnonymously
) - ลงชื่อเข้าใช้ด้วยระบบการตรวจสอบสิทธิ์ที่กำหนดเอง (
signInWithCustomToken
) - จัดการการลงชื่อเข้าใช้ของผู้ให้บริการแยกต่างหาก จากนั้นใช้
signInWithCredential
นอกจากนี้ยังรองรับวิธีการลงชื่อเข้าใช้ต่อไปนี้ด้วย แต่ต้องดำเนินการเพิ่มเติมดังนี้
- ลงชื่อเข้าใช้ด้วยหน้าต่างป๊อปอัป (
signInWithPopup
,linkWithPopup
และreauthenticateWithPopup
) - ลงชื่อเข้าใช้โดยเปลี่ยนเส้นทางไปยังหน้าลงชื่อเข้าใช้ (
signInWithRedirect
,linkWithRedirect
และreauthenticateWithRedirect
) - ลงชื่อเข้าใช้ด้วยหมายเลขโทรศัพท์ด้วย reCAPTCHA
- การตรวจสอบสิทธิ์แบบหลายปัจจัยทาง SMS ด้วย reCAPTCHA
- การปกป้อง reCAPTCHA Enterprise
หากต้องการใช้วิธีการเหล่านี้ในส่วนขยาย Chrome ซึ่งใช้ไฟล์ Manifest V3 คุณต้องใช้ เอกสารนอกหน้าจอ
ใช้จุดแรกเข้าของ firebase/auth/web-ส่วนขยาย
การนำเข้าจาก firebase/auth/web-extension
ทำให้ผู้ใช้ที่ลงชื่อเข้าใช้จาก
ส่วนขยาย Chrome ที่คล้ายกับเว็บแอป
ระบบรองรับ Firebase/auth/web-extension ใน 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 โดยตรง
เนื่องจากโค้ดกำหนดให้ต้องโหลดจากภายนอกแพ็กเกจส่วนขยาย
ตั้งแต่ไฟล์ 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 ลงในรายการที่ได้รับอนุญาต โดเมน:
- เปิดโปรเจ็กต์ในคอนโซล Firebase
- ในส่วน Authentication ให้เปิดหน้า Settings
- เพิ่ม URI เหมือนตัวอย่างต่อไปนี้ในรายการโดเมนที่ได้รับอนุญาต
chrome-extension://CHROME_EXTENSION_ID
ในไฟล์ Manifest ของส่วนขยาย 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 ที่จัดการ การตรวจสอบสิทธิ์ มี 2 วิธีในการนำวิธีการไปใช้ ได้แก่ ที่ได้รับการสนับสนุนโดยตรงในส่วนขยาย:
- ใช้
firebase/auth
แทนfirebase/auth/web-extension
จุดแรกเข้าweb-extension
ใช้สำหรับโค้ดที่ทำงานภายในส่วนขยาย แม้ว่าสุดท้ายแล้วโค้ดนี้จะทำงานในส่วนขยาย (ใน iframe, ในเอกสารนอกหน้าจอ) แต่บริบทที่กำลังทำงานอยู่คือเว็บมาตรฐาน - รวมตรรกะการตรวจสอบสิทธิ์ใน Listener ของ
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 และส่วนขยายของคุณ - ตั้งค่าเอกสารนอกหน้าจอจาก Service Worker background.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()
ภายใน Service Worker ระบบจะสร้าง
เอกสารนอกหน้าจอและโหลดเว็บไซต์ใน iframe iframe ดังกล่าวจะประมวลผล
ในเบื้องหลัง และ Firebase จะผ่านการตรวจสอบสิทธิ์มาตรฐาน
เมื่อแก้ไขหรือปฏิเสธแล้ว ออบเจ็กต์การตรวจสอบสิทธิ์
จะส่งผ่านพร็อกซีจาก iframe ไปยัง Service Worker โดยใช้
เอกสาร