Revision control

Copy as Markdown

Other Tools

const PING = 'TB/PING';
const BRIDGE_PING = 'APP/PING';
const BRIDGE_READY = 'TB/BRIDGE_READY';
const OIDC_USER = 'TB/OIDC_USER';
const OIDC_TOKEN = 'TB/OIDC_TOKEN';
const SIGN_IN_COMPLETE = 'SIGN_IN_COMPLETE';
const SIGN_OUT = 'SIGN_OUT';
const SEND_MESSAGE_TO_BRIDGE = 'SEND_MESSAGE_TO_BRIDGE';
const GET_LOGIN_STATE = 'GET_LOGIN_STATE';
const LOGIN_STATE_RESPONSE = 'LOGIN_STATE_RESPONSE';
const SIGN_IN = 'SIGN_IN';
const FORCE_CLOSE_WINDOW = 'FORCE_CLOSE_WINDOW';
const OPEN_MANAGEMENT_PAGE = 'OPEN_MANAGEMENT_PAGE';
// Add-On to Web — request/response pair for fetching the staged OIDC token set.
// The web page cannot call browser.storage.local directly, so it asks the
// background script to read it on its behalf via these two message types:
// GET_PENDING_ADDON_TOKEN : web page → bridge → background ("give me the token")
// PENDING_ADDON_TOKEN_RESPONSE : background → bridge → web page ("here it is")
const GET_PENDING_ADDON_TOKEN = 'TB/GET_PENDING_ADDON_TOKEN';
const PENDING_ADDON_TOKEN_RESPONSE = 'TB/PENDING_ADDON_TOKEN_RESPONSE';
// Telemetry pref bridge (issue #952). The hosted Send dashboard cannot call the
// browser.thundermailTelemetry experiment API, so it asks the background for the pref:
// GET_TELEMETRY_STATE : web page → bridge → background
// TELEMETRY_STATE_RESPONSE : background → bridge → web page
// TELEMETRY_STATE_CHANGED : background → bridge → web page (runtime change)
const GET_TELEMETRY_STATE = 'TB/GET_TELEMETRY_STATE';
const TELEMETRY_STATE_RESPONSE = 'TB/TELEMETRY_STATE_RESPONSE';
const TELEMETRY_STATE_CHANGED = 'TB/TELEMETRY_STATE_CHANGED';
window.postMessage({ type: BRIDGE_READY }, window.location.origin);
console.log(`[🌉 token-bridge] the token bridge has loaded.`);
// Initial message to the background
browser.runtime.sendMessage({
type: PING,
text: 'This got sent from the bridge to the background.',
});
window.addEventListener('message', (e) => {
// Security: this bridge forwards messages straight to the privileged add-on
// background (OIDC tokens, the encryption passphrase, sign-in/out). Only trust
// messages the app page posted to itself:
// • e.source !== window → came from an embedded/cross-origin frame or
// another window, not the top app page. Reject.
// • e.origin !== window.location.origin → not same-origin. Reject.
// Combined with the scoped content_scripts `matches` in the manifest (the
// bridge is only injected on the Send app's own origins), this keeps a
// third-party page or frame from driving the add-on.
if (e.source !== window) return;
if (e.origin !== window.location.origin) return;
// ----- Web to add-on: Step 6b — refresh token → background (Thundermail) -----
// handleOIDCCallback() posts OIDC_TOKEN with the refresh_token so that
// background.ts can create/update the Thundermail mail account.
if (e?.data?.type === OIDC_TOKEN) {
browser.runtime.sendMessage({
type: OIDC_TOKEN,
token: String(e.data.token ?? ''),
email: String(e.data.email ?? ''),
name: String(e.data.name ?? ''),
});
}
// ----- Web to add-on: Step 6c — full User object → background (auth storage) -----
// handleOIDCCallback() posts OIDC_USER with the full oidc-client-ts User so
// background.ts can persist it in browser.storage.local[STORAGE_KEY_AUTH].
// This is what loadUser() reads back later to re-hydrate the OIDC session
// in the extension popup without requiring a new login.
if (e?.data?.type === OIDC_USER) {
const userData = e.data.user;
if (userData && typeof userData === 'object') {
browser.runtime.sendMessage({
type: OIDC_USER,
user: userData,
});
}
}
if (e?.data?.type === SIGN_IN) {
browser.runtime.sendMessage({
type: SIGN_IN,
});
}
if (e?.data?.type === SIGN_IN_COMPLETE) {
browser.runtime.sendMessage({
type: SIGN_IN_COMPLETE,
});
}
if (e?.data?.type === BRIDGE_PING) {
browser.runtime.sendMessage({
type: PING,
text: String(e.data.text ?? ''),
});
}
if (e?.data?.type === SEND_MESSAGE_TO_BRIDGE) {
browser.runtime.sendMessage({
type: SEND_MESSAGE_TO_BRIDGE,
value: e.data.value,
});
}
if (e?.data?.type === GET_LOGIN_STATE) {
browser.runtime.sendMessage({
type: GET_LOGIN_STATE,
});
}
if (e?.data?.type === FORCE_CLOSE_WINDOW) {
browser.runtime.sendMessage({
type: FORCE_CLOSE_WINDOW,
});
}
if (e?.data?.type === OPEN_MANAGEMENT_PAGE) {
browser.runtime.sendMessage({
type: OPEN_MANAGEMENT_PAGE,
});
}
if (e?.data?.type === SIGN_OUT) {
browser.runtime.sendMessage({
type: SIGN_OUT,
});
}
// ----- Add-On to Web: fetch staged token from background -----
// Step 4 of the Add-On to Web flow.
// The /addon-auth web page posts this to ask the background script to read
// PENDING_ADDON_TOKEN out of browser.storage.local on its behalf.
// The background will respond with PENDING_ADDON_TOKEN_RESPONSE (see below).
if (e?.data?.type === GET_PENDING_ADDON_TOKEN) {
browser.runtime.sendMessage({
type: GET_PENDING_ADDON_TOKEN,
});
}
// ----- Web to add-on: hosted dashboard asks for the telemetry pref -----
// The background returns the state as the sendMessage response (scoped to
// this tab), which we relay back to the page. It answers only for our own
// Send tabs, so a non-Send page gets no response and the page fails closed.
if (e?.data?.type === GET_TELEMETRY_STATE) {
browser.runtime
.sendMessage({ type: GET_TELEMETRY_STATE })
.then((response) => {
if (response?.type === TELEMETRY_STATE_RESPONSE) {
window.postMessage(
{ type: TELEMETRY_STATE_RESPONSE, enabled: response.enabled },
window.location.origin
);
}
})
.catch(() => {
// No handler / not our tab — the page-side timeout fails closed.
});
}
});
// Listen for responses from background script and forward to web app
browser.runtime.onMessage.addListener((message) => {
if (message.type === LOGIN_STATE_RESPONSE) {
window.postMessage(
{
type: LOGIN_STATE_RESPONSE,
isLoggedIn: message.isLoggedIn,
username: message.username,
},
window.location.origin
);
}
// Forward an OIDC token obtained by the background (e.g. from the Accounts Hub)
// to the web app so it can log the user in automatically.
if (message.type === OIDC_TOKEN) {
window.postMessage(
{
type: OIDC_TOKEN,
token: message.token,
email: message.email,
name: message.name,
},
window.location.origin
);
}
// ----- Add-On to Web: return staged token to the web page -----
// Step 5 of the Add-On to Web flow.
// The background has read PENDING_ADDON_TOKEN from storage and sends it here.
// We forward it to the /addon-auth page so authenticateWithAddonToken()
// can resolve its Promise and proceed with authentication.
if (message.type === PENDING_ADDON_TOKEN_RESPONSE) {
window.postMessage(
{
type: PENDING_ADDON_TOKEN_RESPONSE,
tokenSet: message.tokenSet,
},
window.location.origin
);
}
// ----- Add-on to Web: telemetry runtime change (issue #952) -----
// The GET_TELEMETRY_STATE reply is handled inline as the sendMessage
// response above; here we only forward unsolicited runtime changes the
// background broadcasts to our Send tabs.
if (message.type === TELEMETRY_STATE_CHANGED) {
window.postMessage(
{
type: TELEMETRY_STATE_CHANGED,
enabled: message.enabled,
},
window.location.origin
);
}
});