112 lines
4.6 KiB
JavaScript
112 lines
4.6 KiB
JavaScript
/**
|
|
* extension/bridge/bridge.js
|
|
*
|
|
* Content script injected ONLY on pwkeeper.ngodanguyen.tech.
|
|
*
|
|
* Web app → Extension:
|
|
* Listens for the 'passkeeper:session' custom event dispatched by auth.js
|
|
* after login, and forwards access_token + refresh_token + enc_key_salt to
|
|
* the background service worker via chrome.runtime.sendMessage.
|
|
*
|
|
* Extension → Web app:
|
|
* Listens for INJECT_SESSION messages from the background (triggered when the
|
|
* user logs in through the popup). Writes tokens into the web app's storage
|
|
* and fires 'passkeeper:ext-login' so auth.js can unlock the vault overlay.
|
|
*/
|
|
(() => {
|
|
'use strict';
|
|
|
|
// ── Web app → Extension ────────────────────────────────────────────────────
|
|
|
|
function syncToExtension(access_token, refresh_token, enc_key_salt) {
|
|
if (!access_token || !refresh_token) return;
|
|
chrome.runtime.sendMessage({
|
|
type: 'WEB_SESSION_SYNC',
|
|
access_token,
|
|
refresh_token,
|
|
enc_key_salt: enc_key_salt || '',
|
|
}).catch(() => {});
|
|
}
|
|
|
|
// Fired by auth.js completeLogin
|
|
window.addEventListener('passkeeper:session', e => {
|
|
const { access_token, refresh_token, enc_key_salt } = e.detail || {};
|
|
syncToExtension(access_token, refresh_token, enc_key_salt);
|
|
});
|
|
|
|
// Fired by auth.js on logout / sign-out
|
|
window.addEventListener('passkeeper:logout', () => {
|
|
chrome.runtime.sendMessage({ type: 'WEB_SESSION_CLEAR' }).catch(() => {});
|
|
});
|
|
|
|
// On initial page load: sync in both directions
|
|
|
|
// Web → Extension: if the web app is already logged in, forward tokens to extension
|
|
const existing_token = sessionStorage.getItem('access_token');
|
|
const existing_refresh = localStorage.getItem('refresh_token');
|
|
const existing_salt = sessionStorage.getItem('enc_key_salt');
|
|
if (existing_token && existing_refresh) {
|
|
syncToExtension(existing_token, existing_refresh, existing_salt);
|
|
}
|
|
|
|
// Extension → Web: if the web app has no session but the extension does,
|
|
// refresh the access token first (so we inject a guaranteed-fresh pair) then
|
|
// fire passkeeper:ext-login so vault.js can proceed without hitting 401.
|
|
if (!existing_token) {
|
|
(async () => {
|
|
try {
|
|
const local = await chrome.storage.local.get(['refresh_token', 'enc_key_salt']);
|
|
const refresh = local.refresh_token;
|
|
const salt = local.enc_key_salt ||
|
|
(await chrome.storage.session.get('enc_key_salt')).enc_key_salt || '';
|
|
if (!refresh || !salt) return;
|
|
|
|
// Exchange the stored refresh token for a fresh access token
|
|
const res = await fetch(`${location.origin}/api/auth/refresh`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ refresh_token: refresh }),
|
|
});
|
|
if (!res.ok) return;
|
|
const data = await res.json();
|
|
|
|
const freshAccess = data.access_token;
|
|
const freshRefresh = data.refresh_token || refresh;
|
|
|
|
// Write fresh tokens into the page session so vault.js works immediately
|
|
sessionStorage.setItem('access_token', freshAccess);
|
|
localStorage.setItem('refresh_token', freshRefresh);
|
|
sessionStorage.setItem('enc_key_salt', salt);
|
|
|
|
// Keep extension storage in sync with the rotated tokens
|
|
chrome.storage.session.set({ access_token: freshAccess, enc_key_salt: salt });
|
|
chrome.storage.local.set({ refresh_token: freshRefresh, enc_key_salt: salt });
|
|
|
|
window.dispatchEvent(new CustomEvent('passkeeper:ext-login', {
|
|
detail: { enc_key_salt: salt },
|
|
}));
|
|
} catch {}
|
|
})();
|
|
}
|
|
|
|
// ── Extension → Web app ────────────────────────────────────────────────────
|
|
|
|
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
|
if (msg.type === 'INJECT_SESSION') {
|
|
// Write tokens so the web app is effectively logged in
|
|
sessionStorage.setItem('access_token', msg.access_token);
|
|
localStorage.setItem('refresh_token', msg.refresh_token);
|
|
if (msg.enc_key_salt) sessionStorage.setItem('enc_key_salt', msg.enc_key_salt);
|
|
|
|
// Tell the web app a session was injected — vault.js listens for this
|
|
// to dismiss the unlock overlay and load the vault
|
|
window.dispatchEvent(new CustomEvent('passkeeper:ext-login', {
|
|
detail: { enc_key_salt: msg.enc_key_salt },
|
|
}));
|
|
|
|
sendResponse({ ok: true });
|
|
}
|
|
return false;
|
|
});
|
|
})();
|