SimpleAuth — lightweight self-hosted authentication server with LDAP, Kerberos/SPNEGO, and JWT support
  • Go 86.1%
  • Python 5.4%
  • C# 3.6%
  • TypeScript 3.1%
  • Shell 1%
  • Other 0.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
debe 0efb06572e
All checks were successful
CI / Build & Test (push) Successful in 40s
build-docker / Build & Push Image (push) Successful in 15s
smoke-test / End-to-end smoke test (push) Successful in 30s
release / Test before release (push) Successful in 40s
release / Publish Image (push) Successful in 14s
release / Create Release (push) Successful in 1m22s
Release 2.3.2: production hardening + immutable version tags
2026-10-02 23:30:31 +03:00
.claude Encrypt secrets at rest, CSRF protection, sanitize errors, harden Kerberos provisioning 2026-03-30 09:55:16 +03:00
.forgejo/workflows Release 2.3.2: production hardening + immutable version tags 2026-10-02 23:30:31 +03:00
.github release: v2.0.2 — security hardening (Audit Pass 3) 2026-06-01 01:02:28 +03:00
deploy/traefik Traefik: apply aliasHeadersStrategy to ping entrypoint too (zero warnings) 2026-10-02 23:27:43 +03:00
docs Production hardening: header-alias strip, no version check, capped logs, docs 2026-10-02 23:24:27 +03:00
examples release: v2.0.2 — security hardening (Audit Pass 3) 2026-06-01 01:02:28 +03:00
internal Replace nginx reverse proxy with Traefik 3.7 2026-08-29 14:40:35 +03:00
pkg/server release: v2.0.2 — security hardening (Audit Pass 3) 2026-06-01 01:02:28 +03:00
sdk release: v2.0.2 — security hardening (Audit Pass 3) 2026-06-01 01:02:28 +03:00
test Use floating traefik:v3.7 tag (drop digest pin) for periodic updates 2026-08-29 19:43:13 +03:00
ui feat(settings): admin-controlled runtime rate limit with disable toggle 2026-07-09 08:11:01 +03:00
.dockerignore Add Forgejo Actions CI: test, container package, release 2026-08-29 14:40:35 +03:00
.env.example Fix base-path consistency: /sauth everywhere (healthcheck, Traefik routers, smoke test) 2026-08-29 17:36:02 +03:00
.gitignore Add Forgejo Actions CI: test, container package, release 2026-08-29 14:40:35 +03:00
build.md Replace nginx reverse proxy with Traefik 3.7 2026-08-29 14:40:35 +03:00
build.sh Add semantic versioning with VERSION file and Makefile 2026-03-10 19:56:12 +03:00
CHANGELOG.md Release 2.3.2: production hardening + immutable version tags 2026-10-02 23:30:31 +03:00
docker-build.sh Make VERSION file the single source of truth for version 2026-03-27 08:34:07 +03:00
docker-compose.dev.yml Release 2.3.0: CHANGELOG, registry-based prod compose + dev override, README diff section 2026-08-29 14:57:18 +03:00
docker-compose.test.yml Fix Traefik backend healthcheck path (/sauth) + robust test healthcheck 2026-08-29 17:52:43 +03:00
docker-compose.yml Production hardening: header-alias strip, no version check, capped logs, docs 2026-10-02 23:24:27 +03:00
Dockerfile Fix base-path consistency: /sauth everywhere (healthcheck, Traefik routers, smoke test) 2026-08-29 17:36:02 +03:00
go.mod chore(deps): raise the go directive to 1.25.12 (GO-2026-5856) 2026-08-08 17:42:16 +03:00
go.sum chore(deps): bump golang.org/x/text to v0.39.0 (GO-2026-5970) 2026-07-31 22:36:14 +03:00
LICENSE Add MIT license file 2026-03-09 17:08:18 +03:00
main.go release: v2.0.2 — security hardening (Audit Pass 3) 2026-06-01 01:02:28 +03:00
Makefile Add semantic versioning with VERSION file and Makefile 2026-03-10 19:56:12 +03:00
README.md Docs: split into Deployment (ops) and Development (dev) guides; air-gapped first-class 2026-08-29 16:59:35 +03:00
SECURITY-AUDIT.md chore(auth): render the Kerberos/LDAP diagnostic pages with html/template (M39) 2026-08-09 10:06:01 +03:00
VERSION Release 2.3.2: production hardening + immutable version tags 2026-10-02 23:30:31 +03:00

🔐 SimpleAuth-T

Enterprise authentication that fits in a single 10 MB binary.
Active Directory · Kerberos SSO · a standard OIDC provider · signed JWTs — running in 3 commands.

