diff --git a/browser-extensions/chrome/src/background.ts b/browser-extensions/chrome/src/background.ts index e99268941..4d86e975b 100644 --- a/browser-extensions/chrome/src/background.ts +++ b/browser-extensions/chrome/src/background.ts @@ -81,14 +81,14 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { case 'GET_CREDENTIALS_FOR_URL': { if (!vaultState.derivedKey) { - sendResponse({ credentials: [] }); + sendResponse({ credentials: [], status: 'LOCKED' }); return; } chrome.storage.session.get(['encryptedVault'], async (result) => { try { if (!result.encryptedVault) { - sendResponse({ credentials: [] }); + sendResponse({ credentials: [], status: 'LOCKED' }); return; } @@ -97,11 +97,9 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { vaultState.derivedKey! ); - // Initialize SQLite client + // Initialize SQLite client and get credentials. const sqliteClient = new SqliteClient(); await sqliteClient.initializeFromBase64(decryptedVault); - - // Query credentials with their related service information const credentials = sqliteClient.getAllCredentials(); // Filter credentials that match the current domain @@ -123,7 +121,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { sendResponse({ credentials: credentials }); } catch (error) { console.error('Error getting credentials:', error); - sendResponse({ credentials: [] }); + sendResponse({ credentials: [], status: 'LOCKED', error: 'Failed to get credentials' }); } }); break; diff --git a/browser-extensions/chrome/src/contentScript.ts b/browser-extensions/chrome/src/contentScript.ts index 320142bc7..9b863bb8a 100644 --- a/browser-extensions/chrome/src/contentScript.ts +++ b/browser-extensions/chrome/src/contentScript.ts @@ -1,6 +1,11 @@ import { detectForms } from './utils/FormDetector'; import { Credential } from './types/Credential'; +interface CredentialResponse { + status: 'OK' | 'LOCKED'; + credentials?: Credential[]; +} + const placeholderBase64 = 'UklGRjoEAABXRUJQVlA4IC4EAAAwFwCdASqAAIAAPpFCm0olo6Ihp5IraLASCWUA0eb/0s56RrLtCnYfLPiBshdXWMx8j1Ez65f169iA4xUDBTEV6ylMQeCIj2b7RngGi7gKZ9WjKdSoy9R8JcgOmjCMlDmLG20KhNo/i/Dc/Ah5GAvGfm8kfniV3AkR6fxN6eKwjDc6xrDgSfS48G5uGV6WzQt24YAVlLSK9BMwndzfHnePK1KFchFrL7O3ulB8cGNCeomu4o+l0SrS/JKblJ4WTzj0DAD++lCUEouSfgRKdiV2TiYCD+H+l3tANKSPQFPQuzi7rbvxqGeRmXB9kDwURaoSTTpYjA9REMUi9uA6aV7PWtBNXgUzMLowYMZeos6Xvyhb34GmufswMHA5ZyYpxzjTphOak4ZjNOiz8aScO5ygiTx99SqwX/uL+HSeVOSraHw8IymrMwm+jLxqN8BS8dGcItLlm/ioulqH2j4V8glDgSut+ExkxiD7m8TGPrrjCQNJbRDzpOFsyCyfBZupvp8QjGKW2KGziSZeIWes4aTB9tRmeEBhnUrmTDZQuXcc67Fg82KHrSfaeeOEq6jjuUjQ8wUnzM4Zz3dhrwSyslVz/WvnKqYkr4V/TTXPFF5EjF4rM1bHZ8bK63EfTnK41+n3n4gEFoYP4mXkNH0hntnYcdTqiE7Gn+q0BpRRxnkpBSZlA6Wa70jpW0FGqkw5e591A5/H+OV+60WAo+4Mi+NlsKrvLZ9EiVaPnoEFZlJQx1fA777AJ2MjXJ4KSsrWDWJi1lE8yPs8V6XvcC0chDTYt8456sKXAagCZyY+fzQriFMaddXyKQdG8qBqcdYjAsiIcjzaRFBBoOK9sU+sFY7N6B6+xtrlu3c37rQKkI3O2EoiJOris54EjJ5OFuumA0M6riNUuBf/MEPFBVx1JRcUEs+upEBsCnwYski7FT3TTqHrx7v5AjgFN97xhPTkmVpu6sxRnWBi1fxIRp8eWZeFM6mUcGgVk1WeVb1yhdV9hoMo2TsNEPE0tHo/wvuSJSzbZo7wibeXM9v/rRfKcx7X93rfiXVnyQ9f/5CaAQ4lxedPp/6uzLtOS4FyL0bCNeZ6L5w+AiuyWCTDFIYaUzhwfG+/YTQpWyeZCdQIKzhV+3GeXI2cxoP0ER/DlOKymf1gm+zRU3sqf1lBVQ0y+mK/Awl9bS3uaaQmI0FUyUwHUKP7PKuXnO+LcwDv4OfPT6hph8smc1EtMe5ib/apar/qZ9dyaEaElALJ1KKxnHziuvVl8atk1fINSQh7OtXDyqbPw9o/nGIpTnv5iFmwmWJLis2oyEgPkJqyx0vYI8rjkVEzKc8eQavAJBYSpjMwM193Swt+yJyjvaGYWPnqExxKiNarpB2WSO7soCAZXhS1uEYHryrK47BH6W1dRiruqT0xpLih3MXiwU3VDwAAAA=='; // Listen for input field focus @@ -19,10 +24,18 @@ function showCredentialPopup(input: HTMLInputElement) : void { if (!forms.length) return; // Request credentials from background script - chrome.runtime.sendMessage({ type: 'GET_CREDENTIALS_FOR_URL', url: window.location.href }, (response) => { - if (!response.credentials?.length) return; + chrome.runtime.sendMessage({ type: 'GET_CREDENTIALS_FOR_URL', url: window.location.href }, (response: CredentialResponse) => { + switch (response.status) { + case 'OK': + if (response.credentials?.length) { + createPopup(input, response.credentials); + } + break; - createPopup(input, response.credentials); + case 'LOCKED': + createStatusPopup(input, 'AliasVault is locked, please login (again).'); + break; + } }); } @@ -126,6 +139,62 @@ function createPopup(input: HTMLInputElement, credentials: Credential[]) : void document.body.appendChild(popup); } +/** + * Create status popup. TODO: refactor to use same popup basic structure for all popup types. + */ +function createStatusPopup(input: HTMLInputElement, message: string): void { + // Remove existing popup if any + removeExistingPopup(); + + const popup = document.createElement('div'); + popup.id = 'aliasvault-credential-popup'; + + // Get input width + const inputWidth = input.offsetWidth; + + // Set popup width to match input width, with min/max constraints + const popupWidth = Math.max(250, Math.min(960, inputWidth)); + + popup.style.cssText = ` + position: absolute; + z-index: 999999; + background: white; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + padding: 12px 16px; + width: ${popupWidth}px; + `; + + // Add message + const messageElement = document.createElement('div'); + messageElement.style.cssText = ` + color: #666; + font-size: 14px; + `; + messageElement.textContent = message; + popup.appendChild(messageElement); + + // Position popup below input + const rect = input.getBoundingClientRect(); + popup.style.top = `${rect.bottom + window.scrollY + 2}px`; + popup.style.left = `${rect.left + window.scrollX}px`; + + // Add click outside handler + const handleClickOutside = (event: MouseEvent): void => { + if (!popup.contains(event.target as Node)) { + removeExistingPopup(); + document.removeEventListener('mousedown', handleClickOutside); + } + }; + + setTimeout(() => { + document.addEventListener('mousedown', handleClickOutside); + }, 100); + + document.body.appendChild(popup); +} + /** * Remove existing popup */ diff --git a/browser-extensions/chrome/src/context/AuthContext.tsx b/browser-extensions/chrome/src/context/AuthContext.tsx index c6e2bb2a1..1c04179b4 100644 --- a/browser-extensions/chrome/src/context/AuthContext.tsx +++ b/browser-extensions/chrome/src/context/AuthContext.tsx @@ -82,6 +82,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children * Logout */ const logout = async () : Promise => { + // Clear vault in background worker. + await chrome.runtime.sendMessage({ type: 'CLEAR_VAULT' }); + await Promise.all([ localStorage.removeItem('username'), localStorage.removeItem('accessToken'), diff --git a/browser-extensions/chrome/src/pages/Unlock.tsx b/browser-extensions/chrome/src/pages/Unlock.tsx index 0910aac35..0372dc940 100644 --- a/browser-extensions/chrome/src/pages/Unlock.tsx +++ b/browser-extensions/chrome/src/pages/Unlock.tsx @@ -83,7 +83,7 @@ const Unlock: React.FC = () => {

- Enter your master password to unlock your database. + Enter your master password to unlock your vault.

{error && ( diff --git a/src/AliasVault.Client/Auth/Pages/Unlock.razor b/src/AliasVault.Client/Auth/Pages/Unlock.razor index f0e664fe3..b84f3a11b 100644 --- a/src/AliasVault.Client/Auth/Pages/Unlock.razor +++ b/src/AliasVault.Client/Auth/Pages/Unlock.razor @@ -48,7 +48,7 @@ else else {

- Enter your master password to unlock your database. + Enter your master password to unlock your vault.