Skip to content

pulkitpareek18/Z_Auth

Repository files navigation

Z Auth

Z Auth

Privacy-first identity platform with zero-knowledge biometric authentication

Deploy Release License OIDC

Indian Patent Granted • Application No. 202311041001 • US Patent Filed


Z Auth combines WebAuthn passkeys, face-based liveness verification, and Groth16 zero-knowledge proofs into a fully standards-compliant OpenID Connect provider. Raw biometric data never leaves the user's device — the server receives only irreversible cryptographic commitments and verifiable proofs.

Pramaan (Sanskrit: proof/evidence) is the identity verification protocol at the heart of Z Auth.

Key Features

  • Zero-Knowledge Biometric Proofs — Real Groth16 circuits (Circom + snarkjs) prove identity without revealing biometric data. The server verifies a Poseidon commitment, never the face itself.
  • Client-Side Face Matching — Face embeddings are extracted, quantized, and matched entirely in the browser via face-api.js. Only a SHA-256 hash of the quantized embedding is transmitted.
  • Cross-Device ZK Authentication — Enrollment hash is securely provided during authentication challenges, enabling seamless ZK proof generation on new devices without local biometric storage.
  • WebAuthn Passkeys — FIDO2 discoverable credentials eliminate passwords. No server-side secrets, no phishing vectors.
  • Standards-Compliant OIDC — Full OAuth 2.0 Authorization Code flow with PKCE (S256), token refresh, revocation, and a signed JWKS endpoint.
  • On-Chain ZK Verification (Base L2) — Every enrollment and verification is submitted directly to a Groth16 verifier contract on Base Sepolia. Identity commitments are stored immutably on-chain with full proof verification.
  • Blockchain Audit Anchoring — Hash-chained audit events with optional Merkle root anchoring and metadata pinning to IPFS.
  • Single-VPS Deployment — Runs on a single 2 vCPU / 8 GB VM behind Caddy with automatic TLS.

How It Works

User Device                           Z Auth Server                   Base L2 (Sepolia)
┌──────────────────┐                 ┌──────────────────┐            ┌───────────────────┐
│ 1. Face capture   │                │                  │            │ ZAuthIdentity.sol  │
│ 2. Client-side    │  SHA-256 hash  │ Verify biometric │  Enroll   │ + Groth16Verifier  │
│    embedding +    ├───────────────>│ commitment       ├──────────>│                   │
│    face matching  │                │                  │  Verify   │ On-chain ZK proof  │
│ 3. Groth16 proof  │  ZK proof +   │ groth16.verify() ├──────────>│ verification +     │
│    generation     │  public signals│ Poseidon check   │            │ identity storage   │
│ 4. Passkey sign   ├───────────────>│                  │            │                   │
│                   │                │ Issue OIDC tokens│            │                   │
│                   │<───────────────┤                  │            │                   │
└──────────────────┘  access_token   └──────────────────┘            └───────────────────┘
                      id_token

Privacy invariant: Raw biometric descriptors never leave the user's device. The server receives only:

Data Purpose Reversible?
biometric_hash SHA-256 of quantized face embedding No
zk_proof Groth16 proof binding biometric to challenge No
public_signals Poseidon commitment + challenge binding No
passkey_assertion WebAuthn signature from secure enclave No

Architecture

apps/
├── zauth-core/              # OAuth/OIDC server + Pramaan V2 identity engine
│   ├── src/routes/          # OIDC, Pramaan, WebAuthn, admin, liveness endpoints
│   ├── src/services/        # ZK verification, passkey, sessions, audit, anchoring
│   ├── zk/                  # Groth16 circuit artifacts
│   │   ├── biometric_commitment.circom    # Poseidon(preimage) + challenge binding
│   │   ├── biometric_commitment.wasm      # Client-side witness generator
│   │   ├── circuit_final.zkey             # Proving key (client-side)
│   │   └── verification_key.json          # Verification key (server-side)
│   ├── contracts/           # ZAuthIdentity.sol (Base Sepolia) + ZAuthAnchor.sol
│   └── assets/              # Static assets (logo, fonts)
├── zauth-ui/                # Admin console + status dashboard
└── zauth-notes/             # Reference relying-party app (OAuth client)
    ├── src/                 # Express backend (OAuth callback, PostgreSQL)
    └── web/                 # React 18 + Vite frontend

packages/
└── sdk/                     # @zauth/sdk — OIDC client library