License: MIT Made with Go Single binary No dependencies Security audited

SimpleAuth-T is a fork of bodaay/SimpleAuth that ships Traefik 3.7 as the bundled reverse proxy instead of nginx. Source & container images live at git.majlis7.net/debe/SimpleAuth-T (git.majlis7.net/debe/simpleauth-t).

Deploy · Develop · Quick Start · API Reference · AD + Kerberos · SDKs · Security Audit Log · Contributing


Setting up authentication shouldn't mean standing up a Java cluster, a database, a cache, and a weekend. SimpleAuth is the whole identity layer — Active Directory login, transparent Windows SSO, a standards-compliant OIDC provider, and RS256 JWTs — in one Go binary you can scp to a box and run.

No Java. No mandatory database. No config files after first run. Everything is managed from a built-in admin UI, and a domain-joined machine gets you to two-click Kerberos SSO. Drop it in, point your app at it, ship.

docker run -d -p 8080:8080 \
  -e AUTH_HOSTNAME=auth.example.com \
  -e AUTH_ADMIN_KEY=change-me \
  -e AUTH_REDIRECT_URIS="https://myapp.example.com/*" \
  -v simpleauth-data:/data \
  git.majlis7.net/debe/simpleauth-t
# → admin UI at https://auth.example.com/sauth/admin. That's it.

🔀 Differences from upstream SimpleAuth

SimpleAuth-T is the same SimpleAuth application — identical API, config, and behavior. Only the deployment and identity layer changed:

Upstream bodaay/SimpleAuth SimpleAuth-T (this repo)
Bundled reverse proxy nginx Traefik 3.7
Proxy config deploy/nginx/ deploy/traefik/traefik.yml + dynamic.yml
Container image GitHub (ghcr.io) git.majlis7.net/debe/simpleauth-t
CI / releases GitHub Actions Forgejo Actions
docker-compose.yml builds locally pulls the registry image
Build from source (default) docker-compose.dev.yml override

Why Traefik? Traefik 3.7 provides, out of the box:

  • automatic X-Forwarded-For / X-Real-Ip / X-Forwarded-Proto headers (correct client IPs in audit logs and rate limiting);
  • native large-header support — Kerberos/SPNEGO Negotiate tokens pass through with no buffer tuning;
  • middleware-based rate limiting (10 req/s general + 3 req/s on login/token) and security headers;
  • single-file Let's Encrypt ACME — no certbot;
  • no Docker socket needed (the file provider only).

Every Traefik setting — TLS, headers, rate limits, sub-path mounting, Let's Encrypt, troubleshooting — is explained in docs/REVERSE-PROXY.md.

✨ Why you'll like it

It's powerful. Everything a real identity provider needs, nothing watered down:

  • 🪪 Active Directory & LDAP — bind login, attribute sync, group membership, auto-discovery.
  • 🎫 Transparent Kerberos / SPNEGO SSO — users on the domain are logged in without typing anything. SSO setup is a downloadable PowerShell script and one paste-back — no ktpass, no keytab files, no hand-edited LDAP.
  • 🌐 A standard OIDC provider — discovery, authorization-code flow with PKCE, token, userinfo, JWKS, introspection, end-session. Works with any OIDC client library, in any language.
  • 🔑 RS256 JWTs — auto-generated RSA-2048 keys, a JWKS endpoint for offline verification, single-use refresh-token rotation with family replay detection.
  • 👥 Roles, permissions & groups — resolved into the token your app already trusts.
  • 🏢 One directory, many apps — register multiple apps that each own their own roles, permissions, and allowed users. Users sign in once; tokens are audience-scoped, so a token minted for app A is rejected by app B. (details below)

It's simple. The kind of simple you feel in the first five minutes:

  • 📦 One ~10 MB binary. No runtime, no JVM, no sidecars. BoltDB is embedded — zero external dependencies to start.
  • 🖥️ A real admin UI (dark mode included) — users, roles, LDAP, audit log, settings, database, all on a page. No XML, no kcadm.sh.
  • ⚡ 3 commands to running, and users are auto-created on first login from any provider — no import, no sync, no migration job.
  • 🧩 Embeddable — import "simpleauth/pkg/server" and your Go app is the auth server.
  • 📚 Official SDKs for JavaScript/TypeScript, Go, Python, and .NET, each with framework middleware.

SimpleAuth vs. the usual suspects

