From 81689d2c7a16dcf1a87903cc08b1d70b50f592f7 Mon Sep 17 00:00:00 2001 From: ananducv Date: Wed, 16 Jul 2025 12:08:42 +0530 Subject: [PATCH 1/3] authentication issue in production and failing to fetch cards and emails in production fixec Replaces hardcoded API and web app URLs with environment-based configuration, supporting development, preview, and production environments. Adds robust config loading, fallback logic, and detailed logging throughout background and popup scripts. Removes the SETUP.md file, as configuration is now self-documented in code and logs. --- packages/Extenstions/SETUP.md | 86 ------------------------------ packages/Extenstions/background.js | 73 +++++++++++++++++++++---- packages/Extenstions/config.js | 84 +++++++++++++++++++++++++---- packages/Extenstions/popup.js | 31 +++++++++-- 4 files changed, 162 insertions(+), 112 deletions(-) delete mode 100644 packages/Extenstions/SETUP.md diff --git a/packages/Extenstions/SETUP.md b/packages/Extenstions/SETUP.md deleted file mode 100644 index 2602ddd5..00000000 --- a/packages/Extenstions/SETUP.md +++ /dev/null @@ -1,86 +0,0 @@ -# Zecrypt Browser Extension Setup - -## Environment Configuration - -⚠️ **SECURITY NOTICE**: This extension requires proper environment configuration to work securely. Never commit sensitive keys to version control. - -### Development Setup - -1. **Create Environment File** - - Create a `.env` file in the `packages/Extenstions/` directory: - - ```bash - cd packages/Extenstions/ - cp .env.example .env # If example exists, or create manually - ``` - -2. **Configure Environment Variables** - - Add the following to your `.env` file: - - ``` - INDEXED_DB_AES_KEY="HxmfPmPwqQZ3gHKwfHXi6TmPwVDppr0oDKyPwCdopDI=" - ``` - - **Note**: The key should match the `NEXT_PUBLIC_INDEXED_DB_AES_KEY` from the frontend-web `.env.local` file. - -3. **Verify Setup** - - - The `.env` file should NOT be committed to git (it's already in `.gitignore`) - - Check that the extension loads without console errors - - Verify that crypto operations work correctly - -### Production Deployment - -For production environments, consider: - -1. **Build-time Injection**: Use a build script to inject environment variables -2. **Secure Key Management**: Use proper secrets management systems -3. **Key Rotation**: Implement regular key rotation procedures - -### File Structure - -``` -packages/Extenstions/ -├── .env # Your environment file (NOT committed) -├── .gitignore # Ensures .env is ignored -├── config.js # Configuration loader -├── crypto-utils.js # Crypto utilities (uses config) -├── background.js # Background script (initializes config) -└── ... other files -``` - -### Troubleshooting - -**Error: "Extension configuration not loaded"** -- Ensure `.env` file exists in the extensions directory -- Check that `INDEXED_DB_AES_KEY` is properly set in `.env` -- Verify the key format (should be base64 encoded) - -**Error: "Configuration key not found"** -- Check that the key in `.env` matches exactly: `INDEXED_DB_AES_KEY` -- Ensure there are no extra spaces or characters -- Verify the file is saved properly - -**Error: "Invalid encryption key format"** -- The key should be a valid base64 string -- Check that the key length is correct (32 bytes when decoded) -- Ensure the key matches the one used in the web application - -### Security Best Practices - -1. **Never hardcode keys** in source code -2. **Use different keys** for different environments (dev/staging/prod) -3. **Rotate keys regularly** and update all environments -4. **Restrict access** to environment files and key management systems -5. **Monitor usage** and implement key usage auditing - -### Getting the Key - -The `INDEXED_DB_AES_KEY` should be obtained from: -1. Your development team's secure key management system -2. The frontend-web application's environment configuration -3. Your organization's secrets management platform - -Contact your development team if you don't have access to the required keys. \ No newline at end of file diff --git a/packages/Extenstions/background.js b/packages/Extenstions/background.js index 47b4b192..68bc98a4 100644 --- a/packages/Extenstions/background.js +++ b/packages/Extenstions/background.js @@ -1,20 +1,31 @@ -// Base API URL -const API_BASE_URL = 'https://preview.api.zecrypt.io/api/v1/web'; - // Import configuration and crypto utilities importScripts('config.js'); importScripts('crypto-utils.js'); +// API Base URL will be set after configuration is loaded +let API_BASE_URL = null; + // Initialize configuration when extension starts (async function initializeExtension() { try { await ExtensionConfig.initConfig(); + API_BASE_URL = ExtensionConfig.getApiBaseUrl(); + console.log('Extension initialized with API Base URL:', API_BASE_URL); } catch (error) { - console.error('Failed to load extension configuration:', error); - console.error('Please ensure you have created a .env file in the extensions directory with the required keys'); + console.error('Failed to initialize extension configuration:', error); + // Fallback to production URL if config fails + API_BASE_URL = 'https://api.zecrypt.io/api/v1/web'; } })(); +// Function to ensure configuration is loaded +function ensureConfigLoaded() { + if (!API_BASE_URL) { + throw new Error('Extension configuration not loaded. Please reload the extension.'); + } + return API_BASE_URL; +} + // Helper function to check if URL is accessible for script injection function isValidUrl(url) { // Exclude chrome:// URLs, chrome-extension:// URLs, and other restricted schemes @@ -243,21 +254,43 @@ function apiRequest(endpoint, method, data) { options.body = JSON.stringify(data); } - return fetch(API_BASE_URL + resolvedEndpoint, options); + const apiUrl = ensureConfigLoaded() + resolvedEndpoint; + console.log('Making API request:', { + url: apiUrl, + method: method, + hasToken: !!authData.token, + tokenPrefix: authData.token ? authData.token.substring(0, 10) + '...' : 'none', + workspaceId: authData.workspaceId, + projectId: authData.projectId + }); + + return fetch(apiUrl, options); }) .then(function(response) { + console.log('API response status:', response.status, response.statusText); + if (!response.ok) { if (response.status === 401) { + console.error('401 Unauthorized - clearing stored tokens'); chrome.storage.local.remove(['zecryptToken', 'zecryptWorkspaceId', 'zecryptProjectId']); throw new Error('Authentication failed. Please log in again.'); } - throw new Error('API request failed with status ' + response.status); + // Try to get response text for better error details + return response.text().then(errorText => { + console.error('API request failed:', { + status: response.status, + statusText: response.statusText, + errorText: errorText + }); + throw new Error(`API request failed with status ${response.status}: ${errorText}`); + }); } return response.json(); }) .then(function(data) { + console.log('API request successful'); resolve(data); }) .catch(function(error) { @@ -351,32 +384,45 @@ function checkLocalStorageAuth() { func: () => { try { const authData = localStorage.getItem('zecrypt_extension_auth'); + console.log('Extension: Checking localStorage for auth data:', !!authData); + if (authData) { try { const parsed = JSON.parse(authData); + console.log('Extension: Found auth data:', { + hasToken: !!parsed.token, + hasWorkspaceId: !!parsed.workspaceId, + hasProjectId: !!parsed.projectId, + hasProjectAesKey: !!parsed.projectAesKey, + timestamp: parsed.timestamp + }); + // Remove the auth data after reading it localStorage.removeItem('zecrypt_extension_auth'); return parsed; } catch (e) { - console.error('Error parsing auth data:', e); + console.error('Extension: Error parsing auth data:', e); localStorage.removeItem('zecrypt_extension_auth'); return null; } } return null; } catch (error) { - console.error('Error accessing localStorage:', error); + console.error('Extension: Error accessing localStorage:', error); return null; } } }, (results) => { if (chrome.runtime.lastError) { + console.error('Extension: Script execution failed:', chrome.runtime.lastError.message); resolve({ success: false, error: chrome.runtime.lastError.message }); return; } if (results && results[0] && results[0].result) { const authData = results[0].result; + console.log('Extension: Processing auth data from localStorage'); + // Store in extension storage chrome.storage.local.set({ zecryptToken: authData.token, @@ -384,26 +430,31 @@ function checkLocalStorageAuth() { zecryptProjectId: authData.projectId }, async () => { if (chrome.runtime.lastError) { - console.error('Error storing auth data:', chrome.runtime.lastError); + console.error('Extension: Error storing auth data:', chrome.runtime.lastError); resolve({ success: false, error: chrome.runtime.lastError.message }); } else { + console.log('Extension: Successfully stored auth data in chrome.storage.local'); + // Store project AES key securely if provided if (authData.projectAesKey) { await storeProjectAesKey(authData.projectAesKey); + console.log('Extension: Successfully stored project AES key'); } resolve({ success: true, authData }); } }); } else { + console.log('Extension: No auth data found in localStorage'); resolve({ success: false, error: 'No auth data found' }); } }); } else { + console.log('Extension: No accessible tab found for localStorage check'); resolve({ success: false, error: 'No accessible tab found' }); } }); } catch (error) { - console.error('Error checking localStorage:', error); + console.error('Extension: Error in checkLocalStorageAuth:', error); resolve({ success: false, error: error.message }); } }); diff --git a/packages/Extenstions/config.js b/packages/Extenstions/config.js index b4fa945c..afdc8c62 100644 --- a/packages/Extenstions/config.js +++ b/packages/Extenstions/config.js @@ -11,6 +11,30 @@ // This will hold our configuration let CONFIG = null; +/** + * Default configuration values + */ +const DEFAULT_CONFIG = { + // Development environment + DEVELOPMENT: { + API_BASE_URL: 'http://localhost:8000/api/v1/web', + WEB_APP_BASE_URL: 'http://localhost:3000', + IS_DEVELOPMENT: true + }, + // Production environment + PRODUCTION: { + API_BASE_URL: 'https://api.zecrypt.io/api/v1/web', + WEB_APP_BASE_URL: 'https://app.zecrypt.io', + IS_DEVELOPMENT: false + }, + // Preview environment + PREVIEW: { + API_BASE_URL: 'https://preview.api.zecrypt.io/api/v1/web', + WEB_APP_BASE_URL: 'https://preview.app.zecrypt.io', + IS_DEVELOPMENT: false + } +}; + /** * Load configuration from environment file * Note: This is a simplified approach for browser extensions @@ -57,22 +81,39 @@ async function loadConfigFromEnv() { */ async function initConfig() { // Try to load from .env file first - CONFIG = await loadConfigFromEnv(); + let envConfig = await loadConfigFromEnv(); // If no config loaded, check for injected config (build-time injection) - if (!CONFIG && typeof window !== 'undefined' && window.INJECTED_CONFIG) { - CONFIG = window.INJECTED_CONFIG; + if (!envConfig && typeof window !== 'undefined' && window.INJECTED_CONFIG) { + envConfig = window.INJECTED_CONFIG; } - // If still no config, throw an error - if (!CONFIG || !CONFIG.INDEXED_DB_AES_KEY) { + // Determine environment + const environment = envConfig?.ENVIRONMENT || 'PRODUCTION'; + + // Merge default config with environment-specific config and .env overrides + CONFIG = { + ...DEFAULT_CONFIG[environment], + ...envConfig + }; + + // If still no encryption key, throw an error + if (!CONFIG.INDEXED_DB_AES_KEY) { throw new Error( 'Configuration not found! Please create a .env file in the extensions directory with:\n' + - 'INDEXED_DB_AES_KEY="your_key_here"\n\n' + + 'INDEXED_DB_AES_KEY="your_key_here"\n' + + 'ENVIRONMENT="PRODUCTION" # or DEVELOPMENT or PREVIEW\n\n' + 'See README.md for setup instructions.' ); } + console.log('Extension configuration loaded:', { + environment, + apiBaseUrl: CONFIG.API_BASE_URL, + webAppBaseUrl: CONFIG.WEB_APP_BASE_URL, + isDevelopment: CONFIG.IS_DEVELOPMENT + }); + return CONFIG; } @@ -85,7 +126,7 @@ function getConfig(key) { } const value = CONFIG[key]; - if (!value) { + if (value === undefined) { throw new Error(`Configuration key '${key}' not found.`); } @@ -99,6 +140,27 @@ function getIndexedDbAesKey() { return getConfig('INDEXED_DB_AES_KEY'); } +/** + * Get the API base URL + */ +function getApiBaseUrl() { + return getConfig('API_BASE_URL'); +} + +/** + * Get the web app base URL + */ +function getWebAppBaseUrl() { + return getConfig('WEB_APP_BASE_URL'); +} + +/** + * Check if running in development mode + */ +function isDevelopment() { + return getConfig('IS_DEVELOPMENT'); +} + /** * Check if configuration is loaded */ @@ -111,6 +173,9 @@ const ExtensionConfig = { initConfig, getConfig, getIndexedDbAesKey, + getApiBaseUrl, + getWebAppBaseUrl, + isDevelopment, isConfigLoaded }; @@ -119,7 +184,6 @@ if (typeof globalThis !== 'undefined') { globalThis.ExtensionConfig = ExtensionConfig; } -// Also support module exports for potential build tools -if (typeof module !== 'undefined' && module.exports) { - module.exports = ExtensionConfig; +if (typeof self !== 'undefined') { + self.ExtensionConfig = ExtensionConfig; } \ No newline at end of file diff --git a/packages/Extenstions/popup.js b/packages/Extenstions/popup.js index ac77462c..9053fa5e 100644 --- a/packages/Extenstions/popup.js +++ b/packages/Extenstions/popup.js @@ -7,6 +7,30 @@ document.addEventListener('DOMContentLoaded', () => { const logoutBtn = document.getElementById('logoutBtn'); const cardsList = document.getElementById('cardsList'); const emailsList = document.getElementById('emailsList'); + + // Configuration will be loaded when needed + let BASE_URL = null; + + // Function to get web app base URL + function getWebAppBaseUrl() { + if (!BASE_URL) { + try { + // Try to get from extension config + if (chrome.extension.getBackgroundPage() && + chrome.extension.getBackgroundPage().ExtensionConfig && + chrome.extension.getBackgroundPage().ExtensionConfig.isConfigLoaded()) { + BASE_URL = chrome.extension.getBackgroundPage().ExtensionConfig.getWebAppBaseUrl(); + } else { + // Fallback to production URL + BASE_URL = 'https://app.zecrypt.io'; + } + } catch (error) { + console.warn('Could not load config, using fallback URL:', error); + BASE_URL = 'https://app.zecrypt.io'; + } + } + return BASE_URL; + } // Listen for messages from the extension login page window.addEventListener('message', (event) => { // Verify origin for security (in production, be more specific) @@ -59,16 +83,13 @@ document.addEventListener('DOMContentLoaded', () => { } }); // Configuration for development vs production const isDevelopment = false; // Set to false for production - const BASE_URL = isDevelopment - ? 'http://localhost:3000' - : 'https://app.zecrypt.io'; - // Login button click handler + // Login button click handler loginBtn.addEventListener('click', () => { // Start auth checking in background chrome.runtime.sendMessage({ type: 'START_AUTH_CHECK' }); // Open the extension login page in a new tab (with locale prefix) - chrome.tabs.create({ url: `${BASE_URL}/en/extension-login?from=extension` }); + chrome.tabs.create({ url: `${getWebAppBaseUrl()}/en/extension-login?from=extension` }); }); // Logout button click handler logoutBtn.addEventListener('click', () => { From dfd29f7db1de5d05696dcd1fe0a73e7aee4b8c18 Mon Sep 17 00:00:00 2001 From: ananducv Date: Thu, 17 Jul 2025 19:33:08 +0530 Subject: [PATCH 2/3] Refactor email autofill to account autofill Replaces all references to 'email' autofill with 'account' autofill across background, content, and popup scripts and HTML. Updates data processing, UI selectors, and messaging to support generic account credentials (username, password, website) instead of just email addresses, improving flexibility for autofilling login forms. --- packages/Extenstions/background.js | 74 +++++++++-------- packages/Extenstions/content.js | 122 ++++++++++++++++------------- packages/Extenstions/popup.html | 8 +- packages/Extenstions/popup.js | 60 ++++++++------ 4 files changed, 146 insertions(+), 118 deletions(-) diff --git a/packages/Extenstions/background.js b/packages/Extenstions/background.js index 68bc98a4..5839f0ce 100644 --- a/packages/Extenstions/background.js +++ b/packages/Extenstions/background.js @@ -47,8 +47,8 @@ const ENDPOINTS = { cards: function(workspaceId, projectId) { return `/${workspaceId}/${projectId}/cards`; }, - emails: function(workspaceId, projectId) { - return `/${workspaceId}/${projectId}/emails`; + accounts: function(workspaceId, projectId) { + return `/${workspaceId}/${projectId}/accounts`; } }; @@ -175,53 +175,59 @@ async function processCardData(cardRaw) { } } -// Enhanced function to process and decrypt email data -async function processEmailData(emailRaw) { +// Enhanced function to process and decrypt account data +async function processAccountData(accountRaw) { try { const projectAesKey = await getDecryptedProjectAesKey(); if (!projectAesKey) { console.error("Project AES key not found for decryption"); return { - ...emailRaw, - email: 'Key missing' + ...accountRaw, + username: 'Key missing', + password: 'Key missing', + website: accountRaw.url || accountRaw.website || 'undefined' }; } - if (emailRaw.data && emailRaw.data.includes('.')) { + if (accountRaw.data && accountRaw.data.includes('.')) { try { - const decryptedData = await CryptoUtils.decryptDataField(emailRaw.data, projectAesKey); + const decryptedData = await CryptoUtils.decryptDataField(accountRaw.data, projectAesKey); const parsedData = JSON.parse(decryptedData); return { - ...emailRaw, - email: parsedData.email_address || 'undefined', - password: parsedData.password || 'undefined' + ...accountRaw, + username: parsedData.username || 'undefined', + password: parsedData.password || 'undefined', + website: accountRaw.url || accountRaw.website || 'undefined' }; } catch (decryptError) { - console.error("Failed to decrypt email data:", decryptError); + console.error("Failed to decrypt account data:", decryptError); return { - ...emailRaw, - email: 'Decrypt failed' + ...accountRaw, + username: 'Decrypt failed', + password: 'Decrypt failed', + website: accountRaw.url || accountRaw.website || 'undefined' }; } } else { // Data might not be encrypted (legacy format) try { - const parsedData = JSON.parse(emailRaw.data); + const parsedData = JSON.parse(accountRaw.data); return { - ...emailRaw, - email: parsedData.email_address || 'undefined', - password: parsedData.password || 'undefined' + ...accountRaw, + username: parsedData.username || 'undefined', + password: parsedData.password || 'undefined', + website: accountRaw.url || accountRaw.website || 'undefined' }; } catch (parseError) { - console.error("Error parsing email data:", parseError); - return emailRaw; + console.error("Error parsing account data:", parseError); + return accountRaw; } } } catch (error) { - console.error("Error processing email data:", error); - return emailRaw; + console.error("Error processing account data:", error); + return accountRaw; } } @@ -322,17 +328,17 @@ function getCards() { }); } -// Enhanced getEmails function with decryption -function getEmails() { +// Enhanced getAccounts function with decryption +function getAccounts() { return new Promise(function(resolve, reject) { - apiRequest(ENDPOINTS.emails) + apiRequest(ENDPOINTS.accounts) .then(async function(response) { - const emails = response.data || []; - const processedEmails = await Promise.all(emails.map(processEmailData)); + const accounts = response.data || []; + const processedAccounts = await Promise.all(accounts.map(processAccountData)); resolve({ success: true, - data: processedEmails + data: processedAccounts }); }) .catch(function(error) { @@ -613,8 +619,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { return true; } - if (message.dataType === 'emails') { - getEmails() + if (message.dataType === 'accounts') { + getAccounts() .then(response => { sendResponse(response); }) @@ -643,14 +649,14 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { return true; } - if (message.dataType === 'email') { - getEmails() + if (message.dataType === 'account') { + getAccounts() .then(response => { if (response.success && response.data && response.data.length > 0) { - // Return all emails so the content script can show a selector UI + // Return all accounts so the content script can show a selector UI sendResponse({ success: true, data: response.data, multiple: response.data.length > 1 }); } else { - sendResponse({ success: false, error: 'No emails available' }); + sendResponse({ success: false, error: 'No accounts available' }); } }) .catch(error => { diff --git a/packages/Extenstions/content.js b/packages/Extenstions/content.js index 2dbf4db2..d11ee6b1 100644 --- a/packages/Extenstions/content.js +++ b/packages/Extenstions/content.js @@ -3,17 +3,17 @@ function detectForms() { // Look for credit card forms const cardNumberInputs = document.querySelectorAll('input[autocomplete="cc-number"], input[name*="card_number"], input[name*="cardNumber"], input[id*="card-number"], input[id*="cardNumber"]'); - // Look for email inputs - const emailInputs = document.querySelectorAll('input[type="email"], input[name*="email"], input[id*="email"]'); + // Look for username/login inputs + const usernameInputs = document.querySelectorAll('input[type="email"], input[type="text"][name*="username"], input[type="text"][name*="user"], input[type="text"][name*="login"], input[type="text"][id*="username"], input[type="text"][id*="user"], input[type="text"][id*="login"], input[name*="email"], input[id*="email"]'); // Process card number inputs cardNumberInputs.forEach(input => { setupAutofillButton(input, 'card'); }); - // Process email inputs - emailInputs.forEach(input => { - setupAutofillButton(input, 'email'); + // Process username inputs + usernameInputs.forEach(input => { + setupAutofillButton(input, 'account'); }); } @@ -62,13 +62,13 @@ function fetchAndFillData(type, input) { // Single card, fill directly fillCardData(input, response.data[0] || response.data); } - } else if (type === 'email') { - // Check if there are multiple emails + } else if (type === 'account') { + // Check if there are multiple accounts if (response.multiple && response.data.length > 1) { - showEmailSelector(input, response.data); + showAccountSelector(input, response.data); } else { - // Single email, fill directly - fillEmailData(input, response.data[0] || response.data); + // Single account, fill directly + fillAccountData(input, response.data[0] || response.data); } } } else { @@ -78,14 +78,14 @@ function fetchAndFillData(type, input) { }); } -// Show email selector UI when multiple emails are available -function showEmailSelector(input, emails) { +// Show account selector UI when multiple accounts are available +function showAccountSelector(input, accounts) { // Remove any existing selector - removeEmailSelector(); + removeAccountSelector(); // Create overlay const overlay = document.createElement('div'); - overlay.id = 'zecrypt-email-selector-overlay'; + overlay.id = 'zecrypt-account-selector-overlay'; overlay.style.cssText = ` position: fixed; top: 0; @@ -123,7 +123,7 @@ function showEmailSelector(input, emails) { `; const title = document.createElement('h3'); - title.textContent = 'Select Email Account'; + title.textContent = 'Select Account'; title.style.cssText = ` margin: 0; font-size: 18px; @@ -146,21 +146,21 @@ function showEmailSelector(input, emails) { align-items: center; justify-content: center; `; - closeBtn.addEventListener('click', removeEmailSelector); + closeBtn.addEventListener('click', removeAccountSelector); header.appendChild(title); header.appendChild(closeBtn); - // Create email list - const emailList = document.createElement('div'); - emailList.style.cssText = ` + // Create account list + const accountList = document.createElement('div'); + accountList.style.cssText = ` max-height: 300px; overflow-y: auto; `; - emails.forEach((email, index) => { - const emailItem = document.createElement('div'); - emailItem.style.cssText = ` + accounts.forEach((account, index) => { + const accountItem = document.createElement('div'); + accountItem.style.cssText = ` padding: 12px; border: 1px solid #e5e7eb; border-radius: 6px; @@ -170,44 +170,54 @@ function showEmailSelector(input, emails) { background: #f9fafb; `; - emailItem.addEventListener('mouseenter', () => { - emailItem.style.backgroundColor = '#f3f4f6'; - emailItem.style.borderColor = '#4f46e5'; + accountItem.addEventListener('mouseenter', () => { + accountItem.style.backgroundColor = '#f3f4f6'; + accountItem.style.borderColor = '#4f46e5'; }); - emailItem.addEventListener('mouseleave', () => { - emailItem.style.backgroundColor = '#f9fafb'; - emailItem.style.borderColor = '#e5e7eb'; + accountItem.addEventListener('mouseleave', () => { + accountItem.style.backgroundColor = '#f9fafb'; + accountItem.style.borderColor = '#e5e7eb'; }); - emailItem.addEventListener('click', () => { - fillEmailData(input, email); - removeEmailSelector(); + accountItem.addEventListener('click', () => { + fillAccountData(input, account); + removeAccountSelector(); }); - const emailAddress = document.createElement('div'); - emailAddress.textContent = email.email || 'No email address'; - emailAddress.style.cssText = ` + const accountUsername = document.createElement('div'); + accountUsername.textContent = account.username || 'No username'; + accountUsername.style.cssText = ` font-weight: 500; color: #1f2937; margin-bottom: 4px; `; - const emailTitle = document.createElement('div'); - emailTitle.textContent = email.title || 'Untitled Email'; - emailTitle.style.cssText = ` + const accountTitle = document.createElement('div'); + const website = account.website || account.url || ''; + let displayText = account.title || account.name || 'Untitled Account'; + if (website) { + try { + const domain = new URL(website).hostname.replace('www.', ''); + displayText += ` • ${domain}`; + } catch (e) { + displayText += ` • ${website}`; + } + } + accountTitle.textContent = displayText; + accountTitle.style.cssText = ` font-size: 14px; color: #6b7280; `; - emailItem.appendChild(emailAddress); - emailItem.appendChild(emailTitle); - emailList.appendChild(emailItem); + accountItem.appendChild(accountUsername); + accountItem.appendChild(accountTitle); + accountList.appendChild(accountItem); }); // Assemble modal modal.appendChild(header); - modal.appendChild(emailList); + modal.appendChild(accountList); overlay.appendChild(modal); // Add to page @@ -216,23 +226,23 @@ function showEmailSelector(input, emails) { // Close on overlay click overlay.addEventListener('click', (e) => { if (e.target === overlay) { - removeEmailSelector(); + removeAccountSelector(); } }); // Close on escape key const escapeHandler = (e) => { if (e.key === 'Escape') { - removeEmailSelector(); + removeAccountSelector(); document.removeEventListener('keydown', escapeHandler); } }; document.addEventListener('keydown', escapeHandler); } -// Remove email selector UI -function removeEmailSelector() { - const overlay = document.getElementById('zecrypt-email-selector-overlay'); +// Remove account selector UI +function removeAccountSelector() { + const overlay = document.getElementById('zecrypt-account-selector-overlay'); if (overlay) { overlay.remove(); } @@ -509,10 +519,10 @@ function fillCardData(input, cardData) { } } -// Fill email data into form -function fillEmailData(input, emailData) { - // Fill the email address - input.value = emailData.email; +// Fill account data into form +function fillAccountData(input, accountData) { + // Fill the username (could be email or username field) + input.value = accountData.username; triggerInputEvent(input); // Define search scope: the form, or the whole document as a fallback @@ -523,8 +533,8 @@ function fillEmailData(input, emailData) { if (passwordInputs.length > 0) { // Find the first visible password input const visiblePasswordInput = Array.from(passwordInputs).find(el => el.offsetParent !== null); - if (visiblePasswordInput) { - visiblePasswordInput.value = emailData.password; + if (visiblePasswordInput && accountData.password) { + visiblePasswordInput.value = accountData.password; triggerInputEvent(visiblePasswordInput); } } @@ -574,10 +584,10 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (cardInputs.length > 0) { fillCardData(cardInputs[0], message.data); } - } else if (message.dataType === 'email') { - const emailInputs = document.querySelectorAll('input[type="email"], input[name*="email"], input[id*="email"]'); - if (emailInputs.length > 0) { - fillEmailData(emailInputs[0], message.data); + } else if (message.dataType === 'account') { + const usernameInputs = document.querySelectorAll('input[type="email"], input[type="text"][name*="username"], input[type="text"][name*="user"], input[type="text"][name*="login"], input[type="text"][id*="username"], input[type="text"][id*="user"], input[type="text"][id*="login"], input[name*="email"], input[id*="email"]'); + if (usernameInputs.length > 0) { + fillAccountData(usernameInputs[0], message.data); } } diff --git a/packages/Extenstions/popup.html b/packages/Extenstions/popup.html index cc1a0485..2d4e2f76 100644 --- a/packages/Extenstions/popup.html +++ b/packages/Extenstions/popup.html @@ -309,11 +309,11 @@
- - Email Addresses + + Accounts
-
- +
+
diff --git a/packages/Extenstions/popup.js b/packages/Extenstions/popup.js index 9053fa5e..78563c6f 100644 --- a/packages/Extenstions/popup.js +++ b/packages/Extenstions/popup.js @@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', () => { const loginBtn = document.getElementById('loginBtn'); const logoutBtn = document.getElementById('logoutBtn'); const cardsList = document.getElementById('cardsList'); - const emailsList = document.getElementById('emailsList'); + const accountsList = document.getElementById('accountsList'); // Configuration will be loaded when needed let BASE_URL = null; @@ -120,15 +120,15 @@ document.addEventListener('DOMContentLoaded', () => { // Clear any existing data cardsList.innerHTML = ''; - emailsList.innerHTML = ''; + accountsList.innerHTML = ''; } - // Fetch cards and emails data + // Fetch cards and accounts data function fetchData() { // Show loading indicator loadingSection.style.display = 'block'; contentSection.style.display = 'none'; - // Fetch cards and emails in parallel + // Fetch cards and accounts in parallel Promise.all([ new Promise((resolve) => { chrome.runtime.sendMessage({ @@ -141,12 +141,12 @@ document.addEventListener('DOMContentLoaded', () => { new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'FETCH_DATA', - dataType: 'emails' + dataType: 'accounts' }, (response) => { resolve(response || { success: false, error: 'No response' }); }); }) - ]).then(([cardsResponse, emailsResponse]) => { + ]).then(([cardsResponse, accountsResponse]) => { // Hide loading indicator loadingSection.style.display = 'none'; contentSection.style.display = 'block'; @@ -159,11 +159,11 @@ document.addEventListener('DOMContentLoaded', () => { console.error('Failed to fetch cards:', cardsResponse?.error); } - if (emailsResponse && emailsResponse.success) { - renderEmails(emailsResponse.data); + if (accountsResponse && accountsResponse.success) { + renderAccounts(accountsResponse.data); } else { - renderEmails([]); - console.error('Failed to fetch emails:', emailsResponse?.error); + renderAccounts([]); + console.error('Failed to fetch accounts:', accountsResponse?.error); } }); } @@ -194,28 +194,40 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // Render email items - function renderEmails(emails) { - emailsList.innerHTML = ''; + // Render account items + function renderAccounts(accounts) { + accountsList.innerHTML = ''; - if (!emails || emails.length === 0) { - emailsList.innerHTML = '
No email addresses found
'; + if (!accounts || accounts.length === 0) { + accountsList.innerHTML = '
No accounts found
'; return; } - emails.forEach(email => { - const emailItem = document.createElement('div'); - emailItem.className = 'item'; + accounts.forEach(account => { + const accountItem = document.createElement('div'); + accountItem.className = 'item'; - const emailAddress = email.email || 'Unknown Email'; - const passwordDisplay = email.password ? '•'.repeat(email.password.length) : 'No password'; + const accountName = account.title || account.name || 'Unknown Account'; + const username = account.username || 'No username'; + const website = account.website || account.url || ''; - emailItem.innerHTML = ` -
${emailAddress}
-
${passwordDisplay}
+ // Show website domain if available + let displayText = username; + if (website) { + try { + const domain = new URL(website).hostname.replace('www.', ''); + displayText = `${username} • ${domain}`; + } catch (e) { + displayText = `${username} • ${website}`; + } + } + + accountItem.innerHTML = ` +
${accountName}
+
${displayText}
`; - emailsList.appendChild(emailItem); + accountsList.appendChild(accountItem); }); } }); \ No newline at end of file From 16291ba0a783f4a173d9de4406b5519acdd1d201 Mon Sep 17 00:00:00 2001 From: ananducv Date: Fri, 18 Jul 2025 11:48:41 +0530 Subject: [PATCH 3/3] Always show selector UI for cards and accounts Updated background and content scripts to always display the selector UI for cards and accounts, even when only one item is available. This change ensures user confirmation and consistency in the selection process. --- packages/Extenstions/background.js | 8 ++++---- packages/Extenstions/content.js | 18 ++++-------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/packages/Extenstions/background.js b/packages/Extenstions/background.js index 5839f0ce..ab487f45 100644 --- a/packages/Extenstions/background.js +++ b/packages/Extenstions/background.js @@ -637,8 +637,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { getCards() .then(response => { if (response.success && response.data && response.data.length > 0) { - // Return all cards - for now use first one, but structure allows for future selector UI - sendResponse({ success: true, data: response.data, multiple: response.data.length > 1 }); + // Always show selector UI for cards, even for single card + sendResponse({ success: true, data: response.data, multiple: true }); } else { sendResponse({ success: false, error: 'No cards available' }); } @@ -653,8 +653,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { getAccounts() .then(response => { if (response.success && response.data && response.data.length > 0) { - // Return all accounts so the content script can show a selector UI - sendResponse({ success: true, data: response.data, multiple: response.data.length > 1 }); + // Always show selector UI for accounts, even for single account + sendResponse({ success: true, data: response.data, multiple: true }); } else { sendResponse({ success: false, error: 'No accounts available' }); } diff --git a/packages/Extenstions/content.js b/packages/Extenstions/content.js index d11ee6b1..c58edc76 100644 --- a/packages/Extenstions/content.js +++ b/packages/Extenstions/content.js @@ -55,21 +55,11 @@ function fetchAndFillData(type, input) { }, (response) => { if (response && response.success) { if (type === 'card') { - // Check if there are multiple cards - if (response.multiple && response.data.length > 1) { - showCardSelector(input, response.data); - } else { - // Single card, fill directly - fillCardData(input, response.data[0] || response.data); - } + // Always show card selector for user confirmation + showCardSelector(input, response.data); } else if (type === 'account') { - // Check if there are multiple accounts - if (response.multiple && response.data.length > 1) { - showAccountSelector(input, response.data); - } else { - // Single account, fill directly - fillAccountData(input, response.data[0] || response.data); - } + // Always show account selector for user confirmation + showAccountSelector(input, response.data); } } else { // Show error or prompt to log in