egauth

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 1 Imported by: 0

README

egauth

A complete, composable, non-opinionated authentication toolkit for Go.

egauth is a set of independent modules in the style of the standard library's database/sql: you import the ones you need and wire them together with dependency injection. There is no framework to adopt — egauth never owns your HTTP router, your database access, or your conventions. Every module exposes a Service interface plus a Store contract, with in-memory and PostgreSQL (pgx) backends behind a shared cross-backend conformance suite.

Status: pre-1.0. The API is still settling; see Stability.

Modules

Module What it does
identity Accounts & credentials: register, login, password reset, email verification, magic link, change-password/email, account deletion, OAuth identity linking
tokens Stateless JWT access tokens + single-use refresh tokens with rotation & theft detection; API keys. Reference impl in tokens/jwt
sessions Server-side, revocable sessions with idle-timeout (Touch) and fixation defense (Rotate)
passwords Hashing/policy/breach seams + references: argon2, policy, breach/hibp, breach/offline
mfa TOTP (RFC 6238) with recovery codes
otp One-time codes (email/SMS), enumeration-safe HTTP handlers
passkey WebAuthn / passkeys, including discoverable (usernameless) login
oauth OAuth2 / OIDC (Google, GitHub, Discord), PKCE-S256, id_token/nonce/JWKS
delivery Optional reference SMTP mailer + template renderer + OTP sender
ratelimit, event, health Pluggable rate-limiting, audit-event, and readiness seams

Install

go get github.com/JLugagne/egauth

Requires Go 1.26+.

Quickstart: login + refresh

The recommended stateless stack is identity (verify credentials) + tokens/jwt (issue and rotate tokens). This wires it with the in-memory backends; swap in the pgx stores for production. It is the runnable package example (go test ./identity -run Example).

ctx := context.Background()
const tenant = "" // empty string is the single-tenant default partition

// identity: credential verification + account lifecycle
idStore := identitymem.NewStore() // identity/memory; or identity/pgx.NewStore(pool)
svc := identity.NewService(idStore, argon2.NewHasher(), policy.NewDefaultPolicy())

// tokens: stateless access tokens + refresh rotation.
// claimsProvider re-derives a user's claims on every refresh, so a disabled or
// role-changed user is re-evaluated rather than frozen at login.
claimsProvider := tokens.ClaimsProviderFunc[struct{}](
    func(_ context.Context, userID uuid.UUID, tenantID string) (tokens.Claims[struct{}], error) {
        return tokens.Claims[struct{}]{Subject: userID, TenantID: tenantID}, nil
    },
)
tokenStore := tokenmem.NewStore[struct{}]() // tokens/memory; or tokens/pgx.NewStore(pool)
issuer := jwt.New[struct{}](jwt.Config[struct{}]{
    Store:          tokenStore,
    Issuer:         "example-app",
    SecretKey:      hs256SecretFromYourSecretStore, // >= 32 bytes
    AccessTTL:      15 * time.Minute,
    RefreshTTL:     720 * time.Hour,
    ClaimsProvider: claimsProvider, // required for Rotate (refresh)
})

// register, authenticate, issue a token pair
user, err := svc.Register(ctx, tenant, "alice@example.com", password)
// ... handle err
pair, err := issuer.IssueTokenPair(ctx, tokens.Claims[struct{}]{Subject: user.ID, TenantID: tenant})

// later: refresh — rotation single-use-consumes the old refresh token
// (replaying it trips theft detection and revokes the family)
next, err := issuer.Rotate(ctx, tenant, pair.RefreshToken)
Over HTTP

The handlers are à-la-carte http.HandlerFunc factories you mount on your own mux — egauth imposes no router:

claimsOf := func(u *identity.User) tokens.Claims[struct{}] {
    return tokens.Claims[struct{}]{Subject: u.ID, TenantID: u.TenantID}
}
mux := http.NewServeMux()
mux.Handle("POST /login",   identity.LoginHandler(svc, issuer, claimsOf))
mux.Handle("POST /refresh", tokens.RefreshHandler[struct{}](issuer))    // issuer is the Rotator
mux.Handle("POST /logout",  tokens.LogoutHandler(tokenStore))           // revokes the refresh family

