Saved-password store

Read, add, clear, and observe Chromium Profile passwords from a Provision Service Worker.

Entitlement: passwords. API: chrovia.passwords. Available only in a Provision extension Service Worker, even with unrestrictedApi. Observing the message channel also needs messaging.

Add and list test credentials

Use a disposable Profile. This adds actual password-store rows; it is not temporary form filling:

async function addTestLogin() {
  const passwords = globalThis.chrovia?.passwords;
  if (!passwords) {
    throw new Error('passwords is required in a Provision Service Worker');
  }
  await passwords.add([{
    origin: 'https://example.com/login',
    signon_realm: 'https://example.com/',
    username: 'sdk-test-user',
    password: 'replace-with-a-test-password',
  }]);
  const rows = await passwords.list();
  console.log('Saved credential count:', Array.isArray(rows) ? rows.length : 0);
}

Call from a deliberate test command, not on every worker wake. add() accepts the array directly, not {credentials: [...]}. Each item requires a valid origin URL and non-empty string username and password. signon_realm is optional; missing/empty uses the origin's sign-on realm. Use real HTTP(S) login URLs and a matching realm.

Methods

MethodBehavior
add(credentials)Adds saved passwords; Promise resolves to undefined
list()Promise of exported credential objects with origin, signon_realm, username, password
empty()Promise resolving to undefined; requests clearing the Profile password store
watch()Promise resolving to undefined; attaches change observation to the caller's current store

list() excludes blocked entries, federated entries, empty usernames and empty passwords. Its results contain plaintext secrets: do not print the list to logs or expose it through an unrestricted popup/page command. An empty result can also reflect a read failure or unavailable store; it is not a health certificate.

add() does not make the saved-password list equal to the provided array and does not reliably update an existing login by site and username alone. Invalid items are skipped. Within one input array, repeated realm/username pairs are added only once. Existing logins with different origin or form details can remain as separate entries. Use a test profile and inspect the resulting list before relying on replacement behavior.

Observe changes

Register the listener before calling watch() each worker start:

const sdk = globalThis.chrovia;
if (sdk?.passwords && sdk.messaging) {
  sdk.messaging.on('chrovia.passwords.changed', () => {
    console.log('Password store changed; schedule a controlled refresh');
  });
  sdk.passwords.watch().catch(console.error);
}

The event is an empty object announcing a change, not a password payload. Call watch() to enable notifications; list(), add(), and subscribing to the channel alone do not enable them. Repeat watch() on every Provision Service Worker start so notifications resume when a profile is reopened. Worker sleep alone does not stop an existing watch.

Writes from your plugin can produce changes too. Avoid an echo loop where every change triggers a write followed by another change. Batch application-side refreshes and keep secrets in your controlled storage boundary.

Clearing and startup import

empty() is destructive and separate from add(). Its Promise does not report whether clearing succeeded, so resolution does not prove every password was deleted. Verify the result before importing a replacement list or uploading local passwords to a managed vault; a later add() does not remove leftovers from a failed clear. Remember that list() can also return an empty result on a read failure.

Already-open login pages can continue offering old passwords after an import. Complete planned startup imports before opening login pages, or reload affected pages before testing autofill and save prompts.

Import saved passwords with add(); adding passwords to EP alone does not import them. For configuration-based autofill, see injected credentials. This API does not manage TOTP or WebAuthn passkeys.