Documentation
¶
Overview ¶
Package auth provides authentication infrastructure for Credo applications.
Instead of a built-in User field on Context, Credo exposes the authenticated principal through generic *credo.Context methods, and this package provides the Authenticator[T] strategy interface and a middleware factory that populates it. See ADR-012 for the design rationale.
User Accessors ¶
The authenticated user is attached and read through Context methods, with compile-time type safety:
// In middleware (auth.Middleware calls SetUser for you)
ctx.SetUser(myUser)
// In a Credo handler
user, err := ctx.RequireUser[*MyUser]()
if err != nil { ... }
Authenticator Interface ¶
Implement Authenticator[T] for your auth strategy (JWT, session, API key):
type MyAuth struct { ... }
func (a *MyAuth) Authenticate(r *http.Request) (*User, error) { ... }
Middleware Factory ¶
Create middleware from any Authenticator:
app.GlobalMiddleware(auth.Middleware[*User](myAuth, nil))
For custom auth failure handling, provide an ErrorFunc:
auth.Middleware[*User](myAuth, func(err error, ctx *credo.Context) error {
return ctx.Response().JSON(401, map[string]string{"error": err.Error()})
})
Built-in Authenticators ¶
This package includes reusable Authenticator implementations:
- NewJWTAuthenticator[T]
- NewAPIKeyAuthenticator[T]
- NewBasicAuthenticator[T]
JWT: Simple and Advanced Tiers ¶
JWTConfig is two-tiered. The simple tier is Credo-typed: token extraction, signing keys, registered-claim validation (Issuer, Audience, Leeway, RequireExpiry), and ParseClaims, which receives a JWTClaims view with typed accessors (ExpiresAt as time.Time, GetString for custom claims).
Advanced golang-jwt Integration ¶
The JWTConfig.Advanced field exposes the underlying golang-jwt/jwt/v5 primitives — KeyFunc (e.g. JWKS lookup), NewClaims (typed claims structs), ParseToken (raw *jwt.Token access), ParserOptions — for setups the simple tier cannot express. Configuration placed there is coupled to the golang-jwt API by design; see JWTAdvanced.
Index ¶
- Variables
- func BasicChallenge(realm string) string
- func Middleware[T any](a Authenticator[T], onError ErrorFunc) credo.Middleware
- func SecureCompare(x, y string) bool
- type APIKeyAuthenticator
- type APIKeyConfig
- type APIKeyValidator
- type Authenticator
- type BasicAuthenticator
- type BasicConfig
- type BasicValidator
- type ErrorFunc
- type JWTAdvanced
- type JWTAuthenticator
- type JWTClaims
- func (c JWTClaims) Audience() []string
- func (c JWTClaims) ExpiresAt() time.Time
- func (c JWTClaims) Get(name string) any
- func (c JWTClaims) GetString(name string) string
- func (c JWTClaims) IssuedAt() time.Time
- func (c JWTClaims) Issuer() string
- func (c JWTClaims) NotBefore() time.Time
- func (c JWTClaims) Subject() string
- type JWTConfig
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAPIKeyMissing is returned when no API key can be extracted. ErrAPIKeyMissing = errors.New("auth: api key is missing") // ErrAPIKeyInvalid is returned when an API key is rejected. ErrAPIKeyInvalid = errors.New("auth: api key is invalid") )
var ( // ErrBasicCredentialsMissing is returned when Basic credentials are absent. ErrBasicCredentialsMissing = errors.New("auth: basic credentials are missing") // ErrBasicCredentialsInvalid is returned when Basic credentials are rejected. ErrBasicCredentialsInvalid = errors.New("auth: basic credentials are invalid") )
var ( // ErrJWTMissing is returned when no JWT token can be extracted. ErrJWTMissing = errors.New("auth: jwt token is missing") // ErrJWTInvalid is returned when a JWT token fails validation. ErrJWTInvalid = errors.New("auth: jwt token is invalid") )
var ( // ErrAuthenticatorRequired is returned when auth middleware is built // without an authenticator. ErrAuthenticatorRequired = errors.New("auth: authenticator is required") )
Functions ¶
func BasicChallenge ¶
BasicChallenge returns a valid WWW-Authenticate header value for Basic auth.
func Middleware ¶
func Middleware[T any](a Authenticator[T], onError ErrorFunc) credo.Middleware
Middleware creates an credo.Middleware that authenticates requests using the given Authenticator. If authentication succeeds, the user is stored on the request via ctx.SetUser and is then accessible to downstream handlers via ctx.GetUser[T]().
When authentication fails and onError is nil (or returns nil), the middleware returns credo.ErrUnauthorized with the authenticator's error as Internal. Provide an ErrorFunc to customize the failure response.
func SecureCompare ¶
SecureCompare reports whether x and y are equal, in time independent of their contents. Use it inside BasicValidator and APIKeyValidator implementations when comparing a received credential against a known secret — a plain == comparison returns early on the first differing byte, leaking how much of the secret matched through response timing.
Both inputs are hashed before comparison, so the timing reveals neither the contents nor the lengths of the inputs.
SecureCompare is for comparing against plaintext secrets (API keys, tokens). Stored passwords should instead be verified with a dedicated password hash such as bcrypt or argon2id, whose comparison functions are already timing-safe.
Types ¶
type APIKeyAuthenticator ¶
type APIKeyAuthenticator[T any] struct { // contains filtered or unexported fields }
APIKeyAuthenticator validates API keys extracted from HTTP requests.
func NewAPIKeyAuthenticator ¶
func NewAPIKeyAuthenticator[T any](cfg APIKeyConfig[T]) (*APIKeyAuthenticator[T], error)
NewAPIKeyAuthenticator creates a new API key Authenticator.
func (*APIKeyAuthenticator[T]) Authenticate ¶
func (a *APIKeyAuthenticator[T]) Authenticate(r *http.Request) (T, error)
Authenticate validates request API key credentials.
type APIKeyConfig ¶
type APIKeyConfig[T any] struct { // Header is the header name used for key extraction. // Default: "X-API-Key" when Header and Query are both empty. Header string // Prefix trims an optional scheme prefix from Header value. // Example: "ApiKey" for "Authorization: ApiKey <key>". Prefix string // Query is an optional query parameter fallback. Query string // Validator checks whether the extracted key is valid. Validator APIKeyValidator[T] }
APIKeyConfig defines configuration for APIKeyAuthenticator.
type APIKeyValidator ¶
APIKeyValidator validates an API key and returns an authenticated user. ok=false means credentials were recognized but rejected.
When comparing the key against a known secret, use SecureCompare instead of == to avoid leaking timing information.
type Authenticator ¶
Authenticator validates a request and returns the authenticated user. T is the application's user type (e.g., *MyUser, Claims, etc.).
type BasicAuthenticator ¶
type BasicAuthenticator[T any] struct { // contains filtered or unexported fields }
BasicAuthenticator validates HTTP Basic credentials.
func NewBasicAuthenticator ¶
func NewBasicAuthenticator[T any](cfg BasicConfig[T]) (*BasicAuthenticator[T], error)
NewBasicAuthenticator creates a new Basic Authenticator.
func (*BasicAuthenticator[T]) Authenticate ¶
func (a *BasicAuthenticator[T]) Authenticate(r *http.Request) (T, error)
Authenticate validates request Basic credentials.
func (*BasicAuthenticator[T]) Realm ¶
func (a *BasicAuthenticator[T]) Realm() string
Realm returns the configured Basic auth realm.
type BasicConfig ¶
type BasicConfig[T any] struct { // Validator checks whether username/password are valid. Validator BasicValidator[T] // Realm is used by BasicErrorHandler for WWW-Authenticate. // Default: "Restricted". Realm string }
BasicConfig defines configuration for BasicAuthenticator.
type BasicValidator ¶
type BasicValidator[T any] func(username, password string, r *http.Request) (user T, ok bool, err error)
BasicValidator validates username/password credentials and returns user data. ok=false means credentials were parsed but rejected.
When comparing the password against a known plaintext secret, use SecureCompare instead of == to avoid leaking timing information; stored passwords should be verified with a dedicated password hash (bcrypt, argon2id) instead.
type ErrorFunc ¶
ErrorFunc is called when authentication fails. It receives the error from the Authenticator and should return an appropriate HTTP error (or nil to use the default 401 response).
func BasicErrorHandler ¶
BasicErrorHandler returns an ErrorFunc that adds a Basic challenge header before returning 401 Unauthorized.
type JWTAdvanced ¶
type JWTAdvanced[T any] struct { // KeyFunc overrides SigningKey/SigningKeys selection entirely // (e.g. JWKS lookup). KeyFunc jwt.Keyfunc // NewClaims creates the claims value tokens are parsed into. // Default: a JSON object map (jwt.MapClaims). NewClaims func() jwt.Claims // ParseToken converts a validated *jwt.Token into the user value T, // with access to the raw token (header, signature, typed claims). // Configuring both ParseToken and JWTConfig.ParseClaims is a // constructor error. ParseToken func(token *jwt.Token) (T, error) // ParserOptions are appended after the options the framework derives // from the simple tier, so on conflict the last (advanced) option // wins. ParserOptions []jwt.ParserOption }
JWTAdvanced is the escape hatch tier of JWTConfig: direct golang-jwt integration for setups the Credo-typed fields cannot express. Fields here trade framework guarantees for full library access — for example, ParserOptions can override the option the framework derives from SigningMethod, Issuer, Leeway, or RequireExpiry.
type JWTAuthenticator ¶
type JWTAuthenticator[T any] struct { // contains filtered or unexported fields }
JWTAuthenticator validates JWT credentials from an HTTP request.
func NewJWTAuthenticator ¶
func NewJWTAuthenticator[T any](cfg JWTConfig[T]) (*JWTAuthenticator[T], error)
NewJWTAuthenticator creates a new JWT Authenticator.
func (*JWTAuthenticator[T]) Authenticate ¶
func (a *JWTAuthenticator[T]) Authenticate(r *http.Request) (T, error)
Authenticate validates a JWT token and returns the authenticated user value.
type JWTClaims ¶
type JWTClaims struct {
// contains filtered or unexported fields
}
JWTClaims is a read-only view over a validated token's claims, passed to JWTConfig.ParseClaims. Registered claims are exposed with proper Go types — timestamps as time.Time rather than the raw float64 that JSON decoding produces — and read as zero values when absent or malformed (signature and temporal validity are already enforced before ParseClaims runs).
func (JWTClaims) ExpiresAt ¶
ExpiresAt returns the exp claim as a time.Time, or the zero time when absent.
func (JWTClaims) Get ¶
Get returns the named custom claim when the default claims representation (a JSON object map) is in use, or nil when the claim is absent or a custom JWTAdvanced.NewClaims type is configured. Typed custom claims structs should be read via JWTAdvanced.ParseToken instead.
func (JWTClaims) GetString ¶
GetString returns the named custom claim as a string, or "" when the claim is absent or not a string. See JWTClaims.Get for the lookup rules.
func (JWTClaims) IssuedAt ¶
IssuedAt returns the iat claim as a time.Time, or the zero time when absent.
type JWTConfig ¶
type JWTConfig[T any] struct { // Header is the header name used to read the token. // Default: "Authorization". Header string // Prefix is the token scheme prefix in Header. // Default: "Bearer". Prefix string // Query is an optional query parameter name for token fallback. // Example: "token". Query string // Cookie is an optional cookie name for token fallback. // Example: "jwt". Cookie string // SigningMethod is the expected token alg (e.g. HS256, RS256). // Default: HS256. SigningMethod string // SigningKey is the default key used to validate signatures. // It is also used as a fallback when SigningKeys is configured but // an incoming token does not include a kid header. SigningKey any // SigningKeys is an optional key set selected by token header kid. // If token kid is present, it must exist in this map. SigningKeys map[string]any // Issuer, when set, requires the token's iss claim to match exactly. Issuer string // Audience, when set, requires the token's aud claim to contain AT // LEAST ONE of the listed values. This any-of rule is the standard // RFC 7519 validator practice: a token may be addressed to several // audiences, and the recipient accepts it if it identifies itself // with any of them. Audience []string // Leeway widens time-based claim validation (exp, nbf, iat) by the // given duration in both directions, absorbing clock skew between // the token issuer and this server. Leeway time.Duration // RequireExpiry rejects tokens that carry no exp claim. By default // (matching golang-jwt v5), exp is validated only when present — // a token without exp never expires. Set this when tokens are // expected to be short-lived. RequireExpiry bool // ParseClaims converts the validated token's claims into the user // value T. This is the simple-tier counterpart of // [JWTAdvanced.ParseToken]; configuring both is a constructor error. // When neither is set, the raw claims value is type-asserted to T. ParseClaims func(claims JWTClaims) (T, error) // Advanced exposes golang-jwt primitives directly. Most applications // never need it; see [JWTAdvanced]. Advanced JWTAdvanced[T] }
JWTConfig defines configuration for JWTAuthenticator.
The config has two tiers. The simple tier covers common setups with Credo-typed fields only: token extraction, key material, registered-claim validation (Issuer, Audience, Leeway, RequireExpiry), and user extraction via JWTConfig.ParseClaims. The JWTConfig.Advanced tier exposes the underlying golang-jwt primitives for setups the simple tier cannot express (custom claims structs, JWKS key resolution, extra parser options).