Developer Documentation

Everything you need to implement ZenoAuth

From 5-minute quick start to advanced deployment configurations. Comprehensive guides, API references, and real-world examples.

🚀

Quick Start

Get ZenoAuth running in under 5 minutes with Docker or manual installation.

Start Here
👥

SCIM Provisioning

Configure SCIM v2 user and group provisioning for enterprise identity management.

Setup SCIM
🔧

Admin Interface

Modern Next.js admin dashboard for user management, analytics, and audit logging.

Configure
📡

API Reference

Complete REST API documentation with OAuth 2.0 and OpenID Connect endpoints.

View APIs

Quick Start Guide

Option 1: Cloud SaaS (Fastest)

Cloud Setup
# Start your free trial at zenoauth.com 1. Sign up at https://zenoauth.com/signup 2. Create your first organization 3. Configure your OAuth clients # Your endpoints will be: API: https://yourorg.zenoauth.com/api Admin: https://yourorg.zenoauth.com/admin OAuth: https://yourorg.zenoauth.com/oauth

Option 2: Self-Hosted (Docker)

Self-Hosted Setup
# Download from customer portal after license purchase # Contact sales@cloudfragments.com for access # Environment setup export ZENOAUTH__DATABASE__URL="postgres://user:pass@localhost:5432/zenoauth" export ZENOAUTH__SERVER__BASE_URL="http://localhost:3051" export ZENOAUTH__LICENSE_KEY="your-license-key" # Start with Docker Compose docker-compose up -d # Services available: API Server: http://localhost:3051 Admin UI: http://localhost:3050

First Steps After Installation

  1. Access Admin Interface: Navigate to http://localhost:3050
  2. Create Admin User: Register your first admin user via the UI
  3. Register OAuth Client: Create your first OAuth client application
  4. Test Authentication: Test login via /auth/login endpoint
  5. Configure SCIM: Set up SCIM provisioning for your identity provider

API Reference

Authentication Endpoints

Core Auth APIs
# User Registration POST /auth/register Content-Type: application/json { "email": "user@example.com", "password": "secure_password", "first_name": "John", "last_name": "Doe" } # User Login POST /auth/login Content-Type: application/json { "email": "user@example.com", "password": "secure_password" } # Response { "access_token": "eyJ...", "refresh_token": "rt_...", "expires_in": 3600, "token_type": "Bearer" }

OAuth 2.0 Endpoints

OAuth 2.0 APIs
# Authorization Endpoint GET /oauth/authorize? response_type=code& client_id=your_client_id& redirect_uri=https://yourapp.com/callback& scope=openid profile email& state=random_state # Token Endpoint POST /oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=auth_code_from_callback& client_id=your_client_id& client_secret=your_client_secret& redirect_uri=https://yourapp.com/callback # Token Response { "access_token": "eyJ...", "id_token": "eyJ...", "refresh_token": "rt_...", "expires_in": 3600, "token_type": "Bearer", "scope": "openid profile email" }

OpenID Connect

OIDC Discovery
# Discovery Endpoint GET /.well-known/openid-configuration # Response (partial) { "issuer": "https://auth.yourdomain.com", "authorization_endpoint": "/oauth/authorize", "token_endpoint": "/oauth/token", "userinfo_endpoint": "/oauth/userinfo", "jwks_uri": "/oauth/jwks", "scopes_supported": [ "openid", "profile", "email" ], "response_types_supported": [ "code", "token", "id_token" ] } # User Info Endpoint GET /oauth/userinfo Authorization: Bearer eyJ... # Response { "sub": "user_id", "email": "user@example.com", "email_verified": true, "name": "John Doe" }

Admin APIs

Administration
# Create OAuth Client POST /admin/clients Authorization: Bearer admin_token Content-Type: application/json { "name": "My App", "redirect_uris": [ "https://myapp.com/callback" ], "allowed_scopes": [ "openid", "profile", "email" ] } # List Users GET /admin/users Authorization: Bearer admin_token # Get Analytics GET /api/v1/analytics Authorization: Bearer admin_token # Get Audit Logs GET /api/v1/audit Authorization: Bearer admin_token

Configuration Guide