Keycloak ADFS SimpleAuth
Setup Hours (Java + Postgres + cache) Hours + Windows Server 3 commands
Footprint ~500 MB+ A domain controller ~10 MB binary
External deps Database + cache required Windows infra None (optional Postgres)
Kerberos SSO Manual, fiddly Built-in but rigid Two-click auto-config
OIDC provider Full Claims-based Full
Per-app authorization Realms + clients (heavy) Relying-party trusts Built-in, audience-scoped
Admin experience Steep Windows-only MMC One web page
REST API Sprawling None Clean JSON + JWTs

🚀 Quick start

Docker:

docker run -d -p 8080:8080 \
  -e AUTH_HOSTNAME=auth.example.com \
  -e AUTH_ADMIN_KEY=my-secret-admin-key \
  -e AUTH_REDIRECT_URIS="https://myapp.example.com/callback,https://myapp.example.com/*" \
  -e AUTH_CORS_ORIGINS="https://myapp.example.com" \
  -v simpleauth-data:/data \
  simpleauth

Binary:

./simpleauth init-config     # generates simpleauth.yaml
vim simpleauth.yaml          # set hostname, redirect URIs, admin key
./simpleauth                 # running

Open the admin UI at https://<hostname>/sauth/admin and sign in with your admin key. (Omit AUTH_ADMIN_KEY and one is generated on first run and printed to the logs.)

For any real deployment, set three things: AUTH_HOSTNAME, AUTH_ADMIN_KEY, and AUTH_REDIRECT_URIS. Without an allowlist, every login redirect is rejected — by design.

New to OAuth/OIDC? The Quick Start walks you from zero to a logged-in user, and the Integration Guide explains every term (JWT, redirect URI, CORS, the /sauth base path) in plain language.

🔌 Add it to your app

Pick whichever fits — you can mix them:

1. Direct REST — best for mobile, SPAs with a backend, and server-to-server.

curl -X POST https://auth.example.com/sauth/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"secret123"}'
# → { "access_token": "eyJ…", "refresh_token": "eyJ…", "expires_in": 900 }

2. Hosted login page — best for web apps that don't want to build a login form. Redirect to …/sauth/login?redirect_uri=https://myapp/callback; SimpleAuth (or Kerberos SSO) authenticates the user and hands the tokens back on the callback.

3. Standard OIDC — best when you already use an OIDC client library or need an id_token. Point your library at the discovery URL and you're done:

https://auth.example.com/sauth/.well-known/openid-configuration

Then verify tokens offline against the JWKS endpoint (no network call per request), or call /sauth/api/auth/userinfo to introspect. Full walkthrough with every flow: docs/API.md.

Use an SDK instead of raw HTTP

Language Package Install
JavaScript / TypeScript @simpleauth/js npm install @simpleauth/js
Go github.com/bodaay/simpleauth-go go get github.com/bodaay/simpleauth-go
Python simpleauth pip install simpleauth
.NET SimpleAuth reference the project

Every SDK does login/refresh/userinfo, offline JWT verification with cached JWKS, role/permission helpers (HasRole, HasPermission, HasAnyRole), and ships middleware for Express, net/http, FastAPI/Flask/Django, and ASP.NET Core. See examples/.

🏢 One login, many apps — each with its own authorization

Most auth servers make you choose: one shared login or per-app control. SimpleAuth gives you both — one directory, one sign-in, but every app owns its own authorization.

  • Register an app → it gets an app_id + app_secret. The root admin adds apps; existing single-app deployments auto-migrate to a default app, unchanged.
  • Tokens are audience-scoped. Every token carries aud = the app it was minted for, so a token for billing is rejected by analytics — the SDK's audience option enforces it. No more one-token-rules-them-all.
  • Each app owns its roles, permissions, and assignments. viewer in one app is unrelated to viewer in another. Flip on require_assignment and an app admits only the users it has explicitly assigned.
  • Apps manage themselves. An app configures its own roles and assignments using its app_id/app_secret (HTTP Basic or a short-lived management token) and an idempotent POST /api/app/bootstrap you can run on every deploy — no master admin key in your pipeline.
  • App-local users. An app can own users who aren't in your directory at all — perfect for a customer portal — scoped to that app and never shared across apps.

A developer's whole integration becomes: get an app_id/app_secret → bootstrap your roles on deploy → point the SDK at SimpleAuth with audience set → verify(). All four SDKs ship the app-management helpers (appBootstrap, getAppAuthz/setAppAuthz, app-local user CRUD) — see the app-integration examples in examples/ and the Apps reference in docs/API.md.

Think Azure AD / Entra app registrations — one directory, many apps, per-app roles, audience-scoped tokens — without the cluster.

🧰 What's in the box

