auth

package module
v1.1.2 Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2026 License: GPL-2.0, GPL-3.0 Imports: 21 Imported by: 0

README

auth

Go Reference Go version Go Report Card Test coverage Mutation OpenSSF Best Practices OpenSSF Scorecard

Go authentication library: Argon2id passwords, WebAuthn/passkeys, OIDC, sessions, API keys, and RBAC.

A standalone Go authentication library providing password hashing (Argon2id with OWASP parameters), WebAuthn/FIDO2 passkey ceremonies, OIDC provider integration with PKCE, session management with idle/absolute timeouts, API key generation and verification, CSRF token helpers, password-reset/email-verification token primitives, and role-based access control helpers.

Dependencies: golang.org/x/crypto, github.com/go-webauthn/webauthn, github.com/coreos/go-oidc/v3, golang.org/x/oauth2.

Note: HTTP handlers are app-specific and intentionally not included. Consumers should implement their own HTTP layer using the exported authentication primitives.

Install

go get github.com/cplieger/auth@latest

Usage

package main

import (
	"net/http"
	"time"

	"github.com/cplieger/auth"
)

func main() {
	// Hash a password (package-level with OWASP defaults)
	hash, _ := auth.HashPassword("my-secure-password")

	// Verify
	ok, _ := auth.VerifyPassword("my-secure-password", hash)
	_ = ok

	// Or use a configurable Hasher with custom params and optional pepper
	hasher, _ := auth.NewHasher(auth.Argon2Params{
		Memory: 65536, Iterations: 3, Parallelism: 2,
		SaltLength: 16, KeyLength: 32,
	}, auth.WithPepper([]byte("my-secret-pepper")))
	hash2, _ := hasher.Hash("my-secure-password")
	ok2, _ := hasher.Verify("my-secure-password", hash2)
	_, _ = hash2, ok2

	// Set up authenticator with your store implementation (functional options)
	authenticator := auth.NewAuthenticator(
		myStore, // implements auth.SessionStore
		auth.WithIdleTimeout(1*time.Hour),
		auth.WithAbsTimeout(24*time.Hour),
		auth.WithLoginPath("/login"),
		auth.WithCookie(auth.DefaultCookieConfig()),
	)

	// Use in HTTP handler
	http.HandleFunc("/api/protected", func(w http.ResponseWriter, r *http.Request) {
		user, _, ok := authenticator.RequireAuth(w, r)
		if !ok {
			return
		}
		_ = user
	})
}

Configuration

All configuration is via functional options and function parameters — no import-time side effects, no environment variable reads, no global state initialization.

  • WithLogger(l): optional *slog.Logger; if nil, uses slog.Default()
  • WithLoginPath(path): redirect path for unauthenticated browser requests (default: "/login")
  • WithCookie(cfg): configurable cookie name/prefix, Path, SameSite, Domain, Secure (see CookieConfig)
  • WithIdleTimeout(d): session idle timeout (default: 1h)
  • WithAbsTimeout(d): session absolute timeout (default: 24h)
  • WithBypass(fn): bypass function for development (synthetic admin user)
  • WithVerifiers(vs []CredentialVerifier): override the default verifier chain. When set, Authenticate iterates the supplied chain instead of the hardcoded default (SessionVerifier + APIKeyVerifier).
  • WithActivityThrottle(d time.Duration): when d>0, SessionVerifier maintains a per-hash last-write map and calls UpdateSessionActivity at most once per d per hash. d==0 (default) preserves the current write-on-every-request behavior.
  • NewHasher(params, ...HasherOption): configurable Argon2id parameters; use WithPepper([]byte) for HMAC peppering
  • GenerateAPIKey(prefix): pass your key prefix (e.g. "ak_")
  • ValidatePasswordContext(password, username, forbiddenWords): pass app-specific forbidden words
cfg := auth.CookieConfig{
    Name:     "my_session",       // base name (default: "auth_session")
    Prefix:   "__Host-",          // HTTPS prefix (default: "__Host-"; "" to disable)
    Path:     "/",                // cookie path (default: "/")
    Domain:   "",                 // cookie domain (default: unset)
    SameSite: http.SameSiteLaxMode, // (default: Lax)
    Secure:   nil,                // nil=auto (true when HTTPS), or explicit *bool
}
authenticator := auth.NewAuthenticator(myStore, auth.WithCookie(cfg))
CookiePosture

CookiePosture controls the cookie name prefix and Secure flag strategy:

Value Behavior
(default) Static __Host- prefix when Secure is true/auto-HTTPS
PosturePerRequest Selects cookie name and Secure flag at request time via isHTTPS(r). HTTPS requests get __Host-+base+Secure; plain HTTP gets the bare base name without the Secure flag. Respects TrustForwardedHeaders for X-Forwarded-Proto detection.

PosturePerRequest is useful for services that accept both HTTP and HTTPS traffic (e.g., behind a load balancer that terminates TLS for some paths but not others).

API

Password Hashing
  • HashPassword(password) (string, error) — Argon2id hash in PHC string format (OWASP defaults)
  • VerifyPassword(password, hash) (bool, error) — verify hash
  • NeedsRehash(encodedHash) bool — check if hash uses outdated parameters
  • DummyHash() string — pre-computed hash for constant-time login timing equalization
  • DefaultArgon2Params() Argon2Params — OWASP-recommended Argon2id parameters
  • NewHasher(params, ...HasherOption) (*Hasher, error) — configurable Argon2id hasher
  • WithPepper(pepper) HasherOption — enable HMAC peppering on a Hasher
  • Hasher.Hash(password) (string, error) — hash with custom params
  • Hasher.Verify(password, hash) (bool, error) — verify with custom params
  • Hasher.NeedsRehash(hash) bool — check against custom params
  • ValidatePasswordLength(password, passwordOnly) error — NIST length check (max 128)
  • ValidatePasswordContext(password, username, forbiddenWords) error — contextual check
  • CheckBreachedPassword(ctx, client, password) (bool, error) — HIBP k-anonymity
Sessions & Tokens
  • GenerateSessionToken() (plaintext, hash, error) — 256-bit session token
  • RotateSessionToken(oldPlaintext) (newPlaintext, newHash, oldHash, error) — rotation helper
  • ValidateSession(sess, idle, abs, now) error — session expiry check
  • SessionHash(token) string — SHA-256 hash of plaintext token
  • HexSHA256(s) string — hex-encoded SHA-256
  • CSRFToken(key, sessionHash) (string, error) — generate CSRF token bound to session
  • VerifyCSRFToken(key, sessionHash, token, maxAge) error — verify CSRF token
  • GenerateOpaqueToken() (plaintext, hash, error) — for password-reset/email-verification
  • VerifyOpaqueToken(plaintext, storedHash, expiresAt) error — verify opaque token
  • CookieConfig.CookieName(r) string — resolve cookie name for request
  • CookieConfig.SetCookie(w, r, token, maxAge) — set session cookie
  • CookieConfig.ReadCookie(r) string — read session token
  • CookieConfig.ClearCookie(w, r) — clear session cookie
  • SessionCookieName(r) / SetSessionCookie / ReadSessionCookie / ClearSessionCookie — default-config free functions
API Keys
  • GenerateAPIKey(keyPrefix) (plaintext, hash, displayPrefix, displaySuffix, error) — API key generation
  • VerifyAPIKey(ctx, store, key) (*Key, error) — API key verification (constant-time hash equality + expiry check)
  • APIKeyHash(key) string — SHA-256 hash of API key
WebAuthn
  • NewWebAuthn(rpID, rpDisplayName, rpOrigins) (*webauthn.WebAuthn, error) — WebAuthn setup
  • NewWebAuthnUser(user, creds) (*WebAuthnUser, error) — adapt User to webauthn.User interface
  • BeginRegistration / FinishRegistration / BeginLogin / FinishLogin — WebAuthn ceremonies
  • BeginConditionalLogin(wa) (*CredentialAssertion, *SessionData, error) — conditional mediation (autofill UI)
OIDC
  • NewOIDCProvider(ctx, cfg) (*OIDCProvider, error) — OIDC provider with PKCE
  • ValidateOIDCConfig(cfg) error — validate OIDC configuration
  • GenerateOIDCState() (string, error) — random state parameter
  • GeneratePKCE() (verifier, challenge, error) — PKCE S256
Auth Middleware
  • NewAuthenticator(store, ...Option) *Authenticator — create authenticator with functional options
  • NewSessionVerifier(store, ...Option) *SessionVerifier — session-cookie credential verifier
  • NewAPIKeyVerifier(store, ...Option) *APIKeyVerifier — API-key credential verifier
  • Authenticator.Authenticate(r) (*User, string, error) — resolve request to user
  • Authenticator.RequireAuth(w, r) (*User, string, bool) — auth guard
  • HasRole(user, role) bool — RBAC check
  • ValidateRedirectURI(uri) string — safe relative-path redirect validation
  • CanDisableAuthMethod(method, hasPassword, passkeyCount, oidcEnabled, oidcLinked) bool — check method removal safety
  • IsBrowserRequest(r) bool — detect browser vs API client
  • WithVerifiers(vs []CredentialVerifier) — override the default verifier chain
  • WithActivityThrottle(d time.Duration) — throttle UpdateSessionActivity writes (see Configuration)
  • CredentialVerifier — interface for pluggable credential verifiers
  • SessionStore / WebAuthnStore — interfaces for consumer to implement
  • store.Composite — composite interface (subpackage github.com/cplieger/auth/store)

Subpackages

auth/store

Composite interface store.Composite (renamed from store.AuthStore to disambiguate from auth.AuthStore in middleware.go). Consumers referencing store.AuthStore should update their alias target to store.Composite.

auth/ratelimit

Dual sliding-window per-IP + per-account authentication brute-force rate limiter (OWASP ASVS 2.2.1). Standard library only (context, sync, time).

rl := ratelimit.NewRateLimiter(ctx, ratelimit.DefaultConfig())
defer rl.Stop()
if allowed, retryAfter := rl.Allow(clientIP, username); !allowed {
    // reject; retry after retryAfter
}
// On successful login, clear the failure counters:
rl.Reset(clientIP, username)
auth/authtest

Exported in-memory SessionStore implementation for consumer tests.

store := authtest.NewMemStore()
store.AddUser(&auth.User{Username: "test", Role: auth.RoleUser, Enabled: true})

Unsupported Features (by design)

The following features are intentionally out of scope. Each has a documented rationale and, where applicable, a recommended alternative.

Feature Rationale
Full OIDC token-refresh orchestration Library handles authentication, not long-lived API access. Consumer uses oauth2.TokenSource.
Multi-provider OIDC registry Consumer instantiates multiple OIDCProvider instances.
WebAuthn MDS verification Enterprise feature with large surface. Consumer can call credential.Verify(mdsProvider) using stored RawAttestation.
OIDC back-channel logout Enterprise SSO feature beyond scope of auth-primitive library.
Hierarchical RBAC / permission sets Library provides HasRole for flat role check. Use casbin/ory-keto for complex RBAC.
Cookie encryption/signing Opaque-token architecture; cookie value is a random token, not sensitive data.
OIDC userinfo endpoint ID token claims sufficient for authentication. Consumer can call provider.UserInfo().
WebAuthn attestation conveyance Default none is correct for most RPs per FIDO Alliance guidance.
WebAuthn credential filtering (AAGUID) Enterprise policy. Consumer can use go-webauthn's filtering directly.
Passkey well-known endpoints Browser/credential-manager concern, not server-auth-library concern.
CSRF middleware (full HTTP layer) Library provides CSRFToken/VerifyCSRFToken primitives; full middleware is HTTP-framework-specific.

License

GPL-3.0 — see LICENSE.

Documentation

Overview

Package auth implements authentication primitives: password hashing (Argon2id in PHC string format), WebAuthn/FIDO2 passkey ceremonies, OIDC provider integration with PKCE, session management with idle/absolute timeouts, API key generation and verification, and role-based access control helpers.

Index

Examples

Constants

View Source
const (
	CookieNameSecure = "__Host-auth_session"
	CookieNameHTTP   = "auth_session"
)

Legacy constants for backward compatibility.

View Source
const (
	DefaultIdleTimeout = 1 * time.Hour
	DefaultAbsTimeout  = 24 * time.Hour
)

Default session timeout values matching the pre-refactor struct-field defaults.

View Source
const (
	HeaderXAPIKey    = "X-Api-Key" //nolint:gosec // G101 false positive: header name, not a credential
	QueryParamAPIKey = "api_key"
)

HTTP header and query parameter constants for API key authentication.

View Source
const CookieNoPrefix = "-"

CookieNoPrefix is kept for backward compatibility in tests.

View Source
const PasswordMaxLength = 128

PasswordMaxLength is the maximum password length to prevent DoS via extremely long inputs to Argon2id (OWASP recommendation).

View Source
const PasswordMinLengthMultiFactor = 8

PasswordMinLengthMultiFactor is the minimum password length when password login is not the sole sufficient factor.

View Source
const PasswordMinLengthSolo = 15

PasswordMinLengthSolo is the minimum password length when password login is enabled and thus a sole sufficient factor.

Variables

View Source
var (
	ErrSessionExpired  = errors.New("session expired")
	ErrSessionNotFound = errors.New("session not found")
)

Sentinel errors for session operations.

View Source
var (
	ErrTokenExpired = errors.New("auth: token expired")
	ErrTokenInvalid = errors.New("auth: token invalid")
)

Token errors.

View Source
var ErrInvalidAPIKey = errors.New("invalid API key")

ErrInvalidAPIKey is returned when an API key cannot be verified.

View Source
var ErrUnauthenticated = errors.New("unauthenticated")

ErrUnauthenticated is returned when no valid credential is found.

Functions

func APIKeyHash

func APIKeyHash(key string) string

APIKeyHash returns the hex-encoded SHA-256 hash of a key string.

func CSRFToken

func CSRFToken(key []byte, sessionHash string) (string, error)

CSRFToken generates a CSRF token bound to the given session hash using HMAC-SHA256 with a random 16-byte nonce per OWASP signed double-submit. Format: base64url(nonce[16] ∥ expiry[8] ∥ HMAC-SHA256(key, nonce ∥ sessionHash ∥ expiry))

func CanDisableAuthMethod

func CanDisableAuthMethod(method Method, hasPassword bool, passkeyCount int, oidcEnabled, oidcLinked bool) bool

CanDisableAuthMethod checks whether disabling the given auth method would leave the user with no viable authentication method.

func CheckBreachedPassword

func CheckBreachedPassword(ctx context.Context, client *http.Client, password string) (bool, error)

CheckBreachedPassword checks a password against the Have I Been Pwned Passwords API using k-anonymity. Returns true if the password has been found in a breach.

