Composable user management and authentication for Go HTTP applications.
The library is a set of small building blocks — an identity service, a password service, per-request authenticators, multi-step login and registration engines, and optional credential services (TOTP, recovery codes, email/SMS codes, personal access tokens, server-side sessions). You construct only the pieces you need and wire them together yourself: what you never construct never creates a table.
| Layer | Packages | Role |
|---|---|---|
| Vocabulary | root (userauth) |
Domain types (User), capability interfaces (UserGetter, …), errors — no logic |
| Per-request auth | auth/ — cookieauth, basicauth, headerauth, tokenauth, chain |
Authenticate every request on its own (middleware) |
| Flows | flow/ — login, register, emailchange, pat |
Multi-step engines that establish or change credentials |
| Services | service/ — user, password, session, totp, recoverycodes, verificationcode, pat, secondfactor, throttle, cipher |
Each owns one policy and delegates persistence to its own store interface (GORM store/db and store/memory implementations ship with each) |
Interfaces are small and defined at the consumer, so any piece can be replaced:
bring your own user storage by implementing user.Store — or just
userauth.UserGetter if all you need is login.
The minimal useful setup is the identity service, the password service, and a
cookie-session login flow. Two tables (user_models, user_groups), no other
state:
db, _ := gorm.Open(sqlite.Open("users.db"), &gorm.Config{TranslateError: true})
// identity + passwords
userStore, _ := userdb.New(db) // service/user/store/db
users, _ := user.NewService(userStore, user.Opts{DefaultEnabled: true})
passwords, _ := password.NewService(password.Opts{Cost: 12, Rehash: users})
registrar := user.Registrar{Users: users, Password: passwords} // creates accounts from plaintext
// cookie sessions (stateless flavour: everything lives in the encrypted cookie)
cookies, _ := cookieauth.NewCookieStore(hashKey, blockKey)
sessions, _ := cookieauth.New(cookieauth.Cfg{Store: cookies})
// the login engine: verify a password, establish the session
flow := &login.Flow{
Users: users,
Methods: []login.Method{login.PasswordMethod{Users: users, Password: passwords}},
Policy: login.RequireAny(login.Chain{login.MethodPassword}),
Session: sessions,
}Your login handler calls flow.Submit(...); protected routes use
sessions.Middleware. A complete, runnable version of exactly this is
demo/examples/login/password.go.
For the whole feature set over one database — every store, the delete cascade,
session revocation on password change, and a composed second-factor provider —
copy demo/setup. It is the reference wiring: compiled,
copyable, and its tests pin the guarantees (deleting a user purges every
satellite table, a password change revokes sessions, the full setup costs
exactly nine tables).
stores, err := setup.New(db, setup.Opts{Cost: 12, DefaultEnabled: true})
// stores.Users, stores.Passwords, stores.Registrar, stores.Sessions,
// stores.TOTP, stores.Recovery, stores.PATs, ..., stores.ProviderEvery row links a working example in the demo; each example uses only the library's public API and can be copied into a real application.
| I want… | Use | Demo example |
|---|---|---|
| Password form login | flow/login + auth/cookieauth |
demo/examples/login/password.go |
| Passwordless email-code login | service/verificationcode + flow/login |
demo/examples/login/email.go |
| Two-step password + TOTP login | service/totp, login.TOTPMethod |
demo/examples/login/totp.go |
| Recovery codes (lost authenticator) | service/recoverycodes, login.RecoveryMethod |
demo/examples/login/recovery.go |
| A JSON login API for a SPA | flow/login/handlers |
demo/examples/login/api.go |
| HTTP Basic auth | auth/basicauth |
demo/examples/auth/basic.go |
| Trusted-header auth behind a proxy (Authelia, mod_auth_mellon, …) | auth/headerauth |
demo/examples/auth/header.go |
| API tokens (PATs), also in a browser+API mix | service/pat + auth/tokenauth + auth/chain |
demo/examples/auth/token.go, chain.go |
| Self-registration (forms or JSON) | flow/register (+ handlers) |
demo/examples/register |
| A profile page: password/email change, 2FA enrolment, sessions, tokens | service/user, service/totp, service/session, flow/pat |
demo/examples/profile |
| Admin user management, first-run bootstrap | service/user (List, Bootstrap) |
demo/examples/admin |
| Per-user choice of second factor | compose secondfactor.Provider |
demo/setup |
auth/cookieauth runs either stateless — the whole session lives in an
encrypted cookie, nothing server-side, no way to revoke remotely — or against
a registry (service/session): the cookie carries an opaque ID, the
server stores the session, and you get list/revoke ("log out my other
devices"), revocation on password change, and per-user session caps. Pass
cookieauth.Cfg.Store for the first, cookieauth.Cfg.Registry for the
second. The stateless flavour never imports the registry — consumers that
don't use it don't pay for it.
go run ./demo # then open http://localhost:8085Every feature above is mounted with an index page describing it. See
demo/README.md.
Design decisions, package placement rules, and migration guides for breaking
changes live in docs/agents/ — start with
architecture.md.