Authentication

  • Kerberos/SPNEGO transparent Windows SSO
  • Optional shared SSO session cookie (log in once, skip the login page across apps)
  • Auto-SSO with a countdown + cancel
  • LDAP bind login for non-domain users
  • Local passwords (bcrypt, policy, history)
  • Account lockout after repeated failures
  • Hosted login page + standard OIDC
  • Admin impersonation (fully audited)

Tokens & authorization

  • RS256 JWTs, auto-generated RSA-2048 keys
  • JWKS endpoint for offline verification
  • Refresh-token rotation + family replay detection + revocation
  • Roles, permissions, and role→permission mapping
  • Per-app authorization — audience-scoped tokens, per-app roles & assignment
  • App self-service + app-local users (apps own their own scope)
  • Default roles auto-assigned on first login
  • AD/LDAP groups surfaced in the token

Admin UI (dark mode, one page)

  • Users: CRUD, disable, merge, unlock, force reset
  • Roles & permissions
  • LDAP providers + AD setup script + Kerberos
  • Identity mappings & audit log
  • Runtime settings (redirects, CORS, policy, rate limits)
  • Database stats, migration, backend switching, restart

Ops & storage

  • BoltDB embedded by default — zero config
  • PostgreSQL optional (AUTH_POSTGRES_URL), migrate either way from the UI, auto-fallback if it's unreachable
  • Per-IP rate limiting with trusted-proxy support
  • CSRF protection on login forms; strict redirect allowlist
  • Live backup/restore via the API
  • Linux SSO setup script (krb5.conf + every major browser)

🛡️ Security is the whole point

SimpleAuth guards the front door, so security isn't a feature — it's the product. A few things we do differently:

  • A public, living audit trail. SECURITY-AUDIT.md logs every security review, finding, and fix — dated, with stable IDs, by different AI models and humans over time. Nothing is swept under the rug; even WONTFIX decisions are written down with rationale. It's unusual to publish your own audit log. We think a project that protects logins should.
  • Sane defaults that fail closed. Redirect URIs are a strict allowlist (empty = reject all). The confidential OIDC grants (password, client_credentials) and token introspection are disabled unless you set AUTH_CLIENT_SECRET. Trusted proxies default to trust none.
  • Modern token hygiene. RS256 only (no alg-confusion), JWKS with a stable key id, single-use refresh tokens with replay detection, an access-revocation kill switch, and authenticator-verified Kerberos with a replay cache and clock-skew enforcement.
  • Defense in depth. bcrypt password hashing with a configurable policy and history, account lockout, per-IP rate limiting, CSRF tokens, and security headers across responses.

Found something? Please open a security advisory (or a private report) rather than a public issue — see Contributing. Then add it to the audit log; that's exactly what it's for.

🤝 Contributing

This project is public on purpose. SimpleAuth protects authentication, and security software gets better with more eyes on it. If you've ever wanted to work on an auth server that's small enough to actually read end-to-end in an afternoon — this is that codebase.

Especially valuable:

  • 🔍 Security review. Read SECURITY-AUDIT.md, then audit a flow (login, refresh rotation, Kerberos, OIDC grants, the store layer) and send what you find — a PR, an issue, or a new audit pass appended to the log. The file even contains the prompt we hand to each reviewer.
  • 🧩 More SDKs & framework middleware. Rust, Java, PHP, Ruby — or deeper middleware for the four we already ship.
  • 📖 Docs & examples. A clearer explanation, a new framework example in examples/, a fixed typo — all welcome.
  • 🐛 Bugs & papercuts. Edge cases in LDAP/Kerberos against real directories are gold.

Get going in a minute:

git clone <your-fork>
cd SimpleAuth
go build ./...        # builds the single binary
go test ./...         # full suite (no external services needed)
go vet ./...

Open a PR with a focused change and a test, and keep the trio in sync — code, docs, and the SDKs/examples for any surface you touch. New features ship with all of it. First time here? Open an issue describing what you'd like to do and we'll point you at the right file.

📚 Reference

OIDC endpoints

Discovery: GET /.well-known/openid-configuration

Endpoint Method Path
Authorization GET /realms/{issuer}/protocol/openid-connect/auth
Token POST /realms/{issuer}/protocol/openid-connect/token
UserInfo GET/POST /realms/{issuer}/protocol/openid-connect/userinfo
JWKS GET /realms/{issuer}/protocol/openid-connect/certs
Introspection POST /realms/{issuer}/protocol/openid-connect/token/introspect
End Session GET/POST /realms/{issuer}/protocol/openid-connect/logout

