Skip to content
View on GitHub

View on GitHub

github.com • November 2, 2026

An open-source, self-hostable identity server that speaks the Auth0 Authentication API. Point an existing Auth0 SDK at a zeroauthy tenant domain and it works with no code changes.

zeroauthy is a clean-room implementation built from public documentation and open standards. It is not affiliated with Auth0 or Okta. See CLEAN_ROOM.md .

OpenID Certified. zeroauthy is certified by the OpenID Foundation as an OpenID Connect Provider for the Basic OP, Config OP and Form Post OP profiles (commit 38b4229 , October 2026). See docs/conformance.md .

Status: early. Multi-tenant discovery, JWKS, the client_credentials grant, login with the hosted page, the authorization code + PKCE flow, rotating refresh tokens, /oauth/revoke , and logout through /v2/logout and /oidc/logout work today (verified with auth0-spa-js, auth0-react and nextjs-auth0). Pushed authorization requests ( docs/par.md ) and the device authorization flow ( docs/device-flow.md ) are supported too, and so are Auth0's Management API for applications, APIs, client grants and database connections ( docs/management-api.md , verified with auth0-cli) and an admin console ( docs/console.md ). Organizations, organization login and roles, invitations, application grants, discovery domains, and per-tenant operators are implemented ( docs/organizations.md ). Tenant logs, log streams and statistics ( docs/logs.md ), plus transactional Event Streams with webhook and Action delivery and an SSE Events API ( docs/event-streams.md ), are also available. See the compatibility matrix for supported behavior and limits.

Status: early. Multi-tenant discovery, JWKS, the client_credentials grant, login with the hosted page, the authorization code + PKCE flow, rotating refresh tokens, /oauth/revoke , and logout through /v2/logout and /oidc/logout work today (verified with auth0-spa-js, auth0-react and nextjs-auth0). Pushed authorization requests ( docs/par.md ) and the device authorization flow ( docs/device-flow.md ) are supported too, and so are Auth0's Management API for applications, APIs, client grants and database connections ( docs/management-api.md , verified with auth0-cli) and an admin console ( docs/console.md ). Organizations, organization login and roles, invitations, application grants, discovery domains, and per-tenant operators are implemented ( docs/organizations.md ). Tenant logs, log streams and statistics ( docs/logs.md ), plus transactional Event Streams with webhook and Action delivery and an SSE Events API ( docs/event-streams.md ), are also available. See the compatibility matrix for supported behavior and limits.

Tenant-configurable email providers, bounded Liquid templates, previews and operator test sends are also available. Existing tenants can keep the server-wide email driver; see docs/email.md for provider, template and migration guidance.

The goal is a 1:1, drop-in replacement for Auth0's Authentication API and Management API. The Authentication API side is complete apart from what the features below add. The Management API is being built resource by resource, each with its console screens; the full breakdown is in the Management API roadmap .

Core server, signing keys, discovery and JWKS, hosted login and signup, authorization code with PKCE, client credentials, password grants, rotating refresh tokens, logout, password reset, hosted-page languages

Storage: memory, SQLite, PostgreSQL, MySQL, gRPC storage plugins

Custom database connections (Auth0 scripts, import mode)

Consent and third-party applications, PAR, device flow

OpenID Certified (Basic, Config, Form Post OP); conformance suite in CI (those plus RP-Initiated Logout)

Management API and console: applications, APIs, client grants, database connections, users (with , identity linking, sessions, refresh tokens, grants and tickets), user import/export jobs and verification email

Tenant email providers and templates, signup welcome and security notices; see docs/email.md for active workflows and stored-only limits

Tenant settings and attack protection (Plan 19)

Tenant settings , attack protection and automatic blocks

Signing keys and deployment migration (Plan 20)

Managed signing keys, stored-only custom public JWKS, and credential-preserving bootstrap are implemented.

The pinned Deploy CLI passes local YAML/directory imports with generated or preserved credentials, including organizations, Actions and event streams. Database connections remain excluded from that matrix, and attack-protection migration needs an unavailable phone-provider API; the upstream observer lane remains unavailable. See signing keys and Deploy CLI migration for current limits.

Branding and Universal Login (Plan 22)

Tenant branding, themes, bounded page templates, identifier-first login, localized text and transient form partials; console editors with isolated preview. See branding and the implementation record for supported syntax and compatibility limits.

Custom domains and automatic certificates (Plan 23)

Verified domain ownership, shared automatic TLS, authenticated customer proxies, domain-bound login and notification links, and console controls. See the operator guide and acceptance record .

Roles, connections and Actions (Plans 24–28)