// protect a route with the access-token middleware:
mux.Handle("GET /me", tokens.RequireAuth[struct{}](issuer,
    func(w http.ResponseWriter, r *http.Request, actor egauth.Actor, _ struct{}) {
        // actor.UserID / actor.TenantID are authenticated
    }))

Multi-tenancy

Every tenant-scoped operation takes an explicit tenantID string argument. An empty string ("") is the valid single-tenant default partition — passing it explicitly keeps the tenant boundary visible at every call site (the defense against cross-tenant access / IDOR).

For a genuinely single-tenant application, wrap a Service once and drop the argument:

app := identity.NewSingleTenant(svc) // every call uses the empty tenant ("")
user, err := app.Register(ctx, "bob@example.com", password)

SingleTenant facades exist on identity, sessions, mfa, otp, passkey, and tokens/jwt.

Storage backends

Each module ships two interchangeable Store implementations behind one contract:

  • <module>/memory — zero-dependency, for tests and single-process apps.
  • <module>/pgx — PostgreSQL via jackc/pgx. Call pgx.Migrate(ctx, pool) once at startup (forward-only, versioned via a schema_migrations table; re-running is a no-op).
pool, _ := pgxpool.New(ctx, dsn)
_ = identitypgx.Migrate(ctx, pool)
store := identitypgx.NewStore(pool)

Security

egauth is enumeration-safe by default (uniform responses + decoy hashing), enforces brute-force lockout, pins JWTs to HS256 (rejecting none/alg-confusion), rotates refresh tokens with family-based theft detection, stores only SHA-256 hashes of refresh/API/session/OTP secrets, and caps pre-auth body size against hashing-DoS. Credential-bearing types redact their secrets on fmt/slog. Read SECURITY.md for the full model — including the explicit trade-offs (e.g. TOTP secrets stored recoverably, accepted account-existence disclosures) and the boundaries egauth leaves to the application (CSRF tokens, rate-limit policy, mail/SMS transport).

Documentation

Each module has a package overview (go doc github.com/JLugagne/egauth/identity) and the login-critical packages carry runnable examples. See also PRD.md for design goals.

Stability

Pre-1.0: the API may change between minor versions until it settles, at which point releases will follow SemVer with a CHANGELOG. Pin a commit or tag in go.mod for reproducible builds.

Documentation

Overview

Package egauth is a composable authentication toolkit for Go: a set of independent modules you import à la carte and wire together yourself, in the style of the standard library's database/sql — rather than a framework that owns your HTTP router, your database access, or your conventions. The root package itself is deliberately tiny: it exports only Actor, the explicit authenticated-principal value passed to handlers (never smuggled through context.Context). All behavior lives in the sub-packages below.

Modules

identity   Accounts & credentials: register, login (Authenticate), password reset, email
           verification, magic-link login, change-password / change-email, phone verification,
           an independent recovery channel, account deletion, and OAuth identity linking.
tokens     Stateless JWT access tokens + single-use refresh tokens with rotation and theft
           detection, plus API keys. Reference implementation in tokens/jwt.
sessions   Server-side, revocable sessions with idle-timeout (Touch) and fixation defense
           (Rotate).
passwords  Hashing / policy / breach-check seams plus references: passwords/argon2,
           passwords/policy, passwords/breach/hibp, passwords/breach/offline.
mfa        TOTP (RFC 6238) with recovery codes. (SMS is intentionally excluded as a factor.)
otp        One-time codes (email/SMS), with enumeration-safe HTTP handlers.
passkey    WebAuthn / passkeys, including discoverable (usernameless) login.
oauth      OAuth2 / OIDC (Google, GitHub, Discord) with PKCE-S256 and id_token/nonce/JWKS.
delivery   Optional reference SMTP mailer, template renderer, OTP sender and SMS phone-verifier.
ratelimit  Pluggable rate-limiting Limiter + token-bucket reference + middleware.
event      Dependency-free security-event Sink seam (audit logging, slog adapter).
health     Optional Store Ping/readiness seam.

Composable by design

There is no top-level constructor that bundles everything, and that is intentional: each module has its own Service interface, its own Store contract (with in-memory and pgx backends behind a shared cross-backend conformance suite), and functional-option dependency injection. You compose exactly the stack you need. A typical password-login deployment wires identity (verifies credentials and manages the account lifecycle) with tokens (issues the access/refresh pair) — identity never issues tokens or sessions itself, so you pick the token backend that fits.

