Get started

Your first verified login

Five steps, end to end: create a project, grab a test key, and run a register/login round-trip where the password never leaves the device.

1. Create a project

Sign in to the dashboard and create a project. Each project provisions its own client on the ZKAuth engine, isolated from every other project.

Create your account ->

2. Copy your API keys

Every project ships with two keys. Use the test key while you build and the live key in production. Both are scoped to that one project and can be rotated at any time.

  • zka_test_... for local development and CI.
  • zka_live_... for production traffic.
.env.localbash
# .env.localZKAUTH_API_KEY=zka_test_your_test_key_hereZKAUTH_BASE_URL=https://api.zkauth.dev
Keep keys server-side. Treat keys like secrets. The SDK can run in browsers for demos and trusted internal apps, but production applications should put keys behind your server or a narrow backend proxy. Never commit keys or ship live keys inside a public bundle.

3. Install the client

The published JavaScript client wraps the proof handshake. (Prefer no dependency? Every step below is also a plain HTTPS call.)

bash
npm install zkauth-client# or: pnpm add zkauth-client / yarn add zkauth-client
lib/zkauth.tsts
import { ZKAuthSDK } from 'zkauth-client'
export const zkauth = new ZKAuthSDK({  apiKey: process.env.ZKAUTH_API_KEY!,  baseUrl: process.env.ZKAUTH_BASE_URL,})

4. Register a user

On registration the client derives a zero-knowledge proof from the password locally and sends only that proof. The engine stores a verifier, never the secret.

ts
const result = await zkauth.register({  email: 'ada@example.com',  password: 'SecurePassword123!',  deviceInfo: { deviceName: 'Chrome on Mac', deviceType: 'desktop' },})
console.log('created user', result.data.userId)

5. Log in and verify

Login repeats the handshake: the engine checks the proof, applies replay protection, and returns a session.

ts
const res = await zkauth.login({  email: 'ada@example.com',  password: 'SecurePassword123!',  deviceInfo: { deviceName: 'Chrome on Mac', deviceType: 'desktop' },})
// The server verified a proof, never the password.console.log('session for', res.data.user.email)const token = res.data.session.token
That's a real ZK login. No password, password hash, or reversible secret crossed the network. The server only ever saw a proof it could verify.

6. Handle email and device gates

Registration sends a verification email. Login must stay blocked until the address is verified. If the same user logs in from a new device, the engine returns a device-approval response and sends a separate approval email instead of silently trusting the device.

  • Your app should show a clear "check your email" state after registration.
  • Your callback URL should handle zkauth_action values for email verification, device approval/denial, and password reset.
  • If no safe callback is configured, ZKAuth shows hosted fallback pages instead of redirecting to an unknown URL.

Where to next