Guides

Examples

Patterns you can adapt. The rule across all of them: the API key and session token live on the server; the browser only ever talks to your backend.

Adapt, don't copy blindly. These show the shape of an integration with zkauth-client. Match field names to the responses in the API reference.

Next.js: server route + cookie

Do the login on the server and store the session in an http-only cookie. The key never reaches the client.

app/api/login/route.tsts
// app/api/login/route.tsimport { cookies } from 'next/headers'import { ZKAuthSDK } from 'zkauth-client'
const zkauth = new ZKAuthSDK({ apiKey: process.env.ZKAUTH_API_KEY! })
export async function POST(req: Request) {  const { email, password } = await req.json()  const res = await zkauth.login({    email,    password,    deviceInfo: { deviceName: 'Web', deviceType: 'desktop' },  })
  // Keep the token server-side in an http-only cookie.  cookies().set('zkauth_session', res.data.session.token, {    httpOnly: true,    secure: true,    sameSite: 'lax',    path: '/',  })
  return Response.json({ user: res.data.user })}

React: a login form

The form posts credentials to your own route above; it never sees a ZKAuth key.

login-form.tsxtsx
'use client'import { useState } from 'react'
export function LoginForm() {  const [error, setError] = useState('')
  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {    e.preventDefault()    const form = new FormData(e.currentTarget)    const res = await fetch('/api/login', {      method: 'POST',      headers: { 'content-type': 'application/json' },      body: JSON.stringify({        email: form.get('email'),        password: form.get('password'),      }),    })    if (res.ok) location.href = '/dashboard'    else setError('Invalid credentials')  }
  return (    <form onSubmit={onSubmit}>      <input name="email" type="email" required />      <input name="password" type="password" required />      <button type="submit">Sign in</button>      {error && <p role="alert">{error}</p>}    </form>  )}

Express: protect a route

Validate the session cookie on each protected request by calling the engine’s /auth/mewith the user’s bearer token.

server.tsts
import express from 'express'
const BASE = process.env.ZKAUTH_BASE_URL ?? 'https://api.zkauth.dev'const app = express()
// A server validates many users' tokens, so check each one against the// engine's /auth/me rather than the SDK's single in-memory session.async function requireUser(req, res, next) {  const token = req.cookies?.zkauth_session  if (!token) return res.status(401).json({ error: 'unauthenticated' })
  const r = await fetch(`${BASE}/api/v1/auth/me`, {    headers: {      'x-api-key': process.env.ZKAUTH_API_KEY,      Authorization: `Bearer ${token}`,    },  })  if (!r.ok) return res.status(401).json({ error: 'invalid session' })
  req.user = (await r.json()).data?.user  next()}
app.get('/api/profile', requireUser, (req, res) => res.json({ user: req.user }))

Other languages

No SDK for your stack yet? Every example above maps to plain HTTPS calls with an x-api-key header. See the API reference for the request and response shapes.