ax(mining): replace prose comments in auth.go with usage examples
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

All comments on AuthConfig, DefaultAuthConfig, AuthConfigFromEnv,
DigestAuth, NewDigestAuth, Stop, Middleware, and all private methods
were restating the type signature in prose. Replaced with concrete
call-site examples per AX Principle 2 (comments as usage examples).

Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
Claude 2026-04-02 09:29:03 +01:00
parent f18991470d
commit cc8eeab231
No known key found for this signature in database
GPG key ID: AF404715446AEB41

View file

@ -16,22 +16,16 @@ import (
"github.com/gin-gonic/gin"
)
// AuthConfig holds authentication configuration
// AuthConfig{Enabled: true, Username: "admin", Password: "secret", Realm: "Mining API"}
type AuthConfig struct {
// Enabled determines if authentication is required
Enabled bool
// Username for basic/digest auth
Username string
// Password for basic/digest auth
Password string
// Realm for digest auth
Realm string
// NonceExpiry is how long a nonce is valid
Enabled bool
Username string
Password string
Realm string
NonceExpiry time.Duration
}
// DefaultAuthConfig returns the default auth configuration.
// Auth is disabled by default for local development.
// cfg := DefaultAuthConfig() // Enabled: false, Realm: "Mining API", NonceExpiry: 5m
func DefaultAuthConfig() AuthConfig {
return AuthConfig{
Enabled: false,
@ -42,8 +36,7 @@ func DefaultAuthConfig() AuthConfig {
}
}
// AuthConfigFromEnv creates auth config from environment variables.
// Set MINING_API_AUTH=true to enable, MINING_API_USER and MINING_API_PASS for credentials.
// cfg := AuthConfigFromEnv() // reads MINING_API_AUTH, MINING_API_USER, MINING_API_PASS, MINING_API_REALM
func AuthConfigFromEnv() AuthConfig {
config := DefaultAuthConfig()
@ -67,7 +60,7 @@ func AuthConfigFromEnv() AuthConfig {
return config
}
// DigestAuth implements HTTP Digest Authentication middleware
// da := NewDigestAuth(cfg); router.Use(da.Middleware()); defer da.Stop()
type DigestAuth struct {
config AuthConfig
nonces sync.Map // map[string]time.Time for nonce expiry tracking
@ -75,7 +68,7 @@ type DigestAuth struct {
stopOnce sync.Once
}
// NewDigestAuth creates a new digest auth middleware
// da := NewDigestAuth(AuthConfigFromEnv()); router.Use(da.Middleware())
func NewDigestAuth(config AuthConfig) *DigestAuth {
da := &DigestAuth{
config: config,
@ -86,15 +79,14 @@ func NewDigestAuth(config AuthConfig) *DigestAuth {
return da
}
// Stop gracefully shuts down the DigestAuth, stopping the cleanup goroutine.
// Safe to call multiple times.
// defer da.Stop() // safe to call multiple times; stops the nonce cleanup goroutine
func (da *DigestAuth) Stop() {
da.stopOnce.Do(func() {
close(da.stopChan)
})
}
// Middleware returns a Gin middleware that enforces digest authentication
// router.Use(da.Middleware()) // enforces Digest or Basic auth on all routes
func (da *DigestAuth) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
if !da.config.Enabled {
@ -130,7 +122,7 @@ func (da *DigestAuth) Middleware() gin.HandlerFunc {
}
}
// sendChallenge sends a 401 response with digest auth challenge
// da.sendChallenge(c) // writes WWW-Authenticate header and 401 JSON response
func (da *DigestAuth) sendChallenge(c *gin.Context) {
nonce := da.generateNonce()
da.nonces.Store(nonce, time.Now())
@ -150,7 +142,7 @@ func (da *DigestAuth) sendChallenge(c *gin.Context) {
})
}
// validateDigest validates a digest auth header
// ok := da.validateDigest(c, c.GetHeader("Authorization"))
func (da *DigestAuth) validateDigest(c *gin.Context, authHeader string) bool {
params := parseDigestParams(authHeader[7:]) // Skip "Digest "
@ -190,7 +182,7 @@ func (da *DigestAuth) validateDigest(c *gin.Context, authHeader string) bool {
return subtle.ConstantTimeCompare([]byte(expectedResponse), []byte(params["response"])) == 1
}
// validateBasic validates a basic auth header
// ok := da.validateBasic(c, c.GetHeader("Authorization"))
func (da *DigestAuth) validateBasic(c *gin.Context, authHeader string) bool {
// Gin has built-in basic auth, but we do manual validation for consistency
user, pass, ok := c.Request.BasicAuth()
@ -205,7 +197,7 @@ func (da *DigestAuth) validateBasic(c *gin.Context, authHeader string) bool {
return userMatch && passMatch
}
// generateNonce creates a cryptographically random nonce
// nonce := da.generateNonce() // 32-char hex string, cryptographically random
func (da *DigestAuth) generateNonce() string {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
@ -216,12 +208,12 @@ func (da *DigestAuth) generateNonce() string {
return hex.EncodeToString(b)
}
// generateOpaque creates an opaque value
// opaque := da.generateOpaque() // MD5 of realm, stable per auth instance
func (da *DigestAuth) generateOpaque() string {
return md5Hash(da.config.Realm)
}
// cleanupNonces removes expired nonces periodically
// go da.cleanupNonces() // runs until stopChan is closed; interval = NonceExpiry
func (da *DigestAuth) cleanupNonces() {
interval := da.config.NonceExpiry
if interval <= 0 {
@ -246,7 +238,7 @@ func (da *DigestAuth) cleanupNonces() {
}
}
// parseDigestParams parses the parameters from a digest auth header
// params := parseDigestParams(authHeader[7:]) // {"nonce": "abc", "uri": "/api", "qop": "auth"}
func parseDigestParams(header string) map[string]string {
params := make(map[string]string)
parts := strings.Split(header, ",")
@ -267,7 +259,7 @@ func parseDigestParams(header string) map[string]string {
return params
}
// md5Hash returns the MD5 hash of a string as a hex string
// h := md5Hash("user:realm:pass") // "5f4dcc3b5aa765d61d8327deb882cf99"
func md5Hash(s string) string {
h := md5.Sum([]byte(s))
return hex.EncodeToString(h[:])