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 handlersBasic 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.