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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,3 +19,4 @@ extension/
|
||||
yarn-error.log
|
||||
src/locales/_build
|
||||
extension-*.zip
|
||||
.parcel-cache/
|
@@ -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
3
offscreen.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<!doctype html>
|
||||
<audio></audio>
|
||||
<script src="./offscreen.js"></script>
|
40
offscreen.js
Normal file
40
offscreen.js
Normal 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;
|
||||
}
|
8
packages/signup/firebaseConfig.js
Normal file
8
packages/signup/firebaseConfig.js
Normal 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'
|
||||
};
|
11
packages/signup/index.html
Normal file
11
packages/signup/index.html
Normal 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
7140
packages/signup/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
packages/signup/package.json
Normal file
9
packages/signup/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"firebase": "^10.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"parcel": "^2.12.0",
|
||||
"process": "^0.11.10"
|
||||
}
|
||||
}
|
35
packages/signup/signInWithPopup.js
Normal file
35
packages/signup/signInWithPopup.js
Normal 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);
|
||||
}
|
||||
});
|
13
src/auth.js
13
src/auth.js
@@ -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();
|
||||
|
43
src/db.js
43
src/db.js
@@ -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 = {
|
||||
|
@@ -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;
|
||||
}
|
||||
});
|
||||
|
@@ -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"
|
||||
|
Reference in New Issue
Block a user