go-authkit

module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT

README

go-authkit

Reusable, framework- and database-agnostic authentication for Go services: local password login, opaque server-side sessions, OIDC identity linking, CSRF, and role-based access control. Extracted so security fixes land in one place and propagate to every consumer via a version bump.

Design

The database is fully decoupled behind the auth.Store interface. Business logic in auth.Service contains no SQL and no HTTP framework dependency. HTTP wiring lives in optional sibling packages that target the standard net/http (so they compose with chi, gorilla, echo, gin adapters, etc.).

auth/            Service, Store interface, User/Session types, password (Argon2id), cookies
store/sqlite/    SQLite implementation of auth.Store (bring your own *sql.DB driver)
oidc/            OIDC Authorization Code flow (discovery, state/nonce, callback)
httpmw/          net/http middleware: RequireAuth, RequireCSRF, RequireAdmin, RequireRole

To support Postgres in another project, implement auth.Store against Postgres; nothing else changes.

Install

go get github.com/blurrycontour/go-authkit@latest

Quick start (SQLite + chi)

import (
    "database/sql"

    _ "modernc.org/sqlite"

    "github.com/blurrycontour/go-authkit/auth"
    "github.com/blurrycontour/go-authkit/httpmw"
    sqlitestore "github.com/blurrycontour/go-authkit/store/sqlite"
)

db, _ := sql.Open("sqlite", "file:app.db?_pragma=foreign_keys(ON)")

store := sqlitestore.New(db)
if err := store.Migrate(ctx); err != nil { /* ... */ }   // idempotent schema

authSvc := auth.NewService(store, auth.Config{
    SessionCookieName: "app_session",
    SessionTTL:        30 * 24 * time.Hour,
})

_ = authSvc.EnsureBootstrapAdmin(ctx, auth.BootstrapAdmin{
    Username: os.Getenv("ADMIN_USER"),
    Email:    os.Getenv("ADMIN_EMAIL"),
    Password: os.Getenv("ADMIN_PASS"),
})

mw := &httpmw.Middleware{Auth: authSvc, Secure: requestIsSecure}

r := chi.NewRouter()
r.Post("/api/login", loginHandler(authSvc, mw))
r.With(mw.RequireAuth).Get("/api/me", meHandler)
r.With(mw.RequireAuth, mw.RequireCSRF).Post("/api/logout", logoutHandler(authSvc, mw))
r.With(mw.RequireAuth, mw.RequireAdmin).Get("/api/admin/users", listUsers(authSvc))
Login handler (sets session + CSRF cookies)
func loginHandler(a *auth.Service, mw *httpmw.Middleware) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req struct{ Identifier, Password string }
        _ = json.NewDecoder(r.Body).Decode(&req)

        user, sid, exp, err := a.Login(r.Context(), req.Identifier, req.Password, r.UserAgent(), r.RemoteAddr)
        if err != nil {
            http.Error(w, "invalid credentials", http.StatusUnauthorized)
            return
        }
        secure := requestIsSecure(r)
        http.SetCookie(w, a.SessionCookie(sid, exp, secure))
        csrf, _ := mw.IssueCSRFCookie(r)
        http.SetCookie(w, csrf)
        json.NewEncoder(w).Encode(map[string]any{"user": user, "csrfToken": csrf.Value})
    }
}

OIDC

The oidc.Handler runs the Authorization Code flow and delegates user resolution to the auth.Service. Effective config is supplied per request via ConfigFunc, so you can merge env vars with admin-UI settings without a restart.

oh := &oidc.Handler{
    Auth: authSvc,
    ConfigFunc: func(ctx context.Context) (oidc.Config, error) {
        return loadEffectiveOIDCConfig(ctx) // your merge of env + DB
    },
    Secure:   requestIsSecure,
    ClientIP: clientIP,
    OnSuccess: func(w http.ResponseWriter, r *http.Request, u *auth.User, sid string, exp time.Time) {
        http.SetCookie(w, authSvc.SessionCookie(sid, exp, requestIsSecure(r)))
        csrf, _ := mw.IssueCSRFCookie(r)
        http.SetCookie(w, csrf)
        http.Redirect(w, r, "/", http.StatusFound)
    },
}
r.Get("/api/auth/oidc/login", oh.Login)
r.Get("/api/auth/oidc/callback", oh.Callback)
Security note: email-based linking

An OIDC identity is linked to a pre-existing local account by email address only when the provider asserts email_verified: true. Unverified or absent emails get a synthesized @oidc.local placeholder, preventing account takeover via an attacker-controlled email that matches a victim's address.

CSRF

Stateless double-submit cookie. The CSRF cookie is readable by JS; the client echoes it in the X-CSRF-Token header on unsafe methods. RequireCSRF compares them in constant time. Combined with SameSite=Lax session cookies this blocks cross-site request forgery.

Sentinel errors

auth.ErrNotFound, ErrInvalidCredentials, ErrInactive, ErrPasswordLoginDisabled, ErrConflict, ErrRegistrationDisabled. Compare with errors.Is.

Versioning

Semantic versioning. Security patches ship as patch releases so consumers can go get -u (or automate with Dependabot/Renovate) and re-run store.Migrate. Breaking API/schema changes go in a new major (/v2).

Directories

Path Synopsis
Package auth provides framework- and database-agnostic authentication primitives: local password login, opaque server-side sessions, OIDC identity linking, and user management.
Package auth provides framework- and database-agnostic authentication primitives: local password login, opaque server-side sessions, OIDC identity linking, and user management.
Package httpmw provides framework-agnostic net/http middleware for authkit: session authentication, CSRF (double-submit cookie), and role gating.
Package httpmw provides framework-agnostic net/http middleware for authkit: session authentication, CSRF (double-submit cookie), and role gating.
Package oidc provides a thin, framework-agnostic OIDC Authorization Code flow (with PKCE-less nonce + state) built on github.com/coreos/go-oidc.
Package oidc provides a thin, framework-agnostic OIDC Authorization Code flow (with PKCE-less nonce + state) built on github.com/coreos/go-oidc.
store
sqlite
Package sqlite provides a SQLite-backed implementation of auth.Store.
Package sqlite provides a SQLite-backed implementation of auth.Store.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL