mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-02-20 07:54:10 -05:00
Update browser extension autolock timeout handler to use alarm API for >30s timeouts (#1684)
This commit is contained in:
committed by
Leendert de Borst
parent
ba744b8e93
commit
50e73c08f2
@@ -4,7 +4,7 @@
|
||||
|
||||
import { onMessage, sendMessage } from "webext-bridge/background";
|
||||
|
||||
import { handleResetAutoLockTimer, handlePopupHeartbeat, handleSetAutoLockTimeout } from '@/entrypoints/background/AutolockTimeoutHandler';
|
||||
import { handleResetAutoLockTimer, handlePopupHeartbeat, handleSetAutoLockTimeout, initializeAutoLockAlarm, handleAutoLockAlarm } from '@/entrypoints/background/AutolockTimeoutHandler';
|
||||
import { handleClipboardCopied, handleCancelClipboardClear, handleGetClipboardClearTimeout, handleSetClipboardClearTimeout, handleGetClipboardCountdownState } from '@/entrypoints/background/ClipboardClearHandler';
|
||||
import { setupContextMenus } from '@/entrypoints/background/ContextMenu';
|
||||
import { handleGetWebAuthnSettings, handleWebAuthnCreate, handleWebAuthnGet, handlePasskeyPopupResponse, handleGetRequestData } from '@/entrypoints/background/PasskeyHandler';
|
||||
@@ -89,6 +89,15 @@ export default defineBackground({
|
||||
await setupContextMenus();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize auto-lock alarm system.
|
||||
* This ensures the alarm is restored if the service worker was terminated.
|
||||
*/
|
||||
await initializeAutoLockAlarm();
|
||||
|
||||
// Register alarm listener for auto-lock
|
||||
browser.alarms.onAlarm.addListener(handleAutoLockAlarm);
|
||||
|
||||
// Listen for custom commands
|
||||
try {
|
||||
browser.commands.onCommand.addListener(async (command) => {
|
||||
|
||||
@@ -1,78 +1,170 @@
|
||||
import { storage } from 'wxt/utils/storage';
|
||||
|
||||
import { handleLockVault } from '@/entrypoints/background/VaultMessageHandler';
|
||||
|
||||
import { LocalPreferencesService } from '@/utils/LocalPreferencesService';
|
||||
|
||||
let autoLockTimer: NodeJS.Timeout | null = null;
|
||||
import type { Browser } from 'wxt/browser';
|
||||
|
||||
import { browser, storage } from '#imports';
|
||||
|
||||
const AUTO_LOCK_ALARM_NAME = 'vault-auto-lock';
|
||||
|
||||
/*
|
||||
* Threshold in seconds below which we use setTimeout instead of alarms.
|
||||
* Alarms have a minimum delay of 30 seconds in production (packed) extensions.
|
||||
* For short timeouts, setTimeout is more accurate and the service worker
|
||||
* won't terminate before the timer fires.
|
||||
*/
|
||||
const SHORT_TIMEOUT_THRESHOLD = 30;
|
||||
|
||||
// Timer handle for short timeouts using setTimeout
|
||||
let shortTimeoutTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
/**
|
||||
* Reset the auto-lock timer.
|
||||
* Lock the vault due to inactivity timeout.
|
||||
*/
|
||||
export function handleResetAutoLockTimer(): void {
|
||||
resetAutoLockTimer();
|
||||
async function lockVaultDueToInactivity(): Promise<void> {
|
||||
// Check if vault is still unlocked before locking
|
||||
const encryptionKey = await storage.getItem('session:encryptionKey') as string | null;
|
||||
if (!encryptionKey) {
|
||||
// Vault is already locked
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
handleLockVault();
|
||||
console.info('[AUTO_LOCK] Vault locked due to inactivity');
|
||||
} catch (error) {
|
||||
console.error('[AUTO_LOCK] Error locking vault:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle popup heartbeat - extend auto-lock timer.
|
||||
* Clear the short timeout timer if it exists.
|
||||
*/
|
||||
export function handlePopupHeartbeat(): void {
|
||||
extendAutoLockTimer();
|
||||
function clearShortTimeoutTimer(): void {
|
||||
if (shortTimeoutTimer) {
|
||||
clearTimeout(shortTimeoutTimer);
|
||||
shortTimeoutTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auto-lock timeout setting.
|
||||
* Set the auto-lock timer using the appropriate method based on timeout duration.
|
||||
* Uses setTimeout for short timeouts (< 30s) and alarms for longer ones.
|
||||
*/
|
||||
export async function handleSetAutoLockTimeout(timeout: number): Promise<boolean> {
|
||||
await LocalPreferencesService.setAutoLockTimeout(timeout);
|
||||
resetAutoLockTimer();
|
||||
return true;
|
||||
async function setAutoLockTimer(timeoutSeconds: number): Promise<void> {
|
||||
// Clear any existing timers
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
|
||||
if (timeoutSeconds < SHORT_TIMEOUT_THRESHOLD) {
|
||||
/*
|
||||
* Use setTimeout for short timeouts.
|
||||
* Service worker won't terminate before the timer fires.
|
||||
*/
|
||||
shortTimeoutTimer = setTimeout(() => {
|
||||
shortTimeoutTimer = null;
|
||||
lockVaultDueToInactivity();
|
||||
}, timeoutSeconds * 1000);
|
||||
} else {
|
||||
/*
|
||||
* Use alarms for longer timeouts.
|
||||
* Alarms persist across service worker restarts.
|
||||
*/
|
||||
const delayInMinutes = timeoutSeconds / 60;
|
||||
await browser.alarms.create(AUTO_LOCK_ALARM_NAME, {
|
||||
delayInMinutes: delayInMinutes
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the auto-lock timer based on current settings.
|
||||
* Initialize the auto-lock alarm system.
|
||||
* This should be called when the background script starts.
|
||||
* It checks if the vault is unlocked and if so, ensures a timer is set.
|
||||
*/
|
||||
async function resetAutoLockTimer(): Promise<void> {
|
||||
// Clear existing timer
|
||||
if (autoLockTimer) {
|
||||
clearTimeout(autoLockTimer);
|
||||
autoLockTimer = null;
|
||||
export async function initializeAutoLockAlarm(): Promise<void> {
|
||||
// Check if vault is unlocked
|
||||
const encryptionKey = await storage.getItem('session:encryptionKey') as string | null;
|
||||
if (!encryptionKey) {
|
||||
// Vault is locked, clear any existing alarm
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get timeout setting
|
||||
const timeout = await LocalPreferencesService.getAutoLockTimeout();
|
||||
|
||||
// Don't set timer if timeout is 0 (disabled) or if vault is already locked
|
||||
if (timeout === 0) {
|
||||
// Auto-lock disabled, clear any existing alarm
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* For short timeouts, we can't restore the exact remaining time after
|
||||
* service worker restart, so we just set a new timer with the full duration.
|
||||
* For alarms, check if one already exists to avoid resetting the countdown.
|
||||
*/
|
||||
if (timeout < SHORT_TIMEOUT_THRESHOLD) {
|
||||
// Short timeout - set a new setTimeout (can't persist across restarts anyway)
|
||||
if (!shortTimeoutTimer) {
|
||||
await setAutoLockTimer(timeout);
|
||||
}
|
||||
} else {
|
||||
// Long timeout - only create alarm if one doesn't exist
|
||||
const existingAlarm = await browser.alarms.get(AUTO_LOCK_ALARM_NAME);
|
||||
if (!existingAlarm) {
|
||||
await setAutoLockTimer(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the auto-lock alarm firing.
|
||||
* This is called by the alarm listener in background.ts.
|
||||
*/
|
||||
export async function handleAutoLockAlarm(alarm: Browser.alarms.Alarm): Promise<void> {
|
||||
if (alarm.name !== AUTO_LOCK_ALARM_NAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
await lockVaultDueToInactivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the auto-lock timer.
|
||||
* This clears any existing timer and creates a new one with the full timeout period.
|
||||
*/
|
||||
export async function handleResetAutoLockTimer(): Promise<void> {
|
||||
// Get timeout setting
|
||||
const timeout = await LocalPreferencesService.getAutoLockTimeout();
|
||||
|
||||
// Don't set timer if timeout is 0 (disabled)
|
||||
if (timeout === 0) {
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if vault is unlocked before setting timer
|
||||
const encryptionKey = await storage.getItem('session:encryptionKey') as string | null;
|
||||
|
||||
if (!encryptionKey) {
|
||||
// Vault is already locked, don't start timer
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set new timer
|
||||
autoLockTimer = setTimeout(async () => {
|
||||
try {
|
||||
handleLockVault();
|
||||
|
||||
console.info('[AUTO_LOCK] Vault locked due to inactivity');
|
||||
autoLockTimer = null;
|
||||
} catch (error) {
|
||||
console.error('[AUTO_LOCK] Error locking vault:', error);
|
||||
}
|
||||
}, timeout * 1000);
|
||||
await setAutoLockTimer(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the auto-lock timer by the full timeout period.
|
||||
* This is called by popup heartbeats to prevent locking while popup is active.
|
||||
* Handle popup heartbeat - extend auto-lock timer.
|
||||
* This resets the timer to prevent locking while popup is active.
|
||||
*/
|
||||
async function extendAutoLockTimer(): Promise<void> {
|
||||
export async function handlePopupHeartbeat(): Promise<void> {
|
||||
// Get timeout setting
|
||||
const timeout = await LocalPreferencesService.getAutoLockTimeout();
|
||||
|
||||
@@ -83,28 +175,46 @@ async function extendAutoLockTimer(): Promise<void> {
|
||||
|
||||
// Check if vault is unlocked
|
||||
const encryptionKey = await storage.getItem('session:encryptionKey') as string | null;
|
||||
|
||||
if (!encryptionKey) {
|
||||
// Vault is already locked, don't extend timer
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing timer and start a new one
|
||||
if (autoLockTimer) {
|
||||
clearTimeout(autoLockTimer);
|
||||
autoLockTimer = null;
|
||||
await setAutoLockTimer(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auto-lock timeout setting.
|
||||
* Updates the stored preference and resets the timer with the new value.
|
||||
*/
|
||||
export async function handleSetAutoLockTimeout(timeout: number): Promise<boolean> {
|
||||
await LocalPreferencesService.setAutoLockTimeout(timeout);
|
||||
|
||||
// Clear existing timers
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
|
||||
// If timeout is 0 (disabled), we're done
|
||||
if (timeout === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set new timer
|
||||
autoLockTimer = setTimeout(async () => {
|
||||
try {
|
||||
// Lock the vault (preserves local data for offline unlock)
|
||||
handleLockVault();
|
||||
// Check if vault is unlocked before setting new timer
|
||||
const encryptionKey = await storage.getItem('session:encryptionKey') as string | null;
|
||||
if (!encryptionKey) {
|
||||
// Vault is locked, don't start timer
|
||||
return true;
|
||||
}
|
||||
|
||||
console.info('[AUTO_LOCK] Vault locked due to inactivity');
|
||||
autoLockTimer = null;
|
||||
} catch (error) {
|
||||
console.error('[AUTO_LOCK] Error locking vault:', error);
|
||||
}
|
||||
}, timeout * 1000);
|
||||
await setAutoLockTimer(timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the auto-lock alarm and any short timeout timer.
|
||||
* This should be called when the vault is locked.
|
||||
*/
|
||||
export async function clearAutoLockAlarm(): Promise<void> {
|
||||
clearShortTimeoutTimer();
|
||||
await browser.alarms.clear(AUTO_LOCK_ALARM_NAME);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ export default defineConfig({
|
||||
"activeTab",
|
||||
"contextMenus",
|
||||
"scripting",
|
||||
"clipboardWrite"
|
||||
"clipboardWrite",
|
||||
"alarms"
|
||||
];
|
||||
|
||||
// Only add offscreen permission for Chrome and Edge
|
||||
|
||||
Reference in New Issue
Block a user