

























Limen is out today: a modern, composable authentication library for Go.
If you've ever built auth in Go, you know the drill. You pull in bcrypt, glue it to database/sql, reach for
golang-jwt or roll your own sessions, wire up golang.org/x/oauth2, add a CSRF package, remember to set SameSite,
forget to rotate something, and six files later you have a fragile thing you don't want to touch. Meanwhile the JavaScript
ecosystem has better-auth: one import, composable plugins, sensible defaults.
Limen brings that developer experience to Go, while staying idiomatic: small interfaces, explicit configuration, no magic, no framework lock-in.
Limen is a plugin-first authentication library. The core ships with the things every auth system needs: session management, cookie handling, schema, hooks, rate limiting, and security primitives. Every authentication method lives in its own importable Go module.
You compose exactly the auth stack your application needs:
plugins/credential-passwordplugins/two-factor (TOTP, backup codes)adapters/gorm or adapters/sqlhttp.Handler. It drops into net/http, Gin, Echo, Chi, Fiber, or anywhere an http.Handler fits.database/sql or GORM. Add your own adapter for anything else.SameSite=Lax, CSRF protections, password hashing with sane cost factors, session rotation on privilege changes.Plugin interface, and a schema system that lets you add custom user fields without forking.Let's build a real auth layer from scratch: sign up, sign in, protected route, OAuth, and 2FA. You'll need Go 1.25+ and a Postgres database.
go get github.com/thecodearcher/limen
go get github.com/thecodearcher/limen/adapters/sql
go get github.com/thecodearcher/limen/plugins/credential-passwordCreate main.go:
package main
import (
"database/sql"
"log"
"net/http"
"os"
_ "github.com/lib/pq"
"github.com/thecodearcher/limen"
sqladapter "github.com/thecodearcher/limen/adapters/sql"
credentialpassword "github.com/thecodearcher/limen/plugins/credential-password"
)
func main() {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
defer db.Close()
auth, err := limen.New(&limen.Config{
BaseURL: "http://localhost:8080",
Database: sqladapter.NewPostgreSQL(db),
Secret: []byte(os.Getenv("LIMEN_SECRET")), // 32 bytes
Plugins: []limen.Plugin{
credentialpassword.New(),
},
})
if err != nil {
log.Fatal(err)
}
mux := http.NewServeMux()
mux.Handle("/api/auth/", auth.Handler())
log.Println("listening on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}That's a full auth server. auth.Handler() mounts everything the enabled plugins expose under /api/auth/. No manual route wiring.
Generate the schema migrations with the limen CLI, apply them to your database, set LIMEN_SECRET to a 32-byte value (openssl rand -hex 16 will do), and you're up.
credential-password contributes two endpoints:
POST /api/auth/signup/credential
Content-Type: application/json
{ "email": "[email protected]", "password": "correct-horse-battery" }POST /api/auth/signin/credential
Content-Type: application/json
{ "credential": "[email protected]", "password": "correct-horse-battery" }On success, Limen creates a session and sets a signed, HttpOnly cookie.
The same auth value you passed plugins into is how you read the session back out:
mux.HandleFunc("GET /api/me", func(w http.ResponseWriter, r *http.Request) {
session, err := auth.GetSession(r)
if err != nil || session == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// session.User and session.Session are fully typed
json.NewEncoder(w).Encode(session.User)
})Use it in middleware, in a handler, in a Gin context. Limen doesn't care; it only needs the *http.Request.
Two go gets and a line of config:
go get github.com/thecodearcher/limen/plugins/oauth
go get github.com/thecodearcher/limen/plugins/oauth-googleimport (
"github.com/thecodearcher/limen/plugins/oauth"
oauthgoogle "github.com/thecodearcher/limen/plugins/oauth-google"
)
auth, _ := limen.New(&limen.Config{
// ... BaseURL, Database, Secret ...
Plugins: []limen.Plugin{
credentialpassword.New(),
oauth.New(oauth.WithProviders(
oauthgoogle.New(),
)),
},
})Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in your environment and you're done. If you'd rather pass them explicitly, oauthgoogle.WithClientID(...) and WithClientSecret(...) are there.
Limen now exposes:
GET /api/auth/oauth/google/authorize?callback_url=https://yourapp.com/callback
GET /api/auth/oauth/google/callbackIt handles the PKCE flow, state verification, token exchange, profile fetch, and links or creates the user, then drops
them into the same session system the email/password flow uses. auth.GetSession(r) works exactly the same way, regardless
of how the user signed in.
Want GitHub too? Add oauthgithub.New(...) to WithProviders. That's the whole change.
go get github.com/thecodearcher/limen/plugins/two-factorimport twofactor "github.com/thecodearcher/limen/plugins/two-factor"
Plugins: []limen.Plugin{
credentialpassword.New(),
oauth.New( /* ... */ ),
twofactor.New(),
},Your users can now enroll a TOTP authenticator and download backup codes. On the next sign-in, they get a 2FA challenge instead of an immediate session. Limen handles the challenge state, step-up flow, and recovery codes.
Need a display_name on every user? Use the schema system:
limen.WithSchemaUser(
limen.WithUserAdditionalFields(func(ctx *limen.AdditionalFieldsContext) (map[string]any, error) {
if ctx.IsEmpty("display_name") {
return nil, limen.NewLimenError("display_name is required", http.StatusBadRequest, nil)
}
return map[string]any{
"display_name": ctx.GetBodyValue("display_name"),
}, nil
}),
)Extra fields on the sign-up body land directly in your user row. No migration dance, no plugin rewrite.
Out of the box, Limen gives you:
limen CLI for scaffolding and migrationsnet/http, Gin, GORM, OAuth, and two-factor@limen/client: a typed TypeScript client with full API surface coverage@limen/react: React hooks (useSession, useLimen) built on top of the clientOpen an issue if you hit something. Open a PR if you want a plugin that doesn't exist yet. Limen is designed to be extended. That's the whole point.
Go deserves better auth. Let's build it ❤️
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。