Keep client secrets in add-on storage; host only verifies and serves the local API.

This commit is contained in:
alexveley
2026-09-22 07:42:10 -04:00
parent de0814f3ed
commit 41c83dabac
13 changed files with 185 additions and 290 deletions
+21 -41
View File
@@ -4,7 +4,7 @@ const HOST_NAME = "com.easygoingaming.firefox_agent_bridge";
const RECONNECT_MS = 2000;
const ALLOWED = {
"meta.ping": async () => ({ ok: true, extension: "firefox-agent-bridge" }),
"meta.ping": async () => ({ ok: true, extension: "bookmarks-api" }),
"meta.methods": async () => Object.keys(ALLOWED).sort(),
"bookmarks.getTree": () => browser.bookmarks.getTree(),
"bookmarks.getSubTree": (id) => browser.bookmarks.getSubTree(id),
@@ -22,7 +22,6 @@ const ALLOWED = {
let port = null;
let reconnectTimer = null;
let hostUp = false;
const adminWait = new Map();
function asArray(value) {
if (value === undefined || value === null) {
@@ -40,39 +39,32 @@ async function dispatchBookmark(message) {
return fn(...asArray(message.args));
}
function adminCall(method, args) {
if (!port) {
return Promise.reject(new Error("native host is not connected"));
}
const id = crypto.randomUUID();
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
adminWait.delete(id);
reject(new Error("host did not answer"));
}, 8000);
adminWait.set(id, { resolve, reject, timer });
port.postMessage({ type: "admin", id, method, args: args || [] });
});
}
function attach(nextPort) {
port = nextPort;
hostUp = true;
port.onMessage.addListener(async (message) => {
if (message && message.type === "admin-result") {
const pending = adminWait.get(message.id);
if (!pending) {
return;
}
adminWait.delete(message.id);
clearTimeout(pending.timer);
if (message.ok) {
pending.resolve(message.result);
} else {
pending.reject(new Error(message.error || "admin failed"));
if (message && message.type === "auth") {
try {
const client = await findClientByTokenHash(message.token_hash);
port.postMessage({
type: "auth-result",
id: message.id,
ok: Boolean(client),
client: client || null,
});
} catch (err) {
port.postMessage({
type: "auth-result",
id: message.id,
ok: false,
error: err && err.message ? err.message : String(err),
});
}
return;
}
if (message && message.type) {
return;
}
const id = message && message.id;
try {
const result = await dispatchBookmark(message);
@@ -88,11 +80,6 @@ function attach(nextPort) {
port.onDisconnect.addListener(() => {
port = null;
hostUp = false;
for (const [id, pending] of adminWait.entries()) {
clearTimeout(pending.timer);
pending.reject(new Error("native host disconnected"));
adminWait.delete(id);
}
scheduleReconnect();
});
}
@@ -118,16 +105,9 @@ function connect() {
}
browser.runtime.onMessage.addListener((message) => {
const kind = message && message.type;
if (kind === "status") {
if (message && message.type === "status") {
return Promise.resolve({ hostUp, port: 17634 });
}
if (kind === "admin") {
return adminCall(message.method, message.args).then(
(result) => ({ ok: true, result }),
(err) => ({ ok: false, error: err.message })
);
}
return undefined;
});