theauth

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2026 License: MIT Imports: 15 Imported by: 0

README

TheAuth — Go

The auth library for AI agents and the humans who run them.

Session-based auth for Go with magic links, opaque tokens, and chi middleware. Postgres + in-memory storage. Designed to ship.

🚧 v0.1 pre-release. OAuth providers (v0.2), TOTP/passkeys (v0.3+), MCP OAuth 2.1 (v2.0).

Install

go get github.com/glincker/theauth-go

Quickstart

package main

import (
	"net/http"

	"github.com/glincker/theauth-go"
	"github.com/glincker/theauth-go/storage/memory"
	"github.com/go-chi/chi/v5"
)

func main() {
	a, _ := theauth.New(theauth.Config{
		Storage: memory.New(),
		BaseURL: "http://localhost:8080",
	})

	r := chi.NewRouter()
	a.Mount(r) // POST /auth/magic-link, GET /auth/me, etc.

	r.With(a.RequireAuth()).Get("/secret", func(w http.ResponseWriter, r *http.Request) {
		user, _ := theauth.UserFromContext(r.Context())
		w.Write([]byte("hello " + user.Email))
	})

	http.ListenAndServe(":8080", r)
}

Full runnable example: examples/chi-app/.

Storage backends

  • storage/memory — in-memory (tests, demos)
  • storage/postgres — pgx + sqlc-generated queries
pool, _ := pgxpool.New(ctx, "postgres://...")
a, _ := theauth.New(theauth.Config{
    Storage: postgres.New(pool),
    BaseURL: "https://myapp.com",
})

Run migrations from storage/postgres/migrations/ via golang-migrate.

Email senders

  • email.Noop — logs to stdout (default)
  • email.SMTP — minimal SMTP (set host, port, from) — coming v0.2

Custom: implement email.Sender.

Roadmap

  • v0.1 ✅ Magic links, sessions, chi middleware, Postgres + in-memory
  • v0.2 — OAuth providers (GitHub first), refresh-token rotation, SMTP email sender
  • v0.3 — Google + Discord + Microsoft OAuth, TOTP 2FA
  • v0.4 — WebAuthn / passkeys
  • v1.0 — All 17 OAuth providers + SAML
  • v2.0 — MCP OAuth 2.1 server + agent identity + delegation chains

Sibling project

github.com/glincker/theauth — TypeScript implementation (formerly kavachos, rebranded 2026-06).

License

MIT.

Documentation

Overview

Package theauth provides session-based authentication for Go applications.

TheAuth ships magic-link email auth, opaque session tokens with revocation, and chi-friendly middleware. Storage backends include in-memory and Postgres (pgx + sqlc). OAuth providers, TOTP, WebAuthn, and MCP OAuth 2.1 land in future versions — see the README roadmap.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidToken     = errors.New("theauth: invalid token")
	ErrSessionExpired   = errors.New("theauth: session expired")
	ErrUserNotFound     = errors.New("theauth: user not found")
	ErrMagicLinkExpired = errors.New("theauth: magic link expired")
	ErrMagicLinkUsed    = errors.New("theauth: magic link already used")
	ErrEmailNotVerified = errors.New("theauth: email not verified")

	// ErrStorageNotFound is the canonical "row missing" sentinel that storage
	// adapters return on lookup misses. Lives in the root package so service
	// code can errors.Is-check without importing the storage package
	// (which would create an import cycle).
	ErrStorageNotFound = errors.New("theauth: storage row not found")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	Storage      Storage
	EmailSender  email.Sender
	BaseURL      string
	SigningKey   ed25519.PrivateKey
	SessionTTL   time.Duration
	MagicLinkTTL time.Duration
	CookieName   string
	SecureCookie bool
}

Config holds the wiring for a TheAuth instance.

Storage and BaseURL are required. Everything else has sensible defaults applied by New: SessionTTL=24h, MagicLinkTTL=15m, CookieName="theauth_session", EmailSender=email.Noop{}. SigningKey is reserved for future JWT signing (v0.2+); v0.1 uses opaque tokens and leaves the field nil.

type MagicLink struct {
	ID        ULID       `json:"id"`
	Email     string     `json:"email"`
	TokenHash []byte     `json:"-"`
	ExpiresAt time.Time  `json:"expiresAt"`
	UsedAt    *time.Time `json:"usedAt,omitempty"`
	CreatedAt time.Time  `json:"createdAt"`
}

type Session

type Session struct {
	ID        ULID       `json:"id"`
	UserID    ULID       `json:"userId"`
	TokenHash []byte     `json:"-"` // never serialize raw hash
	UserAgent string     `json:"userAgent"`
	IP        string     `json:"ip"`
	CreatedAt time.Time  `json:"createdAt"`
	ExpiresAt time.Time  `json:"expiresAt"`
	RevokedAt *time.Time `json:"revokedAt,omitempty"`
}

func SessionFromContext

func SessionFromContext(ctx context.Context) (*Session, bool)

SessionFromContext returns the Session attached by Authn middleware, if any. Returns false when the request is anonymous.

func (Session) Expired

func (s Session) Expired(now time.Time) bool

Expired reports whether the session is no longer usable at the given time.

type Storage

type Storage interface {
	// Users
	CreateUser(ctx context.Context, u User) (User, error)
	UserByEmail(ctx context.Context, email string) (*User, error)
	UserByID(ctx context.Context, id ULID) (*User, error)
	MarkEmailVerified(ctx context.Context, userID ULID) error

	// Sessions
	CreateSession(ctx context.Context, s Session) (Session, error)
	SessionByTokenHash(ctx context.Context, hash []byte) (*Session, error)
	RevokeSession(ctx context.Context, id ULID) error
	RevokeUserSessions(ctx context.Context, userID ULID) error

	// Magic links
	CreateMagicLink(ctx context.Context, ml MagicLink) error
	ConsumeMagicLink(ctx context.Context, tokenHash []byte) (*MagicLink, error)
}

Storage is the persistence contract TheAuth depends on. Adapters live in sub-packages (storage/memory, storage/postgres). Defined here so that service code in this package can reference it without importing the storage sub-package (which would create an import cycle, because storage imports this package for the model types).

The storage package re-exports this as storage.Storage so consumers can keep importing it from the conventional location.

type TheAuth

type TheAuth struct {
	// contains filtered or unexported fields
}

TheAuth is the public entry point — constructed once at app start and shared across handlers.

func New

func New(cfg Config) (*TheAuth, error)

New validates the Config, applies defaults, and returns a ready TheAuth.

func (*TheAuth) Authn

func (a *TheAuth) Authn() func(http.Handler) http.Handler

Authn looks for a session cookie, validates it, and adds the user + session to the request context. Does NOT reject anonymous requests — pair with RequireAuth.

func (*TheAuth) Mount

func (a *TheAuth) Mount(r chi.Router)

Mount wires TheAuth's HTTP routes onto the supplied chi router under /auth. Routes:

POST   /auth/magic-link            request a magic link
GET    /auth/magic-link/verify     consume a magic link, set session cookie
GET    /auth/me                    return the authenticated user (RequireAuth)
DELETE /auth/sessions/current      revoke the current session (RequireAuth)

func (*TheAuth) RequireAuth

func (a *TheAuth) RequireAuth() func(http.Handler) http.Handler

RequireAuth runs Authn, then rejects requests that don't have a session.

type ULID

type ULID = ulid.ULID

ULID is the canonical ID type — generated in app, stored as uuid in Postgres.

type User

type User struct {
	ID              ULID       `json:"id"`
	Email           string     `json:"email"`
	EmailVerifiedAt *time.Time `json:"emailVerifiedAt,omitempty"`
	Name            string     `json:"name"`
	AvatarURL       string     `json:"avatarUrl"`
	CreatedAt       time.Time  `json:"createdAt"`
	UpdatedAt       time.Time  `json:"updatedAt"`
}

func UserFromContext

func UserFromContext(ctx context.Context) (*User, bool)

UserFromContext returns the authenticated User attached by Authn middleware, if any. Returns false when the request is anonymous.

Directories

Path Synopsis
internal
mcpresource module
postgres
Package postgres provides a Postgres-backed storage.Storage implementation built on top of sqlc-generated queries and pgx/v5.
Package postgres provides a Postgres-backed storage.Storage implementation built on top of sqlc-generated queries and pgx/v5.

Jump to

Keyboard shortcuts

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