Configuration Methods

ZenoAuth supports three configuration methods in order of precedence:

1. Environment Variables

Highest priority. Use ZENOAUTH__SECTION__KEY format.

Environment Variables
export ZENOAUTH__SERVER__PORT=3051 export ZENOAUTH__DATABASE__URL="postgres://..." # Ed25519 keys auto-generated

2. Configuration File

TOML format configuration file at zenoauth.toml.

zenoauth.toml
[server] port = 3051 bind_address = "0.0.0.0" [database] url = "postgres://..." max_connections = 20 [oauth] issuer = "https://auth.example.com"

3. Command Line

Lowest priority. Useful for development and testing.

Command Line
./zenoauth \ --server.port 3051 \ --database.url "postgres://..." \ --config zenoauth.toml

Complete Configuration Reference

Complete zenoauth.toml
# Server Configuration [server] bind_address = "0.0.0.0" port = 8080 base_url = "https://auth.yourdomain.com" workers = 0 # 0 = auto-detect CPU cores # Database Configuration [database] url = "postgres://zenoauth:password@localhost:5432/zenoauth" max_connections = 20 min_connections = 5 idle_timeout_seconds = 300 # Security Configuration [security] jwt_secret = "your-super-secret-jwt-key-at-least-32-characters" session_timeout_seconds = 3600 password_min_length = 8 max_login_attempts = 5 lockout_duration_seconds = 900 # OAuth Configuration [oauth] default_token_lifetime_seconds = 3600 refresh_token_lifetime_seconds = 86400 enable_pkce = true require_pkce = false # Rate Limiting [rate_limiting] enabled = true requests_per_minute = 60 burst_size = 10 # Logging Configuration [logging] level = "info" format = "json" correlation_ids = true # SMTP for Email (Optional) [smtp] host = "smtp.gmail.com" port = 587 username = "your-email@gmail.com" password = "your-password" from_address = "noreply@yourdomain.com"

Deployment Guide

Docker Deployment

Production Docker
# docker-compose.prod.yml version: '3.8' services: zenoauth: image: zenoauth:latest ports: - "3051:3051" environment: - ZENOAUTH__DATABASE__URL=postgres://zenoauth:pass@postgres:5432/zenoauth - ZENOAUTH__SERVER__BASE_URL=https://auth.yourdomain.com # Ed25519 keys auto-generated on startup depends_on: - postgres restart: unless-stopped postgres: image: postgres:15 environment: - POSTGRES_DB=zenoauth - POSTGRES_USER=zenoauth - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped volumes: postgres_data:

Kubernetes Deployment

Kubernetes Manifest
# zenoauth-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: zenoauth spec: replicas: 3 selector: matchLabels: app: zenoauth template: metadata: labels: app: zenoauth spec: containers: - name: zenoauth image: zenoauth:latest ports: - containerPort: 3051 env: - name: ZENOAUTH__DATABASE__URL valueFrom: secretKeyRef: name: zenoauth-secrets key: database-url - name: ZENOAUTH__SERVER__BASE_URL value: "https://auth.yourdomain.com" # Ed25519 signing keys auto-generated resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "500m" readinessProbe: httpGet: path: /health port: 3051 initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 15 periodSeconds: 10

Production Checklist

Security

  • ✅ Enable HTTPS/TLS 1.3
  • ✅ Configure proper JWT secrets
  • ✅ Set up rate limiting
  • ✅ Enable audit logging
  • ✅ Configure CORS policies
  • ✅ Set secure session timeouts

Performance

  • ✅ Optimize database connections
  • ✅ Enable connection pooling
  • ✅ Configure caching policies
  • ✅ Set up monitoring
  • ✅ Configure load balancing
  • ✅ Enable health checks

SCIM v2 Provisioning

SCIM User Operations

Create User

POST /scim/v2/Users
{ "schemas": [ "urn:ietf:params:scim:schemas:core:2.0:User" ], "userName": "john.doe@company.com", "name": { "givenName": "John", "familyName": "Doe" }, "emails": [ { "value": "john.doe@company.com", "primary": true } ], "active": true }

Update User

PATCH /scim/v2/Users/:id
{ "schemas": [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ], "Operations": [ { "op": "replace", "path": "active", "value": false } ] } # Other operations GET /scim/v2/Users # List users GET /scim/v2/Users/:id # Get user DELETE /scim/v2/Users/:id # Delete user

SCIM Group Operations

Group Management
# Create Group POST /scim/v2/Groups { "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], "displayName": "Engineering", "members": [ { "value": "user-uuid-1" }, { "value": "user-uuid-2" } ] } # List Groups GET /scim/v2/Groups # Update Group Membership PATCH /scim/v2/Groups/:id { "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [ { "op": "add", "path": "members", "value": [{"value": "new-user-uuid"}] } ] }

Service Provider Configuration

SCIM clients can discover capabilities via the ServiceProviderConfig endpoint.

GET /scim/v2/ServiceProviderConfig
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"], "patch": { "supported": true }, "bulk": { "supported": false }, "filter": { "supported": true, "maxResults": 200 }, "changePassword": { "supported": false }, "sort": { "supported": false }, "etag": { "supported": false }, "authenticationSchemes": [ { "type": "oauthbearertoken", "name": "OAuth Bearer Token", "description": "Authentication via OAuth 2.0 Bearer Token" } ] } # Resource Types Discovery GET /scim/v2/ResourceTypes

Admin Interface Setup

Next.js Admin Dashboard

ZenoAuth includes a modern Next.js 15 admin dashboard with React 19 and shadcn/ui components.

Development Setup

Local Development
# Navigate to admin directory cd zenoauth-admin # Install dependencies npm install # Set API URL export NEXT_PUBLIC_API_URL=http://localhost:3051 # Start development server npm run dev # Admin UI at http://localhost:3050

Features

Admin Pages
# Available admin pages: /users User management /clients OAuth client management /groups Group management /scopes Scope configuration /analytics Usage analytics /audit Audit logs /tokens Token management /sso-providers SSO configuration /settings System settings

Scope Management

Configure custom OAuth scopes with granular permissions.

Scope API
# Create custom scope POST /api/v1/scopes { "name": "user:profile:read", "description": "Read user profile information" } # List all scopes GET /api/v1/scopes # Validate scopes for an application POST /api/v1/scopes/validate { "scopes": ["openid", "profile", "email"] }

Key Capabilities

React 19

Latest React with
Server Components

TanStack

Query for data
fetching & caching

shadcn/ui

Professional
UI components

Integration Examples

JavaScript/Node.js

Node.js Example
// Express.js middleware const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); const client = jwksClient({ jwksUri: 'https://auth.yourdomain.com/.well-known/jwks.json' }); function getKey(header, callback) { client.getSigningKey(header.kid, (err, key) => { const signingKey = key.publicKey || key.rsaPublicKey; callback(null, signingKey); }); } function authenticateToken(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.sendStatus(401); } jwt.verify(token, getKey, { audience: 'your-client-id', issuer: 'https://auth.yourdomain.com', algorithms: ['EdDSA'] }, (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); }); } // Protected route app.get('/protected', authenticateToken, (req, res) => { res.json({ message: 'Access granted!', user: req.user }); });

Python/Flask

Python Example
# Flask integration import jwt import requests from functools import wraps from flask import Flask, request, jsonify app = Flask(__name__) def get_jwks(): response = requests.get( 'https://auth.yourdomain.com/.well-known/jwks.json' ) return response.json() def token_required(f): @wraps(f) def decorated(*args, **kwargs): token = request.headers.get('Authorization') if not token: return jsonify({'error': 'Token missing'}), 401 try: token = token.split(' ')[1] # Remove 'Bearer ' jwks = get_jwks() # Verify token with Ed25519 public key decoded = jwt.decode( token, key=jwks['keys'][0], algorithms=['EdDSA'], audience='your-client-id', issuer='https://auth.yourdomain.com' ) current_user = decoded except jwt.InvalidTokenError: return jsonify({'error': 'Token invalid'}), 401 return f(current_user, *args, **kwargs) return decorated @app.route('/protected') @token_required def protected(current_user): return jsonify({ 'message': 'Access granted!', 'user': current_user })