Limen handles sessions, cookies, and security primitives. Auth methods — credentials, OAuth providers, 2FA — drop in as plugins. Idiomatic Go, plugin-first DX, no framework lock-in.
package main import ( "net/http" "github.com/thecodearcher/limen" "github.com/thecodearcher/limen/plugins/credential" "github.com/thecodearcher/limen/plugins/oauth" ) func main() { // 1. configure once: sessions + cookies + security primitives auth := limen.New(limen.Config{ SessionStore: limen.MemoryStore(), // or your own CookieName: "limen_session", SignKey: []byte(os.Getenv("SIGN_KEY")), }) // 2. compose plugins — bring only what you need auth.Use(credential.New(credential.Config{ Verify: verifyUserPassword, // you own the DB })) auth.Use(oauth.Google(oauth.GoogleConfig{ ClientID: os.Getenv("GOOGLE_ID"), ClientSecret: os.Getenv("GOOGLE_SECRET"), })) // 3. mount as a standard http.Handler — no framework imposed http.Handle("/auth/", auth) http.ListenAndServe(":8080", nil) }
Most Go auth libraries are either too thin (you write the session layer yourself) or too thick (you adopt their entire stack). Limen does the dangerous parts once and lets you bring everything else.
The parts that are easy to get subtly wrong (cookie flags, signing rotation, CSRF binding) live in the core and stay there.
limen.Plugin.Credentials, OAuth, 2FA today. Magic links, passkeys, SAML when you need them. Same interface, no special cases.
Limen never talks to your DB directly. You hand it a Verify func; it stays out of your model layer.
Each plugin is its own Go module — import what you use, nothing more.
Username + password auth. Limen handles hashing, timing-safe comparison, lockout, and the session handoff. You hand it one function: "given a username + password, return a user ID."
credential.New(credential.Config{ Verify: func(ctx context.Context, u, p string) (string, error) { user, err := db.FindByEmail(ctx, u) if err != nil { return "", err } if !user.CheckPassword(p) { return "", limen.ErrInvalidCreds } return user.ID, nil }, LockoutAfter: 5, })
Limen owes its plugin DX to better-auth. It also makes different trade-offs because Go has different idioms.
limen.Plugin — one interfaceVerify funchttp.HandlerRead the launch post for the design rationale, or jump straight to the source.