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 ¶
- Constants
- Variables
- func APIKeyHash(key string) string
- func CSRFToken(key []byte, sessionHash string) (string, error)
- func CanDisableAuthMethod(method Method, hasPassword bool, passkeyCount int, ...) bool
- func CheckBreachedPassword(ctx context.Context, client *http.Client, password string) (bool, error)
- func ClearSessionCookie(w http.ResponseWriter, r *http.Request)
- func DummyHash() string
- func GenerateAPIKey(keyPrefix string) (plaintext, hash, displayPrefix, displaySuffix string, err error)
- func GenerateOpaqueToken() (plaintext, hash string, err error)
- func GenerateSessionToken() (plaintext, hash string, err error)
- func HasRole(user *User, role Role) bool
- func HashPassword(password string) (string, error)
- func HexSHA256(s string) string
- func IsBrowserRequest(r *http.Request) bool
- func NeedsRehash(encodedHash string) bool
- func ReadSessionCookie(r *http.Request) string
- func RotateSessionToken(oldPlaintext string) (newPlaintext, newHash, oldHash string, err error)
- func SessionCookieName(_ *http.Request) string
- func SessionHash(token string) string
- func SetSessionCookie(w http.ResponseWriter, r *http.Request, token string, maxAge int)
- func ValidatePasswordContext(password, username string, forbiddenWords []string) error
- func ValidatePasswordLength(password string, passwordOnly bool) error
- func ValidateRedirectURI(uri string) string
- func ValidateSession(sess *Session, idleTimeout, absTimeout time.Duration, now time.Time) error
- func VerifyCSRFToken(key []byte, sessionHash, token string, maxAge time.Duration) error
- func VerifyOpaqueToken(plaintext, storedHash string, expiresAt time.Time) error
- func VerifyPassword(password, encodedHash string) (bool, error)
- type APIKeyReader
- type APIKeyVerifier
- type APIKeyVerifierStore
- type Argon2Params
- type AuthStore
- type Authenticator
- type CookieConfig
- func (c *CookieConfig) ClearCookie(w http.ResponseWriter, r *http.Request)
- func (c *CookieConfig) CookieName(r *http.Request) string
- func (c *CookieConfig) EffectiveName() string
- func (c *CookieConfig) ReadCookie(r *http.Request) string
- func (c *CookieConfig) SetCookie(w http.ResponseWriter, r *http.Request, token string, maxAge int)
- func (c *CookieConfig) Validate() error
- type CookiePosture
- type CredentialVerifier
- type Hasher
- type HasherOption
- type Key
- type Method
- type OIDCConfig
- type Option
- func WithAbsTimeout(d time.Duration) Option
- func WithActivityThrottle(d time.Duration) Option
- func WithBypass(fn func() bool) Option
- func WithCookie(cfg CookieConfig) Option
- func WithIdleTimeout(d time.Duration) Option
- func WithLogger(l *slog.Logger) Option
- func WithLoginPath(path string) Option
- func WithTimeoutSource(fn func() (idle, abs time.Duration)) Option
- func WithUnauthorizedResponse(fn func(w http.ResponseWriter, r *http.Request)) Option
- func WithVerifiers(vs []CredentialVerifier) Option
- type PasskeyCredential
- type PasskeyFlags
- type Role
- type Session
- type SessionActivityUpdater
- type SessionReader
- type SessionStore
- type SessionVerifier
- type SessionVerifierStore
- type SessionWriter
- type User
- type UserReader
Examples ¶
Constants ¶
const ( CookieNameSecure = "__Host-auth_session" CookieNameHTTP = "auth_session" )
Legacy constants for backward compatibility.
const ( DefaultIdleTimeout = 1 * time.Hour DefaultAbsTimeout = 24 * time.Hour )
Default session timeout values matching the pre-refactor struct-field defaults.
const HeaderXAPIKey = "X-Api-Key" //nolint:gosec // G101 false positive: header name, not a credential
HeaderXAPIKey is the HTTP header carrying the API key. Keys are accepted only via this header, never a URL query parameter; a query-string key leaks into access logs, browser history, and the Referer header (CWE-598).
const PasswordMaxLength = 128
PasswordMaxLength is the maximum password length to prevent DoS via extremely long inputs to Argon2id (OWASP recommendation).
const PasswordMinLengthMultiFactor = 8
PasswordMinLengthMultiFactor is the minimum password length when password login is not the sole sufficient factor.
const PasswordMinLengthSolo = 15
PasswordMinLengthSolo is the minimum password length when password login is enabled and thus a sole sufficient factor.
Variables ¶
var ( ErrSessionExpired = errors.New("session expired") ErrSessionNotFound = errors.New("session not found") )
Sentinel errors for session operations.
var ( ErrTokenExpired = errors.New("auth: token expired") ErrTokenInvalid = errors.New("auth: token invalid") )
Token errors.
var ErrInvalidAPIKey = errors.New("invalid API key")
ErrInvalidAPIKey is returned when an API key cannot be verified.
var ErrUnauthenticated = errors.New("unauthenticated")
ErrUnauthenticated is returned when no valid credential is found.
Functions ¶
func APIKeyHash ¶
APIKeyHash returns the hex-encoded SHA-256 hash of a key string.
func CSRFToken ¶
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] ∥ issuedAt[8] ∥ HMAC-SHA256(key, nonce ∥ sessionHash ∥ issuedAt))
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 ¶
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.
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/v2"
)
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 ¶
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 ¶
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/v2"
)
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 ¶
HasRole reports whether the user is authorized for the given role.
Example ¶
package main
import (
"fmt"
"github.com/cplieger/auth/v2"
)
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 ¶
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/v2"
)
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 IsBrowserRequest ¶
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 ¶
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 ¶
ReadSessionCookie reads the session token from the cookie using the default CookieConfig.
func RotateSessionToken ¶
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 ¶
SessionCookieName returns the stable cookie name using the default CookieConfig.
func SessionHash ¶
SessionHash returns the hex-encoded SHA-256 hash of a plaintext token.
func SetSessionCookie ¶
SetSessionCookie sets the session cookie on the response using the default CookieConfig.
func ValidatePasswordContext ¶
ValidatePasswordContext rejects passwords that trivially embed the username or any of the provided forbidden words.
func ValidatePasswordLength ¶
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/v2"
)
func main() {
err := auth.ValidatePasswordLength("short", true)
fmt.Println(err != nil)
}
Output: true
func ValidateRedirectURI ¶
ValidateRedirectURI ensures the URI is a safe relative path.
Example ¶
package main
import (
"fmt"
"github.com/cplieger/auth/v2"
)
func main() {
fmt.Println(auth.ValidateRedirectURI("/dashboard"))
fmt.Println(auth.ValidateRedirectURI("https://evil.com"))
}
Output: /dashboard /
func ValidateSession ¶
ValidateSession checks whether a session is still valid given the idle and absolute timeout durations.
func VerifyCSRFToken ¶
VerifyCSRFToken verifies a CSRF token against the session hash and checks that it has not expired (maxAge duration from creation).
func VerifyOpaqueToken ¶
VerifyOpaqueToken checks that the plaintext token matches the stored hash and that the token has not expired. Returns nil on success.
func VerifyPassword ¶
VerifyPassword verifies a password against an encoded Argon2id hash in PHC string format. Uses constant-time comparison.
Types ¶
type APIKeyReader ¶
APIKeyReader validates API keys (looked up by hash).
type APIKeyVerifier ¶
type APIKeyVerifier struct {
// contains filtered or unexported fields
}
APIKeyVerifier authenticates requests via the X-API-Key header. Create with NewAPIKeyVerifier.
func NewAPIKeyVerifier ¶
func NewAPIKeyVerifier(store APIKeyVerifierStore, opts ...Option) *APIKeyVerifier
NewAPIKeyVerifier creates an APIKeyVerifier with the given store and options. Of the shared Option set it consults only WithLogger; every other option (cookie, timeouts, throttle, bypass, hooks) configures session or authenticator behavior this verifier does not have and is silently ignored.
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 ¶
type AuthStore interface {
SessionReader
SessionActivityUpdater
UserReader
APIKeyReader
}
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, error)
NewAuthenticator creates an Authenticator with the given store and options. The store must implement SessionReader, UserReader, and APIKeyReader. It returns an error when the assembled configuration is unusable (see CookieConfig.Validate and WithActivityThrottle). If no idle/absolute timeout is provided, defaults of 1h and 24h are applied.
func (*Authenticator) Authenticate ¶
Authenticate resolves the request to a user by running the credential verifier chain in order. The default chain checks the session cookie first, then the API key (accepted only via the X-Api-Key header, never a URL query parameter -- CWE-598). It returns the user and session hash, or ErrUnauthenticated when no verifier authenticates the request.
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 and returns ok=false. The default response is a 302 redirect to the login path for browser requests and a 401 JSON envelope otherwise; a hook installed via WithUnauthorizedResponse replaces both branches.
Example ¶
package main
import (
"fmt"
"time"
"github.com/cplieger/auth/v2"
"github.com/cplieger/auth/v2/authtest"
)
func main() {
store := authtest.NewMemStore()
store.AddUser(&auth.User{
Username: "alice",
Role: auth.RoleAdmin,
Enabled: true,
})
authn, err := auth.NewAuthenticator(
store,
auth.WithIdleTimeout(1*time.Hour),
auth.WithAbsTimeout(24*time.Hour),
)
if err != nil {
panic(err)
}
_ = authn
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. Under every posture except PosturePerRequest the deployment has ONE stable cookie name; PosturePerRequest alternates between the __Host--prefixed and bare forms of the configured base name per request scheme.
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 reports whether the configuration will produce a usable session cookie. It returns an error when:
- Name, Domain, or Path contains a control character, or Name contains a character invalid in a cookie name -- cases that would make http.SetCookie emit a malformed Set-Cookie header; or
- the posture emits a __Host--prefixed name (every posture except PostureInsecureLAN) while Domain is set or Path is not "/". Browsers silently reject a __Host- cookie that carries a Domain or a non-root Path, breaking every session with no server-side error.
Validate is the single authority for cookie-config validity: the constructors (NewAuthenticator, NewSessionVerifier) call it so an unusable configuration fails fast at construction, and consumers assembling a CookieConfig by hand may call it directly.
type CookiePosture ¶
type CookiePosture int
CookiePosture determines the cookie security posture. Every posture except PosturePerRequest is a deploy-time decision with one stable cookie name; PosturePerRequest selects the name and Secure flag per request from the request scheme.
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 )
func (CookiePosture) String ¶
func (p CookiePosture) String() string
String returns the posture's name so structured-log attributes render "PostureSecure" rather than the bare iota integer.
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.
Chain semantics (see Authenticator.Authenticate): the chain stops at the first verifier that returns a non-nil *User (granted) OR a non-nil error. A non-nil error ABORTS the whole chain - verifiers ordered after it never run - and is surfaced to the caller as a failed authentication. A verifier that wants the chain to continue to the next credential type MUST return (nil, "", nil), never an error, for a credential it cannot positively authenticate. The built-in verifiers differ deliberately: SessionVerifier treats every session problem as (nil, "", nil) and falls through, whereas APIKeyVerifier returns ErrUnauthenticated for a present-but-invalid key. In the default NewAuthenticator chain APIKeyVerifier runs last, so this is unobservable; but a custom WithVerifiers chain that places an error-returning verifier before others will reject a request (HTTP 401) whose earlier-type credential is present-but-invalid even when a later verifier holds a valid credential. This fails closed (denial, never a bypass); to avoid masking a later valid credential, order error-returning verifiers last or have them return (nil, "", nil) on "not my credential".
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 ¶
Hash hashes a password using Argon2id with the configured parameters. Returns the hash in PHC string format.
func (*Hasher) NeedsRehash ¶
NeedsRehash reports whether the hash uses different parameters than this Hasher.
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 ¶
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/v2"
"github.com/cplieger/auth/v2/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.
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 ¶
WithAbsTimeout sets the session absolute timeout duration.
func WithActivityThrottle ¶
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).
d must be less than the configured idle timeout (ideally much less): because the persisted LastActivity is only refreshed once per window, it lags real activity by up to d, so a throttle >= the idle timeout can cause ValidateSession to expire sessions that are actively in use. The constructors reject such a configuration with an error.
func WithBypass ¶
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 ¶
WithIdleTimeout sets the session idle timeout duration.
func WithLogger ¶
WithLogger sets the logger for debug/warning output. If not provided, slog.Default() is used.
func WithLoginPath ¶
WithLoginPath sets the redirect target for unauthenticated browser requests. Defaults to "/login".
func WithTimeoutSource ¶ added in v2.1.0
WithTimeoutSource sets a callback that resolves the session idle and absolute timeouts per verification, instead of the static values configured via WithIdleTimeout/WithAbsTimeout. It exists for consumers whose timeouts are hot-reloadable configuration: the default SessionVerifier consults the source on every Verify, so a changed value takes effect without reconstructing the verifier.
Validation is per-resolution (a construction-time check cannot bind dynamic values): a non-positive idle or absolute value returned by the source falls back to the corresponding static configured value, and the activity throttle (see WithActivityThrottle) is clamped to at most half the resolved idle timeout, so a source that shrinks the idle timeout below the configured throttle can never make the persisted LastActivity stale enough to expire a session that is actively in use.
func WithUnauthorizedResponse ¶ added in v2.1.0
func WithUnauthorizedResponse(fn func(w http.ResponseWriter, r *http.Request)) Option
WithUnauthorizedResponse sets the response written by Authenticator.RequireAuth when a request is not authenticated, replacing the default behavior (a 302 redirect to the login path for browser requests, a 401 JSON envelope otherwise). The hook owns the entire unauthorized response: it is called for browser and API requests alike, so a consumer that wants to keep the browser redirect applies IsBrowserRequest itself. A nil hook (the default) preserves the built-in behavior byte for byte.
func WithVerifiers ¶
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; a consumer that also needs its own unauthorized response pairs it with WithUnauthorizedResponse to keep 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 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, error)
NewSessionVerifier creates a SessionVerifier with the given session store and options. It returns an error when the assembled configuration is unusable: for example a __Host- cookie posture combined with a Domain or a non-root Path (which browsers reject), or an activity throttle that is not less than the idle timeout. See CookieConfig.Validate. If no idle/absolute timeout is provided, defaults of 1h and 24h are applied.
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.
Source Files
¶
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. |
|
internal
|
|
|
capture
Package capture is an auth-internal test helper: a slog.Handler that records log records so tests can assert on auth's logging decisions, plus a helper to install it as the default logger.
|
Package capture is an auth-internal test helper: a slog.Handler that records log records so tests can assert on auth's logging decisions, plus a helper to install it as the default logger. |
|
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 persistence contract for the authentication subsystem.
|
Package store defines the composite persistence contract for the authentication subsystem. |
|
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. |