authorization_code and refresh_token are public flows (auth-code supports PKCE/S256). password, client_credentials, and introspection are confidential — disabled unless AUTH_CLIENT_SECRET is set, then require that secret.

Direct API
Method Path Description
POST /api/auth/login Login, get JWT tokens
POST /api/auth/refresh Rotate refresh token
GET /api/auth/userinfo Current user from the JWT
GET /api/auth/negotiate Kerberos/SPNEGO SSO
POST /api/auth/impersonate Token as another user (admin)
POST /api/auth/reset-password Change password (authenticated)
GET /login · /logout · /account Hosted pages
GET /.well-known/jwks.json JWKS public keys
GET /health Health check
Admin API (all require Authorization: Bearer <admin-key>)
Category Endpoints
Bootstrap POST /api/admin/bootstrap — idempotent: define roles, permissions, ensure users
Users CRUD /api/admin/users, merge/unmerge, disable, unlock, sessions
Roles & Permissions /api/admin/roles, /api/admin/permissions, /api/admin/role-permissions
LDAP CRUD /api/admin/ldap, auto-discover, import/export, test
AD / Kerberos GET /api/admin/setup-script, /api/admin/ldap/:id/setup-kerberos
Linux SSO GET /api/admin/linux-setup-script
Settings / DB / Ops runtime settings, migrate/switch backends, backup, restore, audit, restart

Full reference with request/response examples: docs/API.md.

Essential configuration
Variable Default Description
AUTH_HOSTNAME OS hostname FQDN for TLS cert and Kerberos SPN
AUTH_ADMIN_KEY auto-generated Admin API key
AUTH_BASE_PATH /sauth URL path prefix (every URL starts here)
AUTH_REDIRECT_URIS Allowed redirect URIs, comma-separated (* wildcards)
AUTH_CORS_ORIGINS Browser origins allowed to call SimpleAuth
AUTH_POSTGRES_URL Enables the Postgres backend
AUTH_TLS_DISABLED false Disable TLS (reverse-proxy mode)
AUTH_TRUSTED_PROXIES Trusted proxy CIDRs (default: trust none)
AUTH_CLIENT_SECRET Enables confidential OIDC grants + introspection
AUTH_JWT_ACCESS_TTL 15m Access token lifetime
AUTH_JWT_REFRESH_TTL 720h Refresh token lifetime
AUTH_ENABLE_SESSION_SSO false Shared SSO session cookie across apps
AUTH_AUTO_SSO false Auto-attempt Kerberos SSO on the login page

Everything else — password policy, lockout, rate limiting, audit retention — is in docs/CONFIGURATION.md. Behind Traefik/nginx/Caddy/HAProxy? See docs/REVERSE-PROXY.md (Traefik passes large Kerberos headers natively; nginx needs buffer tuning).

🪟 Active Directory in two clicks

  1. Admin UI → LDAP Providers → AD Setup Script. Enter a service-account name, download the PowerShell script.
  2. Run it on any domain-joined machine — it creates the account, registers SPNs, and exports a config file. No ktpass.
  3. Paste the config back in the UI. SimpleAuth builds the keytab in memory and enables SSO immediately.

Full guide + troubleshooting: docs/ACTIVE-DIRECTORY.md.

🧱 Embed it in your Go app

cfg := server.Defaults()
cfg.Hostname = "myapp.example.com"
cfg.AdminKey = "my-secret-key"
cfg.DataDir  = "./auth-data"
cfg.BasePath = "/auth"

sa, err := server.New(cfg, ui.FS())  // pass nil UI for API-only
if err != nil { log.Fatal(err) }
defer sa.Close()

mux := http.NewServeMux()
mux.Handle("/auth/", http.StripPrefix("/auth", sa.Handler()))
// your app handles everything else
http.ListenAndServe(":8080", mux)

Your app and its auth server, one process, one deploy. See docs/ARCHITECTURE.md.

📖 Documentation

Run it (operations)

Document What's inside
Deployment & Operations Run it online or air-gapped/offline — incl. Traefik cert placement
Quick Start Zero to a logged-in user in 5 minutes
Configuration All config options
Reverse Proxy Traefik deep-dive + nginx/Caddy/HAProxy
Active Directory AD, Kerberos, troubleshooting
Keycloak Migration Replacing Keycloak

Develop it

Document What's inside
Developer Guide Build, test, CI, versioning, releasing
Architecture How it works under the hood
API Reference Every endpoint, every term, with examples
SDK Guide Client SDK usage
Security Audit Log Every review, finding, and fix

License

MIT — see LICENSE. Build something with it, and if it makes your auth simpler, ⭐ the repo so the next person finds it.