Generic OAuth/OIDC, Google and Microsoft connections; roles and permissions.

Actions : login and credentials exchange, signup, password reset/change and custom email triggers; immutable Modules, durable jobs, console and migration tooling. Limits and verification are in the Plan 28 implementation record .

Organizations I (Plan 29)

Organizations, members, role assignments, enabled connections, hosted prompt and picker, claims/refresh checks, branding, Actions context and scoped operators. See Organizations and the verification record .

Organizations II (Plan 30)

Invitations, organization application grants, verified discovery domains and console controls. See Organizations and the implementation record .

Logs, log streams and statistics (Plan 31)

Searchable tenant logs, live console views, Action execution logs, outbound log streams, daily statistics and active-user counts. See Logs for supported destinations and privacy controls.

Event streams (Plan 32)

Transactional CloudEvents for user, organization and enterprise-connection changes; guarded webhooks and durable event-stream Actions; retries, failed-delivery inspection, redelivery and auto-disable; resumable SSE, console controls, seed and Deploy CLI support.

EventBridge configurations are stored disabled, and group events await Plan 38. See Event streams for payloads, retention and other limits, and the validation report for actual results, skips and conformance outcomes.

The roadmap tracks 42 resource groupings; its coverage table records completed and remaining work. Four groups are not planned (Rules, Hooks and rules-configs, which Auth0 is retiring, and customer-managed encryption keys).

With the quick-start server running, open:

and sign in as [email protected] / Correct-Horse-Battery-9 (test data from tests/e2e/seed.yaml ). You are redirected to .

Open the admin console

The console is served only on the system tenant's host, not on localhost . With the quick-start config ( system_host: system.localhost:8443 ) it is at . Browsers resolve *.localhost to 127.0.0.1; for other clients add 127.0.0.1 system.localhost to /etc/hosts .

The quick-start config stores everything in memory, so the first operator must be created by the server itself: start it with ZEROAUTHY_BOOTSTRAP__ADMIN_EMAIL set.

The server writes the one-time setup link to an owner-only (0600) file in the host's temporary directory and logs its path as setup_file , plus the operator email and one-hour validity. Read that private file on the server, open the link, and choose a password of at least 12 characters. Delete the file after use, then sign in at . Server shutdown leaves the file available for delivery. Memory storage is lost on restart, and so is the operator; the start creates a new private setup file.

zeroauthy admin create --email [email protected] --config does the same from another terminal, but only with persistent storage (SQLite, PostgreSQL, MySQL): it opens the database directly, so with memory:// its link points at data the server never sees.

The built console ( console/dist ) is embedded in the binary at compile time. After changing the console, run make console and then rebuild the binary ( make build or the go build above); a running server keeps the console it was built with. See docs/console.md for the screens.

API reference: . The raw contract is at /openapi.yaml , and its source is api/authentication.yaml .

YAML file ( --config ) plus ZEROAUTHY_* environment variables. __ separates levels: ZEROAUTHY_STORAGE__DSN=memory:// sets storage.dsn . See internal/config/config.go for every setting. Tenants, APIs, applications and grants can come from a declarative seed file ; tenants and applications can also be managed in the console or through the Management API . The seed never touches what the console or the API created. By default the seed is applied at every start and overwrites what it defines. Set seed_mode: create_only ( ZEROAUTHY_SEED_MODE=create_only ) to have it only create what does not exist yet (tenants, APIs, applications, grants, connections, users) and change nothing that does, so edits made in the console survive a restart. In that mode the console shows no "defined by the seed" banner. zeroauthy seed follows the same setting. Unknown configuration keys are rejected at startup.

Actions. Configure the actions section or ZEROAUTHY_ACTIONS__* . Defaults: node: node , bundled npm_cli , registry: , cache_dir: /zeroauthy/actions-cache , max_memory_mb: 256 , idle_timeout: 5m , build_timeout: 2m , max_artifact_mb: 50 (range 1–50), and max_concurrent_builds: 2 . Explicit npm/cache paths must be absolute; npm_cli points to bin/npm-cli.js . Node ≥22.13 runs Actions; npm and the registry are needed only on build nodes. Set cache_dir explicitly for services and registry to an HTTP(S) offline mirror when needed. See the Actions operator guide for dependency restrictions, replica artifacts, seed ownership, sandbox limits and hardening.

Event streams. The events section configures retention (default 168h , minimum 24h ), the stream quota (default 10 per tenant) and concurrent SSE connections (default 4 per tenant per server instance). Webhook destinations require HTTPS and refuse private-network targets by default. See Event streams for deployment settings, migration and plugin compatibility.

