A self-hosted OpenID Connect provider with a bundled OpenLDAP directory and a web management UI — for home labs and small businesses that want their own identity provider instead of a hosted one.
It gives you one place to manage your users and groups, one login (OIDC) that your modern apps can use, and one LDAP directory your older or odder apps can bind to directly. Everything runs on your own hardware; there is no phone-home, no hosted control plane, and no per-user pricing.
Setting up the whole stack (this SSO + the theta42/proxy in front of it) with one command? Skip to theta-env — its
setup.shwires the two together and generates the config for you.
Documentation: https://theta42.github.io/sso-manager-node/
| Dashboard | Users |
|---|---|
![]() |
![]() |
| Groups | OAuth Apps |
|---|---|
![]() |
![]() |
| Sites & Replication |
|---|
![]() |
/.well-known/openid-configuration.posixAccount/inetOrgPerson), SSH public keys, and sudo roles, with
memberOf + referential-integrity overlays. This is your single source of
truth for identity, not a sidecar.sudo
rules, SSH public keys via openssh-lpk) and LDAP-native apps (Gitea, Emby,
and anything else that speaks LDAP) use LDAPS (636) or StartTLS against the
same directory, so you don't maintain a second user database for them.app_* env config.Tools like Keycloak, Authentik, Authelia, or Zitadel are OIDC providers, but LDAP is either a paid feature, a federation target you have to run separately, or absent. If your stack already has apps that speak LDAP directly (or you just want one real directory as the source of truth), you end up running two identity systems and keeping them in sync.
SSO Manager bundles the OpenLDAP directory with the OIDC provider, so OIDC apps and LDAP apps read from the same users and groups. The trade-off is scope: it is intentionally small and self-hosted, not an enterprise IAM suite — no fancy workflow engine, no hosted SaaS. If you want a lightweight, self-contained identity provider with a real LDAP backend, that is the niche.
Three ways to run it, in order of how much it sets up for you:
theta-env composes this SSO Manager with
the theta42/proxy (an OIDC-protected reverse
proxy) and generates all the config from a single setup.env — you enter your
domain once and it fills in the LDAP DNs, hostnames, OAuth issuer, and random
secrets consistently:
git clone --recursive https://github.com/theta42/theta-env.git
cd theta-env
cp setup.env.example setup.env # set CFG_DOMAIN to your domain
./setup.sh # generates ./config/, builds + bootstraps + starts both
See the theta-env README for the full first-run flow, DNS/port requirements, and backups.
The all-in-one image bundles the app, OpenLDAP, and Redis. Copy the example secrets file, fill in your values, and build:
git clone https://github.com/theta42/sso-manager-node.git
cd sso-manager-node
mkdir -p config && chmod 700 config
cp secrets.js.example config/sso-secrets.js
$EDITOR config/sso-secrets.js # set ldap.bindPassword, oauth.jwtSecret, ...
docker compose up -d --build
The web UI comes up at http://localhost:3001. To kick the tires with no
config file at all, the entrypoint falls back to safe defaults
(dc=example,dc=com, admin password admin, an auto-generated JWT) — fine for
a local test, not for production.
Your domain is entered once, as the LDAP base DN (stack.ldapBaseDn); the other
LDAP DNs and the OAuth issuer derive from it and must stay consistent. See
DEPLOYMENT.md for the full config reference, the app_* env
vars, LDAPS/TLS, and backups.
An automated installer installs Node.js, Redis, and (on first run) OpenLDAP —
configuring the directory (modules, overlays, schema, the SSO groups) and
seeding /etc/sso-manager/secrets.js with a generated admin password and JWT
secret — then deploys the app to /opt/theta42/sso-manager and starts a
systemd service:
wget -O - https://raw.githubusercontent.com/theta42/sso-manager-node/master/install.sh | sudo bash
That's it — LDAP and the app are both live afterward. Edit
/etc/sso-manager/secrets.js (org name, SMTP, a non-default base DN, ...) and
restart the service to customize. It's idempotent and safe to re-run —
re-running it updates the app in place (never touching LDAP or the secrets
file again) and prints the version you're updating from and to (e.g. Updated v1.1.13 -> v1.1.14), or Already up to date if there's nothing new. Full
details, including env var overrides (LDAP_BASE_DN, SKIP_LDAP, ...), in
DEPLOYMENT.md under Method 2: Bare metal.
Secrets are loaded from OpenBao at boot via
@simpleworkjs/bao-conf, which
deep-merges secret/sso-manager/conf over the file-loaded config (fail-soft:
if OpenBao is unreachable, boot continues from CONF_SECRETS). The SSO
authenticates to OpenBao with the scoped VAULT_TOKEN (env, policy
sso-broker) — never the root token.
The SSO also acts as the vault broker for the whole stack: it mints
per-user (user-<uid>) and per-admin (sso-admin) tokens through the
sso-broker token role and exposes the personal-secrets UI at Vault → My
Secrets (secret/users/<uid>/*, server-side token injection + path-scope
guard) and an admin Apps tab to mint scoped tokens for external apps
(secret/apps/<name>/*). The old utils/conf_manager.js was replaced by
@simpleworkjs/bao-conf; the admin Configuration UI (/api/conf) now
writes secret/sso-manager/conf through bao-conf.set.
The config/*-secrets.js files are operator-edit seed artifacts (gitignored),
not the authoritative store. For the full architecture, policies, token model,
and rotation procedure, see theta-env's
Secrets docs.
┌─────────────┐
│ Browser / │
│ OIDC apps │
└──────┬──────┘
│ HTTP/HTTPS
▼
┌────────────────────────┐ ┌─────────────┐
│ Express SSO Manager │◄────►│ Redis │
│ - OIDC provider │ │ - sessions │
│ - web UI (:3001) │ │ - models │
│ - management API │ └─────────────┘
└────────┬───────────────┘
│ ldapi/ldap (localhost)
▼
┌────────────────────────┐
│ OpenLDAP (slapd) │
│ - users / groups │
│ - LDAPS :636 │─── Linux hosts + LDAP apps bind directly
│ - StartTLS :389 │
└────────────────────────┘
The nitty LDAP details (overlay setup, the custom theta42Person schema, the
required groups, LDAPS/TLS, direct-bind service accounts) live in:
app_* env reference, LDAPS/TLS, backups, troubleshooting./docs —
no internet access required.If you are pointing the app at your own existing LDAP server, see
LDAP requirements in DEPLOYMENT.md — the directory needs the
pw-sha2, ppolicy, memberof, and refint modules plus a small custom
schema. The bundled Docker image and install.sh set all of that up for you.
Required groups: app_sso_admin (full admin), app_sso_oauth_admin (manage
OAuth clients only), app_sso_invite (invitation management) — see
DEPLOYMENT.md for the full setup.
cd nodejs
npm install
npm run dev # nodemon auto-reload
npm test # jest test suite
MIT — see LICENSE.