Complete guide for implementing browser autofill with TrustVault PWA using Credential Management API and Chrome Extension.
TrustVault provides two methods for browser autofill:
- Native Credential Management API - Built into modern browsers, no extension needed
- Chrome Extension - Enhanced autofill with advanced features and better UX
User Credential Flow:
┌─────────────────┐
│ TrustVault │
│ (IndexedDB) │
└────────┬────────┘
│ Encrypted Storage
↓
┌─────────────────┐
│ Origin Check │ ← Validates current page origin
│ (example.com) │
└────────┬────────┘
│ Match Found
↓
┌─────────────────┐
│ Credential │ ← User confirmation required
│ Management API │
└────────┬────────┘
│ Autofill
↓
┌─────────────────┐
│ Login Form │
│ (filled) │
└─────────────────┘
✅ HTTPS-Only - Credentials only autofill on secure connections ✅ Origin Matching - Exact origin validation prevents credential theft ✅ Same-Site Only - No cross-origin credential sharing (unless explicitly configured) ✅ User Confirmation - Requires user interaction before filling (configurable) ✅ Encrypted Storage - All credentials encrypted in IndexedDB ✅ CSP Headers - Content Security Policy prevents exfiltration
- ✅ Chrome 51+
- ✅ Edge 79+
- ✅ Opera 38+
- ❌ Firefox (not yet supported)
- ❌ Safari (not yet supported)
src/core/autofill/
├── credentialManagementService.ts # Core API integration
├── autofillSettings.ts # User preferences
└── README.md # Documentation
import {
storeCredentialInBrowser,
getCredentialFromBrowser,
findMatchingCredentials,
} from '@/core/autofill/credentialManagementService';
// After user adds/updates credential
const browserCred = toBrowserCredential(credential, window.location.origin);
await storeCredentialInBrowser(browserCred);
// On login page load
const matches = findMatchingCredentials(allCredentials, window.location.origin);
if (matches.length > 0) {
// Show autofill UI
}
// Autofill credential
const browserCred = await getCredentialFromBrowser();
if (browserCred) {
usernameField.value = browserCred.id;
passwordField.value = browserCred.password;
}1. After Credential Creation (src/presentation/pages/AddCredentialPage.tsx)
// After successful credential creation
if (credential.category === 'login' && credential.url) {
const browserCred = toBrowserCredential(credential, window.location.origin);
await storeCredentialInBrowser(browserCred);
}2. After Credential Update (src/presentation/pages/EditCredentialPage.tsx)
// After successful credential update
if (credential.category === 'login' && credential.url) {
const browserCred = toBrowserCredential(credential, window.location.origin);
await storeCredentialInBrowser(browserCred);
}3. In Credential Repository (src/data/repositories/CredentialRepositoryImpl.ts)
async create(input: CredentialInput, vaultKey: CryptoKey): Promise<Credential> {
const credential = await this.saveCredential(input, vaultKey);
// Store in browser for autofill
if (credential.category === 'login' && credential.url) {
const browserCred = toBrowserCredential(credential, window.location.origin);
await storeCredentialInBrowser(browserCred);
}
return credential;
}- ✅ Works on all websites (not just TrustVault origin)
- ✅ Detects login forms automatically
- ✅ Shows inline autofill dropdown
- ✅ Supports dynamic forms (SPAs)
- ✅ Better UX with visual indicators
- ✅ Works offline
chrome-extension/
├── manifest.json # Extension manifest (v3)
├── popup.html # Extension popup UI
├── scripts/
│ ├── background.js # Service worker
│ ├── content.js # Content script (form detection)
│ └── popup.js # Popup logic
└── icons/
├── icon-16.png
├── icon-32.png
├── icon-48.png
└── icon-128.png
- Build Extension Icons
# Copy from PWA icons
cp public/pwa-192x192.png chrome-extension/icons/icon-128.png
# Or generate specific sizes
npm run generate-extension-icons- Load Extension in Chrome
1. Open chrome://extensions/
2. Enable "Developer mode"
3. Click "Load unpacked"
4. Select chrome-extension/ directory
- Test Autofill
1. Navigate to any login page (e.g., github.com)
2. Click on username field
3. See TrustVault autofill dropdown
4. Select credential to autofill
Background Service Worker (scripts/background.js)
- Manages credential storage
- Communicates with TrustVault PWA
- Handles cross-origin messaging
- Syncs credentials
Content Script (scripts/content.js)
- Detects login forms
- Injects autofill UI
- Fills credentials
- Watches for dynamic forms
Popup (popup.html + scripts/popup.js)
- Shows credentials count for current site
- Autofill settings
- Quick access to TrustVault PWA
# Enable autofill features
VITE_AUTOFILL_ENABLED=true
# TrustVault origin for extension communication
VITE_TRUSTVAULT_ORIGIN=https://trust-vault-pwa.vercel.appSettings are stored in localStorage with key trustvault_autofill_settings:
{
enabled: boolean; // Master toggle
autoSubmit: boolean; // Auto-submit after fill (default: false)
requireConfirmation: boolean; // Ask before filling (default: true)
enableCrossDomain: boolean; // Allow subdomain matching (default: true)
onlyHTTPS: boolean; // HTTPS only (default: true)
excludedOrigins: string[]; // Blacklist
}Add to PWA navigation:
// src/presentation/App.tsx
<Route path="/settings/autofill" element={<AutofillSettingsPage />} />// SECURE: Exact origin match
if (validateOrigin(credential.url, window.location.origin)) {
// Safe to autofill
}
// RISKY: Domain-only match (requires user confirmation)
if (validateDomain(credential.url, window.location.origin)) {
// Show confirmation dialog first
}// Never autofill on HTTP (unless explicitly allowed)
if (settings.onlyHTTPS && !window.location.origin.startsWith('https://')) {
console.warn('Autofill blocked: HTTP site');
return;
}Add to vercel.json:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; connect-src 'self' https://api.pwnedpasswords.com; script-src 'self'; style-src 'self' 'unsafe-inline';"
}
]
}
]
}- No External API Calls - Credentials never sent to external servers
- Same-Origin Enforcement - Browser enforces origin isolation
- No console.log() - Never log sensitive data in production
- Content Security Policy - Prevents injection attacks
1. User navigates to TrustVault PWA
2. User adds credential for github.com
3. TrustVault stores in IndexedDB (encrypted)
4. TrustVault calls navigator.credentials.store()
5. Browser saves credential for github.com origin
6. User navigates to github.com/login
7. Browser shows "Use password from TrustVault" suggestion
8. User clicks → credentials autofill
1. User installs TrustVault Autofill extension
2. Extension syncs credentials from PWA
3. User navigates to github.com/login
4. Content script detects login form
5. User focuses on username field
6. Extension shows inline dropdown with matches
7. User selects credential
8. Extension fills username + password
9. User clicks login
1. User has credential for login.example.com
2. User visits www.example.com
3. Domain matches (example.com)
4. TrustVault shows confirmation dialog
5. User confirms → credentials autofill
- Add Test Credential
Title: GitHub Test
Username: testuser
Password: Test123!@#
URL: https://github.com/login
- Navigate to GitHub Login
https://github.com/login
- Expected Behavior
- Native API: Browser shows autofill suggestion in dropdown
- Extension: TrustVault dropdown appears on focus
test('autofill works on GitHub', async ({ page }) => {
// Add credential in TrustVault
await page.goto('http://localhost:3000/credentials/add');
await page.fill('[name="title"]', 'GitHub Test');
await page.fill('[name="username"]', 'testuser');
await page.fill('[name="password"]', 'Test123!@#');
await page.fill('[name="url"]', 'https://github.com/login');
await page.click('button[type="submit"]');
// Navigate to GitHub
await page.goto('https://github.com/login');
// Trigger autofill
await page.click('#login_field');
// Verify autofill
const username = await page.inputValue('#login_field');
expect(username).toBe('testuser');
});| Feature | Chrome | Edge | Opera | Firefox | Safari |
|---|---|---|---|---|---|
| Credential Management API | ✅ | ✅ | ✅ | ❌ | ❌ |
| Chrome Extension (Manifest v3) | ✅ | ✅ | ✅ | ❌ | |
| Auto-submit Prevention | ✅ | ✅ | ✅ | ✅ | ✅ |
# Build with autofill enabled
VITE_AUTOFILL_ENABLED=true npm run build
# Deploy to Vercel
vercel deploy --prod- Create ZIP
cd chrome-extension
zip -r trustvault-autofill.zip . -x "*.DS_Store" -x "__MACOSX/*"- Upload to Chrome Web Store
https://chrome.google.com/webstore/devconsole
- Set Privacy Policy
Privacy policy must explain:
- What credentials are stored
- How origin validation works
- That data never leaves user's device
- HTTPS-only enforcement
Check:
- Is autofill enabled in settings?
- Is the site HTTPS?
- Does credential URL match current origin?
- Is Credential Management API supported?
Debug:
import { isCredentialManagementSupported } from '@/core/autofill/credentialManagementService';
console.log('Supported:', isCredentialManagementSupported());
console.log('Origin:', window.location.origin);
console.log('Settings:', loadAutofillSettings());Check:
- Is extension enabled in chrome://extensions/?
- Are there console errors in background service worker?
- Is content script injected? (Check in DevTools → Sources)
Debug:
// In chrome://extensions/ → Inspect views: service worker
chrome.storage.local.get(null, (data) => {
console.log('Extension data:', data);
});// Check browser support
isCredentialManagementSupported(): boolean
// Store credential for specific origin
storeCredentialInBrowser(credential: BrowserCredential): Promise<boolean>
// Get credential with optional mediation
getCredentialFromBrowser(): Promise<{ id: string; password: string } | null>
// Get credential with required mediation (shows UI)
getCredentialWithUI(): Promise<{ id: string; password: string } | null>
// Find matching credentials for current origin
findMatchingCredentials(credentials: Credential[], currentOrigin: string): AutofillMatch[]
// Validate exact origin match
validateOrigin(credentialUrl: string, currentOrigin: string): boolean
// Validate domain match (cross-subdomain)
validateDomain(credentialUrl: string, currentUrl: string): boolean
// Prevent silent credential access (after logout)
preventSilentAccess(): Promise<void>// Load settings from localStorage
loadAutofillSettings(): AutofillSettings
// Save settings to localStorage
saveAutofillSettings(settings: AutofillSettings): void
// Check if autofill enabled for origin
isAutofillEnabledForOrigin(origin: string, settings?: AutofillSettings): boolean
// Add/remove origin from exclusion list
excludeOrigin(origin: string): void
includeOrigin(origin: string): void
// Toggle autofill globally
toggleAutofill(enabled: boolean): voidTo add autofill support for a new browser:
- Research browser's credential management capabilities
- Implement adapter in
src/core/autofill/adapters/ - Add browser detection
- Update documentation
MIT License - See main project LICENSE file