Master key. Signing keys and client secrets are stored envelope-encrypted under a 32-byte master key. Generate one with openssl rand -base64 32 and set it as ZEROAUTHY_SECURITY__MASTER_KEY (or point security.master_key_file at a file containing it). It is required for persistent storage; with memory:// storage zeroauthy uses an ephemeral key and logs a warning.

Storage. storage.dsn selects the driver:

PostgreSQL locale. Create the database with a UTF-8 LC_CTYPE (for example CREATE DATABASE zeroauthy LC_CTYPE 'C.UTF-8' TEMPLATE template0 ; the official container images default to en_US.utf8 ). Under C or POSIX PostgreSQL's lower() folds ASCII letters only, so case-insensitive user and sort would miss non-ASCII matches ( Émilie vs émilie ) that the other drivers find. zeroauthy logs a warning at startup in that case and still runs.

Writing a storage plugin. A plugin is a Go program that calls pkg/storeplugin.Serve with any store.Store ; cmd/zeroauthy-store-memory is a minimal template. Check your driver by running storetest.Run (package pkg/storetest ) with a factory that calls storeplugin.Open on your built binary. Transactions work across the boundary; see decision D-0021 for the protocol.

Plan 20 adds result-returning rotation/revocation, certificate compare-and-set, and the separate custom public-key repository to the storage contract. Rebuild storage plugins against the current pkg/storeplugin ; missing lifecycle RPCs fail closed with an upgrade diagnostic. Transaction views and tenant deletion must cover both managed keys and custom sets; run pkg/storetest conformance.

The SQL drivers have a schema. Apply pending migrations with zeroauthy migrate --config zeroauthy.yaml before starting a new version, or set storage.auto_migrate: true ( ZEROAUTHY_STORAGE__AUTO_MIGRATE=true ) to have serve migrate at startup. Without it, serve refuses to start against a schema that is behind or ahead of the binary. zeroauthy seed requires a migrated database and ignores storage.auto_migrate . MySQL commits DDL implicitly, so if a migration fails part-way there, clean up the objects it created by hand before running zeroauthy migrate again.

System tenant and operators. zeroauthy keeps a reserved tenant named system for itself: its users are the console operators. It is reachable at system. , or at server.system_host , which is required when server.base_domain is empty (for example system.localhost:8443 ). A server.system_host under the base domain must be exactly system. . The server creates it at every start; no other tenant may use the name system or that host. Create an operator with zeroauthy admin create --email [email protected] --config zeroauthy.yaml , which prints a one-time setup link (valid for one hour) where the operator chooses a password. Automated installs may set ZEROAUTHY_BOOTSTRAP__ADMIN_EMAIL instead: on a start that finds no operator, the server creates that one and writes its one-hour setup link to a private 0600 file in the host's temporary directory. The server log contains only the setup_file path, email and validity. Read the file on the server and delete it after use; shutdown does not remove it. If file delivery fails after the operator was persisted, use admin create for that email to issue a fresh link. No password is ever taken from configuration. Operators sign in to the admin console at /admin/ on that host.

Proxies. Enable server.trust_proxy_headers only behind a proxy that sets X-Forwarded-Host ; otherwise clients could choose their own tenant.

jobs.workers defaults to 2; set it to 0 to disable workers on a process while another process handles the shared database queue. jobs.lease defaults to 60s (minimum 1s ); workers renew every third of the lease. Environment overrides are ZEROAUTHY_JOBS__WORKERS and ZEROAUTHY_JOBS__LEASE . All serving processes must the same storage and master key for job reads and export downloads. See the import/export guide for limits and reset advice.

A database connection can run your own Node.js scripts ( login , get_user , create , change_password ) against an existing user store, either as the permanent source of truth or as an import that migrates users on first login. Scripts written for Auth0 run as written (native addons and a few blocked built-ins such as child_process are the known differences) and need Node.js 22.13 or later. See docs/custom-db.md , including the security notes for hosting untrusted tenants.

zeroauthy is OpenID Certified for the Basic OP, Config OP and Form Post OP profiles (commit 38b4229 , approved 2026-10-30). It is also tested against the RP-Initiated Logout OP profile, which OIDF certifies only alongside a session, front-channel or back-channel logout profile; back-channel logout is planned (Plan 41). make conformance runs all four plans locally in containers (Linux, podman or docker). Local results pass, with a few accepted warnings and skips. See docs/conformance.md .

Apache-2.0. See LICENSE and NOTICE .

Extracted Entities

Email Addresses (1)