1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-07-29 17:50:09 +02:00

get auth working with offscreen document

This commit is contained in:
Kushagra Gour
2024-05-09 15:41:41 +05:30
parent 2289af6a0b
commit 71582cf608
13 changed files with 7368 additions and 24 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ extension/
yarn-error.log
src/locales/_build
extension-*.zip
.parcel-cache/

View File

@@ -195,6 +195,8 @@ gulp.task('packageExtension', function () {
child_process.execSync('cp src/options.html extension');
child_process.execSync('cp src/eventPage.js extension');
child_process.execSync('cp src/icon-16.png extension');
child_process.execSync('cp offscreen.html extension');
child_process.execSync('cp offscreen.js extension');
child_process.execSync('rm -rf extension/service-worker.js');
return merge(
gulp

3
offscreen.html Normal file
View File

@@ -0,0 +1,3 @@
<!doctype html>
<audio></audio>
<script src="./offscreen.js"></script>

40
offscreen.js Normal file
View File

@@ -0,0 +1,40 @@
// This URL must point to the public site
const _URL = 'http://localhost:1234';
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 }) {
console.log('got postmessage in offscreen doc from iframe');
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;
}

View File

@@ -0,0 +1,8 @@
export const config = {
apiKey: 'AIzaSyBl8Dz7ZOE7aP75mipYl2zKdLSRzBU2fFc',
authDomain: 'web-maker-app.firebaseapp.com',
databaseURL: 'https://web-maker-app.firebaseio.com',
projectId: 'web-maker-app',
storageBucket: 'web-maker-app.appspot.com',
messagingSenderId: '560473480645'
};

View File

@@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<title>signInWithPopup</title>
</head>
<body>
<h1>signInWithPopup</h1>
<script type="module" src="signInWithPopup.js"></script>
</body>
</html>

7140
packages/signup/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
{
"dependencies": {
"firebase": "^10.11.1"
},
"devDependencies": {
"parcel": "^2.12.0",
"process": "^0.11.10"
}
}

View File

@@ -0,0 +1,35 @@
setTimeout(() => {
console.log('firing postmessag from iframe');
// window.top.postMessage('{"a":2}', "*");
}, 3000);
import { signInWithPopup, GoogleAuthProvider, getAuth } from 'firebase/auth';
import { initializeApp } from 'firebase/app';
import { config } from './firebaseConfig.js';
const app = initializeApp(config);
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);
}
});

View File

@@ -8,12 +8,23 @@ import {
signOut
} from 'firebase/auth';
import { log } from './utils';
import { signInWithCredential } from 'firebase/auth/web-extension';
export const authh = {
logout() {
signOut();
},
login(providerName) {
async login(providerName) {
debugger;
const authenticationObject = await chrome.runtime.sendMessage({
type: 'firebase-auth',
provider: providerName
});
const credential =
GoogleAuthProvider.credentialFromResult(authenticationObject);
// authenticationObject is of the type UserCredentialImpl. Use it to authenticate here
return signInWithCredential(auth, credential);
var provider;
if (providerName === 'facebook') {
provider = new FacebookAuthProvider();

View File

@@ -1,7 +1,17 @@
// import './firebaseInit';
import 'firebase/firestore';
import { db } from './firebaseInit';
import { getDoc, getDocs, doc, updateDoc, setDoc } from 'firebase/firestore';
import {
getDoc,
getDocs,
doc,
updateDoc,
setDoc,
where,
collection,
getCountFromServer,
query
} from 'firebase/firestore';
import { deferred } from './deferred';
import { trackEvent } from './analytics';
import { log } from './utils';
@@ -159,10 +169,7 @@ function getArrayFromQuerySnapshot(querySnapshot) {
async function fetchItem(itemId) {
const remoteDb = await getDb();
return remoteDb
.doc(`items/${itemId}`)
.get()
.then(doc => {
getDoc(doc(remoteDb, `items/${itemId}`)).then(doc => {
if (!doc.exists) return {};
const data = doc.data();
return data;
@@ -184,23 +191,23 @@ function getArrayFromQuerySnapshot(querySnapshot) {
async function getPublicItemCount(userId) {
const remoteDb = await getDb();
return remoteDb
.collection('items')
.where('createdBy', '==', userId)
.where('isPublic', '==', true)
.get()
.then(snapShot => {
return snapShot.size;
});
const q = query(
collection(remoteDb, 'items'),
where('createdBy', '==', userId),
where('isPublic', '==', true)
);
const snapshot = await getCountFromServer(q);
return snapshot.data().count;
}
async function getUserSubscriptionEvents(userId) {
const remoteDb = await getDb();
return remoteDb
.collection('subscriptions')
.where('userId', '==', userId)
.get()
.then(getArrayFromQuerySnapshot);
const q = query(
collection(remoteDb, 'subscriptions'),
where('userId', '==', userId)
);
return getDocs(q).then(getArrayFromQuerySnapshot);
}
window.db = {

View File

@@ -45,3 +45,78 @@ chrome.runtime.onInstalled.addListener(function callback(details) {
}
}
});
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 chrome.offscreen.hasDocument()) return;
await chrome.offscreen.createDocument({
url: path,
reasons: [chrome.offscreen.Reason.DOM_SCRAPING],
justification: 'authentication'
});
}
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;
}
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message.type === 'firebase-auth') {
firebaseAuth().then(sendResponse);
return true;
}
});

View File

@@ -4,7 +4,7 @@
"manifest_version": 3,
"description": "Blazing fast & offline playground for your web experiments",
"homepage_url": "https://webmaker.app",
"permissions": ["storage", "tabs"],
"permissions": ["storage", "tabs", "offscreen"],
"optional_permissions": ["downloads"],
"host_permissions": ["<all_urls>"],
"content_security_policy": {
@@ -26,6 +26,8 @@
"service_worker": "eventPage.js",
"type": "module"
},
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp4s9naKH2y92qItJCcj3deRGjjqhDfgUR2WDe34IEYEB4PGtmx/9IO4PaO49gMr82DBRuwxCg/vr9SyiVlG1/j0TApJlkaHVsRZE4EME2rbL1vzIQRE8gNB+b5L6rPl4GPX/eFqIuUe/UgQPZAadLBVxdwBdOv5ou8OY0jrLx/h0wcLVvk9d8/ELpk28Hb2LArCBd+vIngHK55Db1GMEGAyNUVzFCTg/MZ7w5fKLpA94mF2X0/Bv9sCjj7vDir24Mp5Z/Y3obTHvpzddppAE6ZEQ3fmpRkOJ3MbuaKYm9hNHs4az6XLW6Sdlv2YcIcCfvsev/WEKUZeD8KIRtRyyPQIDAQAB",
"icons": {
"16": "icon-16.png",
"48": "icon-48.png"