Skip to content

docs: add browser dApp guide for levelPrivateStateProvider with canary password verification#1126

Open
pplmaverick wants to merge 1 commit into
midnightntwrk:mainfrom
pplmaverick:main
Open

docs: add browser dApp guide for levelPrivateStateProvider with canary password verification#1126
pplmaverick wants to merge 1 commit into
midnightntwrk:mainfrom
pplmaverick:main

Conversation

@pplmaverick

Copy link
Copy Markdown

Summary

Adds a browser usage guide for levelPrivateStateProvider, covering:

  1. How cryptoBackend auto-detection works in browser environments
  2. Why a canary pattern is needed for early password validation
  3. How to implement resetStorage() for production UX

Motivation

Browser compatibility was added in midnight-js #827, but no documentation or example
exists showing how to use levelPrivateStateProvider in a browser dApp.

Additionally, the SDK's internal verifyPassword() only performs an in-memory cache
consistency check — it does not validate the password against stored encrypted data.
On first unlock (or after page reload), there is no password verification at all.
A wrong password causes an AES-GCM auth tag failure deep inside a later operation,
producing an opaque WebCrypto OperationError instead of a clear message at unlock time.

The canary pattern below provides fail-fast password validation at unlock time.

Proposed content

Using levelPrivateStateProvider in browser dApps

import { levelPrivateStateProvider }
  from '@midnight-ntwrk/midnight-js-level-private-state-provider';

// cryptoBackend auto-detects Web Crypto availability.
// In browsers, 'webcrypto' is selected automatically via isWebCryptoAvailable().
// Set it explicitly if you need predictable behavior across environments.
const provider = levelPrivateStateProvider({
  accountId: address,
  cryptoBackend: 'webcrypto', // explicit, recommended for browser dApps
});

Canary pattern for early password validation

Without a canary, a wrong password isn't caught until the app attempts to decrypt
real private state data. AES-GCM auth tag verification then throws an opaque
OperationError deep inside a transaction flow rather than at unlock time.

A canary value written on first unlock lets you catch this at the earliest possible
point with a meaningful error message:

const CANARY_KEY = '__canary__';
const CANARY_VALUE = 'midnight-verified';

async function unlockWithVerification(provider) {
  try {
    const existing = await provider.get(CANARY_KEY);
    if (existing === null) {
      // First unlock: write canary
      await provider.set(CANARY_KEY, CANARY_VALUE);
    }
    // If get() resolved without throwing, AES-GCM auth tag passed —
    // password is correct.
  } catch {
    throw new Error('Incorrect password — private state could not be decrypted');
  }
}

Resetting private state

No built-in reset method exists in the official PrivateStateProvider interface.
If you need to clear all private state for an account (e.g. after a failed unlock),
implement a resetStorage() method in your provider wrapper that clears the
underlying storage for that accountId.

Tested in

…y password verification

Signed-off-by: pplmaverick <95532618+pplmaverick@users.noreply.github.com>
@pplmaverick pplmaverick requested review from a team as code owners July 10, 2026 05:25
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

@pplmaverick is attempting to deploy a commit to the Midnight Foundation Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant