Rene Nochebuena 18e5a16f7e feat(httpauth): initial release — provider-agnostic HTTP auth middleware
Provides SetTokenData for upstream AuthMiddleware implementations,
EnrichmentMiddleware and AuthzMiddleware compatible with any provider that
calls SetTokenData, ClaimsPermissionProvider for JWT-embedded permissions,
and CachedPermissionProvider for TTL-backed runtime resolution via any
Cache implementation.
2026-05-07 21:37:25 -06:00

httpauth

Provider-agnostic HTTP middleware for identity enrichment and RBAC authorization.

Overview

Three composable func(http.Handler) http.Handler middleware functions and two rbac.PermissionProvider implementations:

Component Responsibility
EnrichmentMiddleware Calls app-provided IdentityEnricher; stores rbac.Identity in context
AuthzMiddleware Resolves permission mask via rbac.PermissionProvider; gates request
ClaimsPermissionProvider Reads pre-computed masks from JWT claims — no DB call
CachedPermissionProvider Wraps any provider with a TTL cache; falls through on miss or error
SetTokenData Injects uid + claims from any verified token into the request context

Any upstream AuthMiddleware that calls SetTokenData is compatible — Firebase, self-issued JWT, API key, etc.

Installation

require code.nochebuena.dev/go/httpauth v0.1.0

Usage

With JWT-embedded permissions (simple apps)

// Auth middleware (e.g. httpauth-jwt) calls httpauth.SetTokenData after verification.
// JWT claims include: { "permisos": { "usuarios": 515, "roles": 6 } }

r.Use(jwtauth.AuthMiddleware(signer, publicPaths, nil))
r.Use(httpauth.EnrichmentMiddleware(myEnricher))

claimsProvider := httpauth.NewClaimsPermissionProvider("permisos")
r.With(httpauth.AuthzMiddleware(claimsProvider, "usuarios", rbac.Permission(1))).
    Get("/usuarios", handler)

With runtime resolution + cache (complex apps)

r.Use(jwtauth.AuthMiddleware(signer, publicPaths, nil))
r.Use(httpauth.EnrichmentMiddleware(myEnricher, httpauth.WithTenantHeader("X-Tenant-ID")))

dbProvider := myapp.NewDBPermissionProvider(db)
cachedProvider := httpauth.NewCachedPermissionProvider(dbProvider, valkeyCache, 5*time.Minute)

r.With(httpauth.AuthzMiddleware(cachedProvider, "usuarios", rbac.Permission(1))).
    Get("/usuarios", handler)

Interfaces

IdentityEnricher

type IdentityEnricher interface {
    Enrich(ctx context.Context, uid string, claims map[string]any) (rbac.Identity, error)
}

Implement in your application to load user data and return an rbac.Identity.

Cache

type Cache interface {
    Get(ctx context.Context, key string) (int64, bool, error)
    Set(ctx context.Context, key string, value int64, ttl time.Duration) error
}

Implement with Valkey, Redis, or any in-memory store. Cache keys follow the format rbac:{uid}:{resource}.

rbac.PermissionProvider (from the rbac package)

type PermissionProvider interface {
    ResolveMask(ctx context.Context, uid, resource string) (rbac.PermissionMask, error)
}

AuthzMiddleware accepts any implementation — ClaimsPermissionProvider, CachedPermissionProvider, or your own.

SetTokenData

func SetTokenData(ctx context.Context, uid string, claims map[string]any) context.Context

Called by provider-specific AuthMiddleware implementations after token verification. EnrichmentMiddleware reads the injected values automatically.

Options

Option Description
WithTenantHeader(header) Reads TenantID from the named request header. If absent, TenantID remains "".

HTTP status codes

Condition Status
No uid in context (EnrichmentMiddleware) 401
Enricher error 500
No rbac.Identity in context (AuthzMiddleware) 401
Permission denied or provider error 403

Cache key format

CachedPermissionProvider uses rbac:{uid}:{resource} as the cache key. To invalidate manually, delete the key directly via your Cache implementation.

Description
No description provided
Readme MIT 51 KiB
2026-05-07 23:09:23 -06:00
Languages
Go 100%