docker/
├── compose.base.yml         # Service definitions (Postgres 16, Redis 7, apps)
├── compose.dev.yml          # Hot reload, local ports, debug logging
├── compose.prod.yml         # Caddy TLS, health checks, read-only FS
└── compose.test.yml         # Ephemeral CI stack

Technology Stack

Layer Technology
Authentication WebAuthn / FIDO2 passkeys (@simplewebauthn/server)
Biometrics face-api.js (client-side only), SHA-256 commitments
Zero-Knowledge Circom 2.1.9, Groth16 via snarkjs, Poseidon hash
Identity Protocol OAuth 2.0 / OpenID Connect with PKCE S256
Blockchain Base Sepolia (L2), Solidity 0.8.24, ethers.js v6
On-Chain Verification Groth16Verifier.sol — ZK proof verification directly on Base
Storage IPFS via Pinata REST API
Backend Node.js 20, Express, TypeScript
Frontend React 18, Vite, CSS design tokens (light + dark mode)
Database PostgreSQL 16 (append-only audit model)
Cache Redis 7 (sessions, challenges, handoff state)
Reverse Proxy Caddy 2 (automatic TLS via Let's Encrypt)
CI/CD GitHub Actions, GHCR images, Trivy vulnerability scanning

Quick Start

# Clone and configure
git clone https://github.com/pulkitpareek18/Z_Auth.git
cd Z_Auth
cp env/.env.dev.example env/.env.dev

# Start the development stack
make up-dev

# Services available at:
#   Auth server   → http://localhost:3000/ui/login
#   Demo app      → http://localhost:3001
#   Notes app     → http://localhost:5173

Live Instance

Service URL
Auth Server auth.geturstyle.shop
Notes App notes.geturstyle.shop
Admin Console console.geturstyle.shop
OIDC Discovery auth.geturstyle.shop/.well-known/openid-configuration

Authentication Flow

Pramaan V2 — ZK Biometric Authentication (AAL2)

All authentication flows require face verification with zero-knowledge proof generation (AAL2:zk assurance level). The primary flow uses cross-device QR handoff:

Desktop                    Phone                      Server
  │                          │                          │
  ├─ POST /auth/handoff/start ─────────────────────────>│
  │<── QR code + handoff_id ────────────────────────────┤
  │        │                                            │
  │   Scan QR                                           │
  │        ├─ Face liveness challenge ─────────────────>│
  │        │<── [blink, turn_left, turn_right] ─────────┤
  │        ├─ Liveness frames ─────────────────────────>│
  │        ├─ Passkey assertion ───────────────────────>│
  │        ├─ Groth16 proof + public signals ──────────>│
  │        ├─ POST /auth/handoff/approve ──────────────>│
  │        │                                            │
  │ ← Poll (approved) → session + consent redirect ────┤

Cross-device support: When authenticating from a new device (no local enrollment data), the server securely provides the enrollment hash during the challenge phase so the ZK proof can be generated with the correct Poseidon preimage.

Account Recovery

  1. Enter one of eight recovery codes generated at enrollment
  2. Verify biometric commitment or provide three-of-eight codes
  3. Old passkeys revoked, new passkey + recovery codes issued

Z Notes — Reference Application

Z Notes is a full-featured notes app that demonstrates Z Auth integration as a relying party. It showcases the complete OAuth 2.0 + PKCE flow with ZK biometric authentication.

Stack: React 18 + Vite + TypeScript frontend, Express + PostgreSQL backend

Landing page features:

  • Professional conversion-focused design with dark mode support
  • "How It Works" visual flow showing the 3-step ZK authentication process
  • Security deep-dive section with ZK flow diagram and privacy data comparison
  • Patent and trust section highlighting Indian Patent 202311041001
  • Full responsive design (mobile, tablet, desktop)

API Reference

OIDC Endpoints

Method Path Description
GET /.well-known/openid-configuration Discovery document
GET /.well-known/jwks.json JSON Web Key Set
GET POST /oauth2/authorize Authorization endpoint (PKCE S256)
POST /oauth2/token Token exchange
POST /oauth2/revoke Token revocation
GET /oauth2/userinfo User claims (sub, acr, amr, uid, did)

Pramaan V2 Identity

Method Path Description
POST /pramaan/v2/enrollment/start Begin identity enrollment
POST /pramaan/v2/enrollment/complete Finalize with ZK proof
POST /pramaan/v2/proof/challenge Request authentication challenge (includes enrollment hash)
POST /pramaan/v2/proof/submit Submit Groth16 proof for verification
GET /pramaan/v2/identity/me Current identity context

Liveness & Handoff

Method Path Description
POST /auth/handoff/start Initiate QR cross-device handoff
GET /auth/handoff/status Poll handoff state
POST /auth/handoff/approve Phone-side approval
POST /auth/liveness/challenge Start liveness challenge
POST /auth/liveness/verify Submit liveness result

Health

Method Path Description
GET /health/live Liveness probe
GET /health/ready Readiness (DB + Redis)
GET /health/deps Dependency status

SDK

import { ZAuthClient } from "@zauth/sdk";

const client = new ZAuthClient({
  issuer: "https://auth.geturstyle.shop",
  clientId: "my-app",
  redirectUri: "https://myapp.com/callback",
  scopes: ["openid", "profile", "zauth.identity"],
});

// Start authorization flow
const { url, state, codeVerifier } = await client.authorize();
window.location.href = url;

// Handle callback
const { code } = client.parseCallback(window.location.search);
const tokens = await client.exchangeCode(code, codeVerifier);
const user = await client.getUserInfo(tokens.access_token);
// → user.sub, user.uid, user.did, user.acr, user.amr

Security Model

Property Implementation
No biometric templates server-side Face matching is client-side only; server stores SHA-256 hashes
Zero-knowledge identity proofs Groth16 circuit with Poseidon commitment binding
No passwords WebAuthn discoverable credentials (passkeys)
Cross-device ZK support Enrollment hash provided in challenge for new-device authentication
On-chain ZK verification Groth16 proofs verified on Base Sepolia via ZAuthIdentity.sol
Tamper-evident audit trail SHA-256 hash-chained events, on-chain proof events (ProofVerified)
Nullifier-based consumption Recovery codes and proof requests use insert-only nullifiers
PKCE S256 enforced All OAuth flows require proof key for code exchange
Container hardening Read-only filesystem, memory limits, Trivy scanning
CSP + Helmet Strict Content Security Policy with form-action restrictions

See docs/THREAT_MODEL.md for the full threat model.

Deployment

Production (Single VPS)

# 1. Point DNS A records for all subdomains → VPS IP
# 2. Configure firewall
make vps-setup

# 3. Deploy
make release

Environment Strategy

Environment Compose File Features
dev compose.dev.yml Hot reload, local ports, debug logging
test compose.test.yml Ephemeral CI stack, integration tests
prod compose.prod.yml Caddy TLS, health checks, read-only FS

CI/CD

Workflow Trigger Actions
ci-pr.yml Pull request Lint, type-check, test, build, Trivy scan
ci-main.yml Manual Full test suite, build, security scan
deploy-demo.yml Manual Deploy to demo environment
deploy-prod.yml Push to main / tag Build, deploy to VPS, smoke test

On-Chain Identity (Base L2)

Every enrollment and verification is submitted directly to the Base Sepolia L2 chain. The ZAuthIdentity.sol contract inherits a Groth16 verifier generated from the ZK circuit, enabling full on-chain proof verification.

How It Works

  1. Enrollment — When a user enrolls, their ZK proof is verified on-chain via enrollIdentity(). The contract stores the identity commitment (Poseidon hash), commitment root, and version.
  2. Verification — During login, the ZK proof is submitted to verifyAndLog(), which re-verifies the proof on-chain and emits a ProofVerified event for an immutable audit trail.
  3. Identity LookupgetIdentity() is a free view call that retrieves the on-chain identity record for cross-device verification.

Smart Contracts

Contract Chain Purpose
ZAuthIdentity.sol Base Sepolia On-chain identity registry + Groth16 proof verification
Groth16Verifier.sol Base Sepolia Auto-generated BN128 verifier (inherited by ZAuthIdentity)
Identity Contract: 0x34E3dd36326B8360B161Cd8DEc33f71821E33797

Patent

Indian Patent Granted — Application No. 202311041001
US Patent Filed
Title A system for performing person identification using biometric data and zero-knowledge proof in a decentralized network
Applicant Yushu Excellence Technologies Private Limited

License

Apache License 2.0

About

Privacy-first OAuth 2.0 / OpenID Connect identity platform with WebAuthn passkeys, zero-knowledge biometrics, and blockchain audit anchoring.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors