mirror of
https://github.com/morgan9e/virtual-webauthn
synced 2026-04-14 00:04:09 +09:00
125 lines
3.0 KiB
JavaScript
125 lines
3.0 KiB
JavaScript
const HOST_NAME = "com.example.virtual_webauthn";
|
|
|
|
let port = null;
|
|
let seq = 0;
|
|
const pending = new Map();
|
|
let sessionKey = null;
|
|
|
|
function connect() {
|
|
if (port) return;
|
|
try {
|
|
port = chrome.runtime.connectNative(HOST_NAME);
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
port.onMessage.addListener((msg) => {
|
|
if (msg.sessionKey) {
|
|
sessionKey = msg.sessionKey;
|
|
}
|
|
const cb = pending.get(msg.id);
|
|
if (cb) {
|
|
pending.delete(msg.id);
|
|
cb(msg);
|
|
}
|
|
});
|
|
|
|
port.onDisconnect.addListener(() => {
|
|
port = null;
|
|
for (const [id, cb] of pending) {
|
|
cb({ id, success: false, error: "Host disconnected" });
|
|
}
|
|
pending.clear();
|
|
updateIcon(false);
|
|
});
|
|
|
|
updateIcon(true);
|
|
}
|
|
|
|
function sendNative(msg) {
|
|
return new Promise((resolve, reject) => {
|
|
connect();
|
|
if (!port) {
|
|
reject(new Error("Cannot connect to native host"));
|
|
return;
|
|
}
|
|
const id = ++seq;
|
|
const timer = setTimeout(() => {
|
|
pending.delete(id);
|
|
reject(new Error("Timed out"));
|
|
}, 120_000);
|
|
|
|
pending.set(id, (resp) => {
|
|
clearTimeout(timer);
|
|
resolve(resp);
|
|
});
|
|
|
|
port.postMessage({ ...msg, id });
|
|
});
|
|
}
|
|
|
|
// --- Icon status ---
|
|
|
|
let lastStatus = null;
|
|
|
|
function updateIcon(connected) {
|
|
const status = connected ? "ok" : "err";
|
|
if (status === lastStatus) return;
|
|
lastStatus = status;
|
|
const icon = connected ? "icon-green.svg" : "icon-red.svg";
|
|
const title = connected
|
|
? "Virtual WebAuthn — Connected"
|
|
: "Virtual WebAuthn — Disconnected";
|
|
chrome.action.setIcon({ path: icon });
|
|
chrome.action.setTitle({ title });
|
|
}
|
|
|
|
async function pingLoop() {
|
|
try {
|
|
const resp = await sendNative({ type: "ping" });
|
|
updateIcon(resp.success === true);
|
|
} catch {
|
|
updateIcon(false);
|
|
}
|
|
setTimeout(pingLoop, 10_000);
|
|
}
|
|
|
|
pingLoop();
|
|
|
|
// --- Message relay from content script ---
|
|
|
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
if (message.type !== "VWEBAUTHN_REQUEST") return;
|
|
|
|
const msg = {
|
|
type: message.action,
|
|
data: message.payload,
|
|
};
|
|
|
|
// Password from content.js (already in isolated context)
|
|
if (message.password) {
|
|
msg.password = message.password;
|
|
} else if (sessionKey) {
|
|
msg.sessionKey = sessionKey;
|
|
}
|
|
|
|
if (message.action === "list" && message.rpId) {
|
|
msg.rpId = message.rpId;
|
|
}
|
|
|
|
sendNative(msg)
|
|
.then((resp) => {
|
|
if (!resp.success) {
|
|
const err = resp.error || "";
|
|
if (err.includes("session") || err.includes("Session")) {
|
|
sessionKey = null;
|
|
}
|
|
}
|
|
sendResponse(resp);
|
|
})
|
|
.catch((error) => {
|
|
sendResponse({ success: false, error: error.message });
|
|
});
|
|
return true;
|
|
});
|