Core concepts
Authentication
Passkeys are the recommended production primary factor. ZKAuth also exposes an experimental Groth16 password path and preview OPAQUE helpers for teams evaluating different credential models.
Experimental zero-knowledge password proofs
The experimental method. The client turns the password into a zero-knowledge proof and sends the proof plus its public signals. The engine verifies it with a Groth16 verifier and stores a verifier value, never the password or a reversible hash of it.
// The client derives the proof locally; the server only verifies it.await zkauth.register({ email, password, deviceInfo })const session = await zkauth.login({ email, password, deviceInfo })Each attempt begins with a short-lived server challenge. The circuit binds the stored commitment, tenant, email, challenge, purpose, version, and expiry; the engine atomically consumes the ceremony when the authentication decision is committed. A tampered or replayed proof is rejected.
Project-bound hosted sign-up/sign-in helper pages use the same proof boundary through the hosted proxy. They can register accounts and verify engine login without exposing project API keys.
- After engine sign-in, hosted UI can return a single-use handoff code to an allowlisted callback for server-side redemption.
- The hosted account security helper can use the in-memory bearer token for account and security controls.
Experimental production boundary
OPAQUE preview helpers
OPAQUE is a password-authenticated key exchange (PAKE). The server stores an opaque credential it cannot use to recover the password, even offline.
Because OPAQUE runs in the browser, route its steps through a backend proxy so your project key stays server-side.
// app/api/zkauth/opaque/[...path]/route.ts (your backend)// Forwards browser OPAQUE steps to the engine with your project key,// so the key is never exposed to client code.export const POST = (req: Request) => fetch(`${loadServerOnlyApiBaseUrl()}/api/v1/opaque/${path}`, { method: 'POST', headers: { 'x-api-key': loadServerOnlyProjectKey(), 'content-type': 'application/json' }, body: await req.text(), })Built-in proxy
/api/zkauth/opaque/[...path] and /api/zkauth/webauthn/[...path] as a working reference for this pattern.Passkey and WebAuthn helper flows
The engine issues registration and authentication options, verifies the authenticator’s response, and applies account, device, MFA, risk, and session policy before issuing a session. This is the recommended production primary-factor direction within the documented origin boundary.
- Registration requires both the project key and an active user bearer session. User ID, email, and tenant identity come from that session, never from the registration request body. Registration and passkey revocation also require a recent verified sign-in.
- The hosted account helper lists bounded metadata for the signed-in user’s passkeys and can revoke one opaque passkey ID. The API does not return credential IDs, public keys, or signature counters. Revocation then attempts a security email; delivery failure is logged and cannot restore the credential.
- Ceremonies require the relying-party ID and origin to match the domain serving them.
- Treat this as a helper surface, not a broad hardware compatibility guarantee.
ZKAuth also has hosted passkey sign-in for credentials registered on the hosted WebAuthn origin. It supports project-required MFA, credential inventory and revocation, and the single-use application handoff. Hosted custom-domain RP activation is not public yet; it requires verified domain ownership, TLS, explicit proxy pinning, and credential re-registration.
Current accounts retain another credential or recovery path, so users can revoke their final passkey if an authenticator is lost or stolen. Passkey-only signup is not supported until a dedicated recovery policy and evidence exist.
# Customer data-plane / self-hosted engine configurationWEBAUTHN_RP_ID=your-app-domain.comWEBAUTHN_ORIGIN=https://your-app-domain.comOrigin must match
WEBAUTHN_RP_ID / WEBAUTHN_ORIGIN don’t match the domain the browser flow runs on. These variables are customer-data-plane or self-hosted settings; they are not per-project overrides on the hosted service.Sessions
A successful login returns a bearer session token. Keep it server-side when you can, read the current user and session metadata through /auth/me, rotate active tokens with /auth/refresh, and revoke the current token with logout.
const res = await zkauth.login({ email, password, deviceInfo })
// The token is on the instance; also store it in an http-only, secure cookie.const token = res.data.session.tokenconst current = await zkauth.getCurrentUser()await zkauth.logout() // revokes the current sessionSession listing lives under /security/sessions and includes device, last-activity, expiry, MFA, and sensitive-session metadata. Account stats and recent login activity are available through bearer dashboard endpoints and are used by the hosted account helper.
Terminating all other sessions keeps the current session active, and the engine requires a fresh first-factor session for that account-wide action. Use logout to revoke the current one.
Replay protection & trusted devices
- Proof freshness: each server-issued ceremony is single-use; replays are rejected by an atomic database consume.
- Tamper rejection: modified proofs fail verification.
- Trusted devices:
deviceInforegisters a device fingerprint; mismatches can be challenged with separate single-use approval and denial links.
For the exact guarantees and the boundaries we don’t cross, read Security and Limitations.