idStore := identitymem.NewStore()                          // or identity/pgx.NewStore(pool)
idSvc := identity.NewService(idStore, argon2.NewHasher(), policy.NewDefaultPolicy())

tkStore := tokensmem.NewStore()                            // or tokens/pgx.NewStore(pool)
issuer := jwt.New(jwt.Config{SecretKey: secret}, tkStore)  // tokens/jwt reference issuer

user, _ := idSvc.Register(ctx, tenantID, email, password)
pair, _ := issuer.Issue(ctx, tenantID, tokens.Claims[MyClaims]{Subject: user.ID.String()})

The complete, runnable login + refresh wiring (including the HTTP handlers) lives in the identity package's example tests — see Example, ExampleNewSingleTenant and ExampleLoginHandler.

Multi-tenancy

Multi-tenancy is explicit and pervasive: every Store and Service operation takes a tenantID string. The empty string is the valid single-tenant default partition, so a single-tenant application simply passes "" — or wraps a Service in that module's SingleTenant facade (e.g. identity.NewSingleTenant) to drop the argument from every call.

Security and stability

egauth is security-literate by default (Argon2id, enumeration-resistant auth paths, refresh rotation with theft detection, alg-pinned JWTs, secure-by-default cookies, pre-auth body caps). The full threat model is in SECURITY.md; the module overview and a copy-pasteable quickstart are in README.md. The API is pre-1.0 and still settling; pin a commit or tag in go.mod for reproducible builds (see the Stability section of README.md).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Actor

type Actor struct {
	UserID   uuid.UUID
	TenantID string
}

Actor represents the authenticated entity making a request. It is explicitly passed as an argument to handlers, never transported via context.Context.

Directories