func ClearSessionCookie

func ClearSessionCookie(w http.ResponseWriter, r *http.Request)

ClearSessionCookie clears the session cookie using the default CookieConfig.

func DummyHash

func DummyHash() string

DummyHash returns a pre-computed Argon2id hash used by the login handler to equalize timing when the username doesn't exist (H2 mitigation). The hash is computed lazily on first call via sync.Once.

func GenerateAPIKey

func GenerateAPIKey(keyPrefix string) (plaintext, hash, displayPrefix, displaySuffix string, err error)

GenerateAPIKey generates a new API key with 256 bits of entropy. The keyPrefix is prepended to the random hex string (e.g. "ak_"). It returns the plaintext key, its SHA-256 hash, a display prefix (first 8 chars), and a display suffix (last 4 chars).

Example
package main

import (
	"fmt"

	"github.com/cplieger/auth"
)

func main() {
	plaintext, hash, prefix, suffix, err := auth.GenerateAPIKey("ak_")
	if err != nil {
		panic(err)
	}
	fmt.Println(plaintext[:3], len(hash) == 64, len(prefix) == 8, len(suffix) == 4)
}
Output:
ak_ true true true

func GenerateOpaqueToken

func GenerateOpaqueToken() (plaintext, hash string, err error)

GenerateOpaqueToken generates a cryptographically random opaque token suitable for password-reset or email-verification flows. Returns the plaintext token (to send to the user) and its SHA-256 hash (to store in the database).

func GenerateSessionToken

func GenerateSessionToken() (plaintext, hash string, err error)

GenerateSessionToken generates a cryptographically random session token (256 bits / 32 bytes). It returns the hex-encoded plaintext token and its SHA-256 hash (also hex-encoded).

Example
package main

import (
	"fmt"

	"github.com/cplieger/auth"
)

func main() {
	plaintext, hash, err := auth.GenerateSessionToken()
	if err != nil {
		panic(err)
	}
	fmt.Println(len(plaintext) == 64, len(hash) == 64, plaintext != hash)
}
Output:
true true true

func HasRole

func HasRole(user *User, role Role) bool

HasRole reports whether the user is authorized for the given role.

Example
package main

import (
	"fmt"

	"github.com/cplieger/auth"
)

func main() {
	admin := &auth.User{Role: auth.RoleAdmin}
	user := &auth.User{Role: auth.RoleUser}
	fmt.Println(auth.HasRole(admin, auth.RoleUser))
	fmt.Println(auth.HasRole(user, auth.RoleAdmin))
}
Output:
true
false

func HashPassword

func HashPassword(password string) (string, error)

HashPassword hashes a password using Argon2id with OWASP parameters. Returns the hash in PHC string format: $argon2id$v=19$m=19456,t=2,p=1$<base64-salt>$<base64-hash>

Example
package main

import (
	"fmt"

	"github.com/cplieger/auth"
)

func main() {
	hash, err := auth.HashPassword("my-secure-password")
	if err != nil {
		panic(err)
	}
	ok, err := auth.VerifyPassword("my-secure-password", hash)
	if err != nil {
		panic(err)
	}
	fmt.Println(ok)
}
Output:
true

func HexSHA256

func HexSHA256(s string) string

HexSHA256 returns the hex-encoded SHA-256 hash of s.

func IsBrowserRequest

func IsBrowserRequest(r *http.Request) bool

IsBrowserRequest returns true if the request appears to be from a browser (Accept header contains text/html and no X-API-Key header).

func NeedsRehash

func NeedsRehash(encodedHash string) bool

NeedsRehash reports whether the encoded hash was produced with parameters different from the current OWASP-recommended defaults. Returns true if the hash is invalid or uses outdated parameters.

func ReadSessionCookie

func ReadSessionCookie(r *http.Request) string

ReadSessionCookie reads the session token from the cookie using the default CookieConfig.

func RotateSessionToken

func RotateSessionToken(oldPlaintext string) (newPlaintext, newHash, oldHash string, err error)

RotateSessionToken generates a new session token, returning the new plaintext, new hash, and old hash. The caller is responsible for atomically replacing the session record in the store (delete old hash, insert new hash with same session data).

func SessionCookieName

func SessionCookieName(_ *http.Request) string

SessionCookieName returns the stable cookie name using the default CookieConfig.

func SessionHash

func SessionHash(token string) string

SessionHash returns the hex-encoded SHA-256 hash of a plaintext token.

func SetSessionCookie

func SetSessionCookie(w http.ResponseWriter, r *http.Request, token string, maxAge int)

SetSessionCookie sets the session cookie on the response using the default CookieConfig.

func ValidatePasswordContext

func ValidatePasswordContext(password, username string, forbiddenWords []string) error

ValidatePasswordContext rejects passwords that trivially embed the username or any of the provided forbidden words.

func ValidatePasswordLength

func ValidatePasswordLength(password string, passwordOnly bool) error

ValidatePasswordLength enforces minimum and maximum password length. Maximum length (128 chars) prevents DoS via Argon2id processing of extremely long inputs.

Example
package main

import (
	"fmt"

	"github.com/cplieger/auth"
)

func main() {
	err := auth.ValidatePasswordLength("short", true)
	fmt.Println(err != nil)
}
Output:
true

func ValidateRedirectURI

func ValidateRedirectURI(uri string) string

ValidateRedirectURI ensures the URI is a safe relative path.

Example
package main

import (
	"fmt"

	"github.com/cplieger/auth"
)

func main() {
	fmt.Println(auth.ValidateRedirectURI("/dashboard"))
	fmt.Println(auth.ValidateRedirectURI("https://evil.com"))
}
Output:
/dashboard
/

func ValidateSession

func ValidateSession(sess *Session, idleTimeout, absTimeout time.Duration, now time.Time) error

ValidateSession checks whether a session is still valid given the idle and absolute timeout durations.

func VerifyCSRFToken

func VerifyCSRFToken(key []byte, sessionHash, token string, maxAge time.Duration) error

VerifyCSRFToken verifies a CSRF token against the session hash and checks that it has not expired (maxAge duration from creation).

func VerifyOpaqueToken

func VerifyOpaqueToken(plaintext, storedHash string, expiresAt time.Time) error

VerifyOpaqueToken checks that the plaintext token matches the stored hash and that the token has not expired. Returns nil on success.

func VerifyPassword

func VerifyPassword(password, encodedHash string) (bool, error)

VerifyPassword verifies a password against an encoded Argon2id hash in PHC string format. Uses constant-time comparison.

Types

type APIKeyReader

type APIKeyReader interface {
	GetAPIKeyByHash(ctx context.Context, hash string) (*Key, error)
}

APIKeyReader validates API keys (looked up by hash).

type APIKeyVerifier

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

APIKeyVerifier authenticates requests via X-API-Key header or api_key query param. Create with NewAPIKeyVerifier.

func NewAPIKeyVerifier

func NewAPIKeyVerifier(store APIKeyVerifierStore, opts ...Option) *APIKeyVerifier

NewAPIKeyVerifier creates an APIKeyVerifier with the given store and options.

func (*APIKeyVerifier) Verify

func (v *APIKeyVerifier) Verify(ctx context.Context, r *http.Request) (*User, string, error)

Verify checks the API key header and query param, returns the user if valid.

type APIKeyVerifierStore

type APIKeyVerifierStore interface {
	APIKeyReader
	UserReader
}

APIKeyVerifierStore is the minimal interface for API key verification.

type Argon2Params

type Argon2Params struct {
	// Memory in KiB. Default: 19456 (19 MiB, OWASP recommendation #2).
	Memory uint32
	// Iterations (time cost). Default: 2.
	Iterations uint32
	// Parallelism (threads). Default: 1.
	Parallelism uint8
	// SaltLength in bytes. Default: 16.
	SaltLength uint32
	// KeyLength in bytes. Default: 32.
	KeyLength uint32
}

Argon2Params describes the Argon2id parameters for password hashing.

func DefaultArgon2Params

func DefaultArgon2Params() Argon2Params

DefaultArgon2Params returns the OWASP-recommended Argon2id parameters.

func (Argon2Params) Validate

func (p Argon2Params) Validate() error

Validate checks that the params are within safe bounds.

type AuthStore

AuthStore is the composed interface needed by Authenticator — session lookup, user lookup, and API key lookup.

type Authenticator

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

Authenticator resolves an HTTP request to an authenticated user. Create with NewAuthenticator.

func NewAuthenticator

func NewAuthenticator(store AuthStore, opts ...Option) *Authenticator

NewAuthenticator creates an Authenticator with the given store and options. The store must implement SessionReader, UserReader, and APIKeyReader. If no idle/absolute timeout is provided, defaults of 1h and 24h are applied.

func (*Authenticator) Authenticate

func (a *Authenticator) Authenticate(r *http.Request) (*User, string, error)

Authenticate checks session cookie first, then API key header, then API key query param. Returns the user and session hash, or ErrUnauthenticated.

func (*Authenticator) RequireAuth

func (a *Authenticator) RequireAuth(w http.ResponseWriter, r *http.Request) (*User, string, bool)

RequireAuth checks authentication and returns the user. If not authenticated, it writes the appropriate response (401 or redirect) and returns ok=false.

Example
package main

import (
	"fmt"
	"time"

	"github.com/cplieger/auth"
	"github.com/cplieger/auth/authtest"
)

func main() {
	store := authtest.NewMemStore()
	store.AddUser(&auth.User{
		Username: "alice",
		Role:     auth.RoleAdmin,
		Enabled:  true,
	})

	_ = auth.NewAuthenticator(
		store,
		auth.WithIdleTimeout(1*time.Hour),
		auth.WithAbsTimeout(24*time.Hour),
	)
	fmt.Println("authenticator configured")
}
Output:
authenticator configured

type CookieConfig

type CookieConfig struct {
	// Name is the base cookie name (without __Host- prefix).
	// Default: "auth_session".
	Name string

	// Path is the cookie Path attribute. Default: "/".
	Path string

	// Domain is the cookie Domain attribute. Default: "" (unset).
	// Note: __Host- prefix requires Domain to be unset.
	Domain string

	// SameSite is the cookie SameSite attribute. Default: http.SameSiteLaxMode.
	SameSite http.SameSite

	// Posture selects the deploy-time cookie security posture.
	// Default: PostureSecure.
	Posture CookiePosture

	// TrustForwardedHeaders enables honoring X-Forwarded-Proto to detect HTTPS.
	// MUST only be enabled when the app is behind a reverse proxy that always
	// sets/overwrites this header. When false (default), only r.TLS is used.
	TrustForwardedHeaders bool
}

CookieConfig holds configurable cookie attributes for session cookies. The posture is a deploy-time decision — ONE stable cookie name per deployment.

func DefaultCookieConfig

func DefaultCookieConfig() CookieConfig

DefaultCookieConfig returns a CookieConfig with secure defaults.

func (*CookieConfig) ClearCookie

func (c *CookieConfig) ClearCookie(w http.ResponseWriter, r *http.Request)

ClearCookie clears the session cookie.

func (*CookieConfig) CookieName

func (c *CookieConfig) CookieName(r *http.Request) string

CookieName returns the cookie name for this config. In PosturePerRequest mode the name is selected from the request scheme (__Host-<base> over HTTPS, bare <base> over plain HTTP); in all other postures the request is ignored and the stable EffectiveName() is returned.

func (*CookieConfig) EffectiveName

func (c *CookieConfig) EffectiveName() string

EffectiveName returns the ONE stable cookie name for this deployment. Determined entirely by Posture at config time — no per-request logic.

For PosturePerRequest the actual emitted/read name varies per request (see CookieName / SetCookie / ReadCookie); EffectiveName returns the secure __Host-<base> form as the canonical name for request-less callers and validation.

func (*CookieConfig) ReadCookie

func (c *CookieConfig) ReadCookie(r *http.Request) string

ReadCookie reads the session token from the cookie using the request-appropriate name (per-request in PosturePerRequest mode, otherwise the stable EffectiveName()).

func (*CookieConfig) SetCookie

func (c *CookieConfig) SetCookie(w http.ResponseWriter, r *http.Request, token string, maxAge int)

SetCookie sets the session cookie on the response using this config.

The Secure attribute follows the posture via isSecureCookieForRequest: it is set for every HTTPS posture and omitted only for plain-HTTP delivery, namely PostureInsecureLAN or PosturePerRequest over an HTTP request. Omitting Secure there is deliberate: a browser never sends a Secure cookie over plain HTTP, so forcing it would silently break sessions on HTTP-only LAN deployments. The default posture (PostureSecure) always sets Secure. Static analysis flags the conditional Secure (gosec G124, CodeQL go/cookie-secure-not-set); that is a documented false positive for the HTTP-LAN support, exercised by cookie_perrequest_test.go and redteam_test.go.

func (*CookieConfig) Validate

func (c *CookieConfig) Validate() error

Validate checks that the CookieConfig fields do not contain characters that would cause http.SetCookie to silently produce a malformed header.

type CookiePosture

type CookiePosture int

CookiePosture determines the cookie security posture at deploy time. This is a DEPLOY-TIME decision, NOT per-request.

const (
	// PostureSecure is the default: __Host- prefix + Secure + HttpOnly + SameSite=Lax.
	// Works on any HTTPS deployment including self-signed certificates.
	PostureSecure CookiePosture = iota

	// PostureInsecureLAN is for HTTP-only LAN/Docker deployments (ip:port, dockername:port).
	// Uses a non-prefixed name, no Secure flag. Explicitly opt-in only.
	PostureInsecureLAN

	// PostureForceSecure forces Secure flag even behind a TLS-terminating proxy
	// where r.TLS is nil. Requires TrustForwardedHeaders=true to detect HTTPS.
	PostureForceSecure

	// PosturePerRequest selects the cookie name and Secure flag per request,
	// driven by isHTTPS(r) (which honors TrustForwardedHeaders): __Host-<base>
	// with Secure over HTTPS, and the bare <base> without Secure over plain
	// HTTP. Intended for a single instance serving both HTTP-LAN (ip:port) and
	// HTTPS-proxied traffic. The base name stays configurable via Name.
	PosturePerRequest
)

type CredentialVerifier

type CredentialVerifier interface {
	Verify(ctx context.Context, r *http.Request) (*User, string, error)
}

CredentialVerifier resolves an HTTP request to an authenticated user using a specific credential type (session, API key, passkey). Implementations return (nil, "", nil) to indicate "not my credential type" and allow the next verifier in the chain to attempt authentication.

type Hasher

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

Hasher provides configurable Argon2id password hashing with optional pepper.

func NewHasher

func NewHasher(params Argon2Params, opts ...HasherOption) (*Hasher, error)

NewHasher creates a Hasher with the given params. Returns an error if params are invalid. Use WithPepper to enable HMAC peppering.

func (*Hasher) Hash

func (h *Hasher) Hash(password string) (string, error)

Hash hashes a password using Argon2id with the configured parameters. Returns the hash in PHC string format.

func (*Hasher) NeedsRehash

func (h *Hasher) NeedsRehash(encodedHash string) bool

NeedsRehash reports whether the hash uses different parameters than this Hasher.

func (*Hasher) Verify

func (h *Hasher) Verify(password, encodedHash string) (bool, error)

Verify verifies a password against an encoded Argon2id hash in PHC format.

type HasherOption

type HasherOption func(*hasherConfig)

HasherOption configures a Hasher.

func WithPepper

func WithPepper(pepper []byte) HasherOption

WithPepper sets the HMAC pepper applied to passwords before hashing. If not provided, no pepper is applied.

type Key

type Key struct {
	CreatedAt time.Time  `json:"created_at"`
	ExpiresAt *time.Time `json:"expires_at,omitempty"`
	KeyHash   string     `json:"-"`
	KeyPrefix string     `json:"key_prefix"`
	KeySuffix string     `json:"key_suffix"`
	Label     string     `json:"label"`
	ID        int64      `json:"id"`
	UserID    int64      `json:"user_id"`
}

Key represents a machine-to-machine API key for a user.

func VerifyAPIKey

func VerifyAPIKey(ctx context.Context, store APIKeyReader, key string) (*Key, error)

VerifyAPIKey hashes the provided key, looks it up in the store, and returns the matching APIKey record. Returns ErrInvalidAPIKey if the key is not found or has expired.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cplieger/auth"
	"github.com/cplieger/auth/authtest"
)

func main() {
	store := authtest.NewMemStore()
	store.AddUser(&auth.User{
		Username: "bot",
		Role:     auth.RoleUser,
		Enabled:  true,
	})

	plaintext, hash, prefix, suffix, _ := auth.GenerateAPIKey("ak_")
	store.AddAPIKey(&auth.Key{
		UserID:    1,
		KeyHash:   hash,
		KeyPrefix: prefix,
		KeySuffix: suffix,
		Label:     "ci",
	})

	key, err := auth.VerifyAPIKey(context.Background(), store, plaintext)
	fmt.Println(key != nil, err == nil)
}
Output:
true true

type Method

type Method string

Method is a typed identifier for the authentication mechanism used to establish a session.

const (
	MethodPassword Method = "password"
	MethodPasskey  Method = "passkey"
	MethodOIDC     Method = "oidc"
)

Auth method identifiers stored in sessions and used for method guards.

type OIDCConfig

type OIDCConfig struct {
	IssuerURL    string `json:"issuer_url" yaml:"issuer_url"`
	ClientID     string `json:"client_id" yaml:"client_id"`
	ClientSecret string `json:"-" yaml:"client_secret"`
	RedirectURI  string `json:"redirect_uri" yaml:"redirect_uri"`
	AutoRedirect bool   `json:"auto_redirect" yaml:"auto_redirect"`
}

OIDCConfig holds OIDC provider settings.

type Option

type Option func(*authConfig)

Option configures an Authenticator, SessionVerifier, or APIKeyVerifier.

func WithAbsTimeout

func WithAbsTimeout(d time.Duration) Option

WithAbsTimeout sets the session absolute timeout duration.

func WithActivityThrottle added in v1.1.0

func WithActivityThrottle(d time.Duration) Option

WithActivityThrottle sets the minimum interval between session-activity writes for a given session within a SessionVerifier.

The default (d == 0) preserves write-on-every-authenticated-request behavior. When d > 0, the verifier records a session-activity write at most once per d per session hash, coalescing the high-frequency writes that would otherwise hit the store on every request. The write remains best-effort (errors are logged, never fatal).

func WithBypass

func WithBypass(fn func() bool) Option

WithBypass sets a function that reports whether authentication is bypassed. When the function returns true, all requests are treated as authenticated with a synthetic admin user.

func WithCookie

func WithCookie(cfg CookieConfig) Option

WithCookie sets the cookie configuration for session cookies.

func WithIdleTimeout

func WithIdleTimeout(d time.Duration) Option

WithIdleTimeout sets the session idle timeout duration.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the logger for debug/warning output. If not provided, slog.Default() is used.

func WithLoginPath

func WithLoginPath(path string) Option

WithLoginPath sets the redirect target for unauthenticated browser requests. Defaults to "/login".

func WithVerifiers added in v1.1.0

func WithVerifiers(vs []CredentialVerifier) Option

WithVerifiers sets an explicit, ordered credential-verifier chain for an Authenticator, replacing the default session + API-key chain. When the provided slice is empty (or this option is not used), the default chain is used. This lets consumers inject custom verifiers (e.g. a TOTP or app-specific session verifier) without copying Authenticate/RequireAuth.

type PasskeyCredential

