enbox docs
Guides

Authentication

How authentication works in Enbox — connect, lock, disconnect, and multi-identity flows.

Overview

@enbox/auth provides the AuthManager class that handles the full authentication lifecycle for Enbox applications. It manages:

  • Identity creation and agent bootstrapping
  • Wallet-based connection for cross-device identity sharing
  • Vault locking and session restoration with a passphrase
  • Multi-identity support (switching between DIDs)
  • DWN registration with remote nodes

Installation

npm install @enbox/auth @enbox/agent
npm install @enbox/browser # for browser wallet-connect handlers

Basic usage

Most applications should let a connection store own auth and its session-bound API facade:

import { createConnectionStore, defineApplicationManifest } from '@enbox/browser';

const application = defineApplicationManifest({ protocols: [AppProtocol] } as const);
const store = createConnectionStore({ application, password: userPassword });
let snapshot = await store.initialize();
if (snapshot.phase === 'disconnected') {
  snapshot = await store.connectVault({ createIdentity: true });
}
if (snapshot.phase !== 'connected') {
  throw snapshot.error ?? new Error('Connection was not established.');
}

console.log(snapshot.session.did); // 'did:dht:...'
const enbox = snapshot.enbox;

// ...use enbox...

// On sign-out and application shutdown:
await store.disconnect();
await store.dispose();

Use AuthManager directly only when an integration needs to own the auth lifecycle itself. The remaining examples describe that advanced layer.

Connection flows

1. First-time setup

When no identity exists, connectVault({ createIdentity: true }) creates a new DID, generates encryption keys, and bootstraps the agent with a local DWN.

2. Returning user

If an identity already exists in the vault, connectVault() unlocks the vault and resumes the session.

3. Wallet connect

For cross-device identity sharing, configure an AuthManager with a connectHandler and request the protocols you need. The handler drives the permission-scope UX (e.g. via a browser overlay) and returns a delegate session bound to the connecting wallet.

import { AuthManager } from '@enbox/auth/browser';
import { BrowserConnectHandler } from '@enbox/browser';

const walletAuth = await AuthManager.create({
  connectHandler: BrowserConnectHandler({ appName: 'Notes App' }),
});

const session = await walletAuth.connect({ protocols: [NotesProtocol] });

4. Grant expiry and refresh

Wallet connections use delegated grants with a fixed expiry. Inspect the newest complete approval before an operation, or recognize an expired/revoked grant after a DWN request fails:

import { isSessionInvalidError } from '@enbox/auth/browser';

const status = await walletAuth.getConnectionStatus();

if (status.state === 'expiring-soon' || status.state === 'expired') {
  await walletAuth.refresh({ protocols: [NotesProtocol] });
}

try {
  await writeNote();
} catch (error) {
  if (isSessionInvalidError(error)) {
    await walletAuth.refresh({ protocols: [NotesProtocol] });
  } else {
    throw error;
  }
}

refresh() opens the configured connect handler for fresh consent and grants to the same delegate DID; it does not replace the delegate's local keys. Pass the protocols your app still needs. Revocation status is best-effort and reflects revocations that have reached the local agent.

For proactive UX, opt in to polling. Automatic refresh can prompt for consent when a session is expiring or expired, but never for a revoked session:

const stopMonitoring = walletAuth.startConnectionMonitor({
  intervalMs: 5 * 60 * 1000,
  autoRefresh: { protocols: [NotesProtocol] },
  onError: error => reportConnectionError(error),
});

walletAuth.on('connection-expiring', ({ status }) => {
  showRenewalNotice(status.expiresAt);
});

walletAuth.on('connection-expired', ({ status }) => {
  showDisconnectedNotice(status.state);
});

// Call when this UI no longer needs connection monitoring.
stopMonitoring();

5. Disconnect

Direct auth consumers must close the Enbox facade they created from the session, then end and dispose the auth lifecycle:

import { Enbox } from '@enbox/api';

const enbox = Enbox.fromSession(session);

// ...use enbox...

enbox.close();
await auth.disconnect();
console.log(auth.state); // 'unlocked'
await auth.shutdown();

6. Lock / Unlock

await auth.lock();
console.log(auth.state); // 'locked'

await auth.restoreSession({ password: 'my-passphrase' });
console.log(auth.state); // 'connected'

Events

The auth manager emits state change events. The handler receives a payload with the previous and current states:

auth.on('state-change', ({ previous, current }) => {
  console.log(`Auth state changed: ${previous} → ${current}`);
});

Further reading

See the full AUTH.md in the Enbox repository for the state model, connection paths, sync scoping, and registration notes.

On this page