Path Synopsis
adapters
otel module
pgx module
Package delivery provides reference implementations of the message-delivery seams egauth defines but deliberately does not fill itself: a standard-library SMTP Mailer (satisfying identity.Mailer), a Go-template-based message renderer you can fully override, and an OTP code Sender wired through an application-supplied contact-resolution seam.
Package delivery provides reference implementations of the message-delivery seams egauth defines but deliberately does not fill itself: a standard-library SMTP Mailer (satisfying identity.Mailer), a Go-template-based message renderer you can fully override, and an OTP code Sender wired through an application-supplied contact-resolution seam.
Package event defines egauth's optional security-event seam.
Package event defines egauth's optional security-event seam.
Package health defines the optional health-check seam implemented by egauth's pgx-backed stores, so readiness/liveness probes can be written against any store without depending on a specific backend or holding a separate handle to the underlying connection pool.
Package health defines the optional health-check seam implemented by egauth's pgx-backed stores, so readiness/liveness probes can be written against any store without depending on a specific backend or holding a separate handle to the underlying connection pool.
Package identity is egauth's account and credential-verification module: registration, password login (Authenticate), password reset, email verification, magic-link login, authenticated change-password / change-email, account deletion, and just-in-time provisioning of external (OAuth) identities.
Package identity is egauth's account and credential-verification module: registration, password login (Authenticate), password reset, email verification, magic-link login, authenticated change-password / change-email, account deletion, and just-in-time provisioning of external (OAuth) identities.
pgx
internal
doctest command
Command doctest guards the Hugo docs against API drift.
Command doctest guards the Hugo docs against API drift.
pgxmigrate
Package pgxmigrate provides the shared migration runner used by every pgx-backed store in egauth (sessions, identity, tokens, mfa, otp, passkey).
Package pgxmigrate provides the shared migration runner used by every pgx-backed store in egauth (sessions, identity, tokens, mfa, otp, passkey).
mfa
Package mfa implements multi-factor authentication: time-based one-time passwords (TOTP, RFC 6238 / RFC 4226) for authenticator apps, and single-use recovery codes.
Package mfa implements multi-factor authentication: time-based one-time passwords (TOTP, RFC 6238 / RFC 4226) for authenticator apps, and single-use recovery codes.
memory
Package memory provides an in-memory mfa.Store, primarily for tests and single-process use.
Package memory provides an in-memory mfa.Store, primarily for tests and single-process use.
pgx
Package pgx provides a PostgreSQL-backed mfa.Store using jackc/pgx.
Package pgx provides a PostgreSQL-backed mfa.Store using jackc/pgx.
storetest
Package storetest provides a shared contract test suite for mfa.Store implementations.
Package storetest provides a shared contract test suite for mfa.Store implementations.
Package oauth implements the OAuth2 authorization-code flow (with PKCE) as stateless, composable HTTP handlers.
Package oauth implements the OAuth2 authorization-code flow (with PKCE) as stateless, composable HTTP handlers.
pgx
otp
Package otp implements short numeric one-time passcodes (e.g.
Package otp implements short numeric one-time passcodes (e.g.
memory
Package memory provides an in-memory otp.Store, primarily for tests and single-process use.
Package memory provides an in-memory otp.Store, primarily for tests and single-process use.
pgx
Package pgx provides a PostgreSQL-backed otp.Store using jackc/pgx.
Package pgx provides a PostgreSQL-backed otp.Store using jackc/pgx.
storetest
Package storetest provides a shared conformance suite for otp.Store implementations.
Package storetest provides a shared conformance suite for otp.Store implementations.
Package passkey implements WebAuthn / FIDO2 passkeys (registration and login ceremonies) on top of the go-webauthn library, following egauth's conventions: a credential Store (memory + pgx implementations with a shared contract), a Service that runs the ceremonies, and à-la-carte HTTP handlers.
Package passkey implements WebAuthn / FIDO2 passkeys (registration and login ceremonies) on top of the go-webauthn library, following egauth's conventions: a credential Store (memory + pgx implementations with a shared contract), a Service that runs the ceremonies, and à-la-carte HTTP handlers.
memory
Package memory provides an in-memory passkey.Store, primarily for tests and single-process deployments.
Package memory provides an in-memory passkey.Store, primarily for tests and single-process deployments.
pgx
Package pgx provides a PostgreSQL-backed passkey.Store using jackc/pgx.
Package pgx provides a PostgreSQL-backed passkey.Store using jackc/pgx.
storetest
Package storetest provides a shared conformance suite for passkey.Store implementations.
Package storetest provides a shared conformance suite for passkey.Store implementations.
Package passwords defines egauth's password seams — Hasher (hash and constant-time compare), Policy (validate a candidate password), and BreachChecker (k-anonymity breach lookup) — plus the shared error sentinels and the MaxPasswordLength pre-hash DoS cap.
Package passwords defines egauth's password seams — Hasher (hash and constant-time compare), Policy (validate a candidate password), and BreachChecker (k-anonymity breach lookup) — plus the shared error sentinels and the MaxPasswordLength pre-hash DoS cap.
breach/hibp
Package hibp implements passwords.BreachChecker against the Have I Been Pwned "Pwned Passwords" range API using k-anonymity: the password is hashed with SHA-1, and only the first five hex characters of that digest are ever sent to the service.
Package hibp implements passwords.BreachChecker against the Have I Been Pwned "Pwned Passwords" range API using k-anonymity: the password is hashed with SHA-1, and only the first five hex characters of that digest are ever sent to the service.
breach/offline
Package offline implements passwords.BreachChecker against an in-memory set of known- compromised password SHA-1 hashes loaded once at startup (for example from the downloadable HIBP "Pwned Passwords" offline corpus, or a custom blocklist).
Package offline implements passwords.BreachChecker against an in-memory set of known- compromised password SHA-1 hashes loaded once at startup (for example from the downloadable HIBP "Pwned Passwords" offline corpus, or a custom blocklist).
Package ratelimit provides a small, pluggable request-throttling seam for the egauth HTTP handlers and a dependency-free in-memory token-bucket reference implementation.
Package ratelimit provides a small, pluggable request-throttling seam for the egauth HTTP handlers and a dependency-free in-memory token-bucket reference implementation.
Package sessions is egauth's server-side session module: opaque session tokens backed by a store, with sliding idle-timeout (Touch), rotation against session fixation (Rotate), and revocation.
Package sessions is egauth's server-side session module: opaque session tokens backed by a store, with sliding idle-timeout (Touch), rotation against session fixation (Rotate), and revocation.
pgx
Package tokens is egauth's stateless-token module: JWT access tokens plus single-use refresh tokens with rotation, family-based reuse/theft detection, and long-lived API keys.
Package tokens is egauth's stateless-token module: JWT access tokens plus single-use refresh tokens with rotation, family-based reuse/theft detection, and long-lived API keys.
jwt
pgx

Jump to

Keyboard shortcuts

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