Package Overview: dracory/auth
Last Updated: 2026-10-29
What is dracory/auth?
dracory/auth is a batteries-included authentication library for Go that provides ready-to-use authentication flows with minimal setup. It is implementation-agnostic — you bring your own database, session store, and email service; the library handles the authentication logic, UI pages, and API endpoints.
Core Philosophy
- Four Login Methods, One Composable Mux
- Email OTP (passwordless) — email-based verification codes
- Magic link — single-use emailed sign-in links
- Username/password — traditional authentication with password storage
- AuthKnight — delegated hosted login (external service verifies the user)
Methods are mounted together behind a single mux via NewMultiMethodAuth. The first method owns <endpoint>/login; secondary methods mount at <endpoint>/login-<name>. Custom methods implement the public types.Method interface — no library change required.
- Complete Solution
- Pre-built HTML pages for login, registration, password reset
- JSON API endpoints for SPA/mobile apps
- Authentication middleware for protecting routes
- Customizable email templates
- Production-Grade Security
- Structured error handling with error codes
- CSRF protection and per-route rate limiting
- OTP codes bound to random nonces (not store keys)
- Session invalidation on password reset
- Secure cookie defaults and input validation
- Structured logging for audit trails
- Flexible Storage + Hooks
- You implement the storage layer via callback functions — works with any database (SQL, NoSQL, in-memory)
types.PostAuthHooksdrives the shared post-authentication pipeline: allowlist → find-or-create → active check → session → cookie → redirect- Optional capabilities (session store, temp-key delete, post-auth hooks) are attached via type-asserted setters — the public interfaces stay stable
🏗️ Architecture Overview
Entry points
├── NewOtpAuth(ConfigOtp) → AuthOtpInterface
├── NewUsernameAndPasswordAuth(ConfigUsernameAndPassword) → AuthPasswordInterface
├── NewAuthKnightAuth(ConfigAuthKnight) → AuthAuthKnightInterface
└── NewMultiMethodAuth(endpoint, []types.Method) → composed mux
├── NewOTPMethodWithPage(a, page)
├── NewMagicLinkMethodFromAuth(a, page, linkCallback)
├── NewPasswordMethod(a, page)
└── NewAuthKnightMethod(a)
authImplementation
├── types/ — public configs, interfaces, Method, PostAuthHooks
├── internal/api/ — JSON endpoints (login, code-verify, magic-link,
│ register, password reset, impersonation, callbacks)
├── internal/ui/ — server-rendered pages (login, magic-link, register, …)
├── internal/core/ — shared post-auth pipeline
└── internal/rules/ — reusable rule checks (throttles, validation, registration policy)
📦 Package Organization
types/ — Public API types
config_shared.go—ConfigSharedembedded by all method configsconfig_passwordless.go,config_username_and_password.go,config_authknight.go— per-method configsauth_interfaces.go—AuthSharedInterface,AuthOtpInterface,AuthPasswordInterface,AuthAuthKnightInterfacemethod.go—Methodinterface (Name,PageHandler,AjaxHandler,CallbackRoute) +RouteProviderfor extra routespost_auth.go—PostAuthHooks(EmailAllowed, UserFindByEmailOrCreate, UserIsActive, SessionCreate, SetAuthCookie, RedirectURL, RegisterRules)session.go,cookie_config.go,observability.go,user_auth_options.go,messages.go
internal/api/ — JSON endpoints (one subdirectory per endpoint)
api_login, api_login_code_verify, api_magic_link_send, api_magic_link_callback, api_register, api_register_code_verify, api_logout, api_password_restore, api_password_reset, api_authenticate_via_username, api_authknight_callback, api_impersonate_start, api_impersonate_stop
internal/ui/ — Server-rendered pages
page_login, page_magic_link, page_login_code_verify, page_register, page_register_code_verify, page_logout, page_password_restore, page_password_reset, shared (page scaffold, AuthKnight redirect)
internal/ support packages
core (post-auth pipeline), rules (email send throttle, validation), links (route paths), emails, middlewares, helpers, testutils
Root package — public API
- Constructors:
NewOtpAuth,NewUsernameAndPasswordAuth,NewAuthKnightAuth,NewMultiMethodAuth - Method adapters:
NewOTPMethod(WithPage),NewMagicLinkMethod(FromAuth),NewPasswordMethod,NewAuthKnightMethod - Middleware:
WebAuthOrRedirectMiddleware,ApiAuthOrErrorMiddleware,WebAppendUserIdIfExistsMiddleware - Cookies:
AuthCookieSet,AuthCookieRemove,CookieName+types.WithSecureoptions
🔑 Multi-Method Composition
a, _ := auth.NewOtpAuth(cfg) // shared impl serves otp + magiclink
p, _ := auth.NewUsernameAndPasswordAuth(pcfg) // password impl
mma, err := auth.NewMultiMethodAuth("/auth", []types.Method{
auth.NewPasswordMethod(p, nil), // primary → /auth/login
auth.NewOTPMethodWithPage(a, nil), // → /auth/login-otp
auth.NewMagicLinkMethodFromAuth(a, nil, linkCallback), // → /auth/login-magiclink
})
mux.Handle("/auth/", http.StripPrefix("", mma.Router()))
- First method mounts at
GET|POST /auth/login; later methods at/auth/login-<name> - Method callbacks (
/magiclink-callback,/authknight-callback) mount once each RouteProvider.ExtraRoutes()mounts method-scoped routes (OTP code-verify, password restore/reset) only when that method is presentLoginAlternativesHTMLrenders an "OR — use another method" fragment for login pages
🔗 Post-Auth Pipeline
Every method's verify path funnels through core.PostAuth when any PostAuthHooks are configured — one place for allowlist checks, user find-or-create, active validation, session creation, cookie writing, and redirect resolution. When no hooks are set, the legacy per-flow behavior (token + FuncUserStoreAuthToken) is used.
🔑 Key Flows
Email OTP
POST /auth/api/login → generate code → FuncTemporaryKeySet → FuncEmailSend → POST /auth/api/login-code-verify → post-auth → session/token + cookie
Magic link
POST <login path> → store MagicLinkPayload{email, token, return, remember} under a nonce key → EmailSend/EmailEnqueue the link → GET /auth/magiclink-callback?token= → post-auth → redirect
Username/password
POST /auth/api/login → FuncUserLogin(username, password) → post-auth → token + cookie. Recovery via /password-restore + /password-reset (session invalidation built in).
AuthKnight
Login page redirects to the hosted AuthKnight flow → GET /auth/authknight-callback?once= → verify once with AuthKnight's /api/who → post-auth (existing users) or register-redirect with a verified-email temp key (new users).
Verified-email registration (unified)
OTP, magic link, and AuthKnight share one register-first pipeline:
enter email → issue proof (code / link / once) → verify
→ verified-email token (verified_key, 15 min, single-use)
→ /register form (email read-only) → single INSERT
The token resolves to the verified email server-side — the form never trusts a posted email. Existing accounts authenticate normally.
ConfigShared.UserCreationPolicy selects when the user row
is created: CreateUserOnAuth (default — created at verify
time, incomplete profiles gated to the completion form by
PostAuthHooks.RegistrationComplete) vs
CreateUserOnRegister (deferred — no row until the form
submits; requires a FuncUserExists or passwordless
find-by-email hook to distinguish accounts). Degraded configurations
fall back to session-first with a Warn log.
ConfigShared.FuncEmailCaptured(ctx, email, verified, options)
is an optional lead-capture hook fired on submit (verified=false
— unverified, attacker-controllable) and on successful verification
(verified=true) across login, register, and magic-link paths.
🔒 Middleware
WebAuthOrRedirectMiddleware— resolves token → userID into context; redirects to login on failureApiAuthOrErrorMiddleware— same check, JSON error on failureWebAppendUserIdIfExistsMiddleware— optional auth, never blocksauth.GetCurrentUserID(r)— retrieve the authenticated user ID
🧪 Examples
examples/ contains runnable apps: passwordless/, usernamepassword/, authknight/ — in-memory storage, email via localhost:1025, complete callback implementations.
📚 Related Documentation
- README.md — getting started
- rate_limit.html — rate limiting configuration
- impersonation.html — admin "login as" feature
- authknight.html — AuthKnight integration guide
- ui-patterns.html — how the built-in pages are rendered
- project-structure.html — detailed directory layout
- peers/ — feature comparisons vs peer auth solutions