Skip to content

Authentication

All auth lives under client.auth.zk. Identity is derived from (username, password) on the device and proven with a zero-knowledge proof — see Identity & app keys for the model.

const user = await client.auth.zk.register({
username: "ada",
password: "correct horse battery staple",
email: "ada@example.com", // optional
});

By default register also signs the user in (returns { username, commitment } and establishes a session). Pass login: false to register without signing in.

const user = await client.auth.zk.login("ada", "correct horse battery staple");
// { username: "ada", commitment: "1481…" }

On success the client holds both the session token and the derived identity, so storage and messaging work immediately.

await client.auth.zk.login("ada", pw, { rememberMe: true }); // longer-lived session

With a persisted sessionStore, call restore() on boot. It validates the stored token with the server and returns the user, or null (clearing a stale token):

const user = await client.auth.zk.restore();
if (user) {
// Authenticated — token-gated calls (most of storage) work.
}
client.user; // { username, commitment } | null (sync)
client.isAuthenticated; // boolean (sync)
await client.auth.zk.logout(); // clears session + identity + persisted token

Because identity is deterministic in (username, password), signing in on a second device reproduces the same identity and commitment — the user transparently sees the same data. There’s no key export/import step.

Login throws on failure; the message tells you what to surface:

try {
await client.auth.zk.login(username, password);
} catch (err) {
const msg = (err as Error).message;
if (/commitment mismatch|incorrect password/.test(msg)) {
// Wrong password — or a legacy/incompatible account.
} else if (/User not found/.test(msg)) {
// No account with that username.
} else if (/challenge/i.test(msg)) {
// Auth challenge expired/replayed — just retry.
}
}

See client.auth reference for the full method list.