type PasskeyCredential struct {
	CreatedAt       time.Time `json:"created_at"`
	AttestationType string    `json:"-"`
	Transport       string    `json:"transport,omitempty"`
	Name            string    `json:"name"`
	CredentialID    []byte    `json:"-"`
	PublicKey       []byte    `json:"-"`
	AAGUID          []byte    `json:"-"`
	RawAttestation  []byte    `json:"-"`
	ID              int64     `json:"id"`
	UserID          int64     `json:"user_id"`
	SignCount       uint32    `json:"-"`
	BackupEligible  bool      `json:"backup_eligible"`
	BackupState     bool      `json:"-"`
	UserPresent     bool      `json:"-"`
	UserVerified    bool      `json:"-"`
	CloneWarning    bool      `json:"-"`
}

PasskeyCredential represents a WebAuthn/FIDO2 credential registered to a user.

type PasskeyFlags

type PasskeyFlags struct {
	UserPresent    bool
	UserVerified   bool
	BackupEligible bool
	BackupState    bool
	CloneWarning   bool
}

PasskeyFlags holds the boolean authenticator flags for a credential update.

type Role

type Role string

Role is a typed string identifying a user's authorization level.

const (
	RoleAdmin Role = "admin"
	RoleUser  Role = "user"
)

User role constants.

type Session

type Session struct {
	CreatedAt    time.Time  `json:"created_at"`
	LastActivity time.Time  `json:"last_activity"`
	OIDCExpiry   *time.Time `json:"oidc_expiry,omitempty"`
	TokenHash    string     `json:"-"`
	AuthMethod   Method     `json:"auth_method"`
	IPAddress    string     `json:"ip_address"`
	UserID       int64      `json:"user_id"`
}

Session represents a server-side authenticated session.

type SessionActivityUpdater

type SessionActivityUpdater interface {
	UpdateSessionActivity(ctx context.Context, tokenHash string, now time.Time) error
}

SessionActivityUpdater updates the last activity timestamp for a session.

type SessionReader

type SessionReader interface {
	GetSessionByHash(ctx context.Context, tokenHash string) (*Session, error)
}

SessionReader finds session data by token hash.

type SessionStore

type SessionStore interface {
	SessionReader
	SessionWriter
}

SessionStore composes read + write for middleware that needs both.

type SessionVerifier

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

SessionVerifier authenticates requests via session cookie. Create with NewSessionVerifier.

func NewSessionVerifier

func NewSessionVerifier(store SessionVerifierStore, opts ...Option) *SessionVerifier

NewSessionVerifier creates a SessionVerifier with the given session store and options. If no idle/absolute timeout is provided, defaults of 1h and 24h are applied.

func (*SessionVerifier) Verify

func (v *SessionVerifier) Verify(ctx context.Context, r *http.Request) (*User, string, error)

Verify checks the session cookie and returns the user if valid.

type SessionVerifierStore

type SessionVerifierStore interface {
	SessionReader
	SessionActivityUpdater
	UserReader
}

SessionVerifierStore is the minimal interface for session verification.

type SessionWriter

type SessionWriter interface {
	CreateSession(ctx context.Context, sess *Session) error
	UpdateSessionActivity(ctx context.Context, tokenHash string, now time.Time) error
	DeleteSession(ctx context.Context, tokenHash string) error
	DeleteUserSessions(ctx context.Context, userID int64, exceptHash string) error
}

SessionWriter persists and removes session data.

type User

type User struct {
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
	Username     string    `json:"username"`
	Email        string    `json:"email,omitempty"`
	DisplayName  string    `json:"display_name,omitempty"`
	PasswordHash string    `json:"-"`
	Role         Role      `json:"role"`
	OIDCSub      string    `json:"-"`
	OIDCIssuer   string    `json:"-"`
	ID           int64     `json:"id"`
	Enabled      bool      `json:"-"`
}

User represents an authenticated user account.

type UserReader

type UserReader interface {
	GetUserByID(ctx context.Context, id int64) (*User, error)
}

UserReader retrieves user records for authentication.

Directories

Path Synopsis
Package authtest provides an in-memory implementation of auth.AuthStore for use in consumer tests.
Package authtest provides an in-memory implementation of auth.AuthStore for use in consumer tests.
Package oidc wraps coreos/go-oidc to provide OIDC/OAuth2 authentication flows with PKCE.
Package oidc wraps coreos/go-oidc to provide OIDC/OAuth2 authentication flows with PKCE.
Package ratelimit implements a dual sliding-window rate limiter for authentication attempts (per-IP and per-account).
Package ratelimit implements a dual sliding-window rate limiter for authentication attempts (per-IP and per-account).
Package store defines the composite store interface used by the authentication subsystem and implemented by persistence layers.
Package store defines the composite store interface used by the authentication subsystem and implemented by persistence layers.
Package webauthn wraps go-webauthn/webauthn to provide WebAuthn/FIDO2 passkey ceremony helpers.
Package webauthn wraps go-webauthn/webauthn to provide WebAuthn/FIDO2 passkey ceremony helpers.

Jump to

Keyboard shortcuts

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