-
Release v1.0.0 Stable
released this
2026-05-07 23:09:23 -06:00 | 1 commits to main since this releasev1.0.0
code.nochebuena.dev/go/httpauthOverview
httpauthv1.0.0 commits the provider-agnostic middleware stack as stable and adds
ChainPermissionProvider— the last roadmap item. The module now ships three
rbac.PermissionProviderimplementations covering every common resolution pattern:
claims-embedded, TTL-cached, and chained (fast-path + fallback in the same request).Dependency bumped to
rbac v1.0.0.What Changed Since v0.1.0
New:
NewChainPermissionProviderfunc NewChainPermissionProvider(providers ...rbac.PermissionProvider) rbac.PermissionProviderTries each provider in order and returns the first non-zero mask. Errors propagate
immediately — subsequent providers are not consulted.Primary use case — JWT fast-path with DB fallback:
chain := httpauth.NewChainPermissionProvider( httpauth.NewClaimsPermissionProvider("permisos"), // JWT claims — no DB call httpauth.NewCachedPermissionProvider(dbProvider, valkeyCache, 5*time.Minute), // fallback ) r.With(httpauth.AuthzMiddleware(chain, "usuarios", rbac.Permission(1))).Get("/usuarios", h)When the JWT embeds permission masks (
customClaimsfromjwtauth.IssueTokenPair),
the chain returns immediately from the first provider. When the JWT has no embedded
masks (e.g. a token issued before permissions were cached in claims), the chain falls
through to the DB-backed provider transparently.Dependency bump
code.nochebuena.dev/go/rbac v0.9.0 → v1.0.0Full API (stable)
SetTokenData(ctx, uid, claims) context.Context— integration contract called
by provider-specific AuthMiddleware implementations.EnrichmentMiddleware(enricher, opts...) func(http.Handler) http.HandlerAuthzMiddleware(provider rbac.PermissionProvider, resource string, required rbac.Permission) func(http.Handler) http.HandlerNewClaimsPermissionProvider(claimsKey string) rbac.PermissionProvider— reads
bitmasks from JWT claims; handlesfloat64(JSON) andint64.NewCachedPermissionProvider(inner, cache, ttl) rbac.PermissionProvider— TTL
cache with silent fallthrough on cache error. Cache key:rbac:{uid}:{resource}.NewChainPermissionProvider(providers...) rbac.PermissionProvider— first
non-zero mask wins; errors propagate immediately. (New in v1.0.0)IdentityEnricherinterface —Enrich(ctx, uid, claims) (rbac.Identity, error)Cacheinterface —Get(ctx, key) (int64, bool, error)andSet(ctx, key, value, ttl) errorWithTenantHeader(header string) EnrichOptMigration from v0.1.0
No breaking changes. The only addition is
NewChainPermissionProvider.go get code.nochebuena.dev/go/httpauth@v1.0.0 go get code.nochebuena.dev/go/rbac@v1.0.0Downloads
-
Release v0.1.0 Stable
released this
2026-05-07 21:44:08 -06:00 | 2 commits to main since this releasev0.1.0
code.nochebuena.dev/go/httpauthOverview
httpauthprovides provider-agnostic HTTP middleware for identity enrichment and
RBAC authorization. It is the shared foundation for allhttpauth-*provider modules
(httpauth-firebase,httpauth-jwt, etc.) — they converge onrbac.Identityas
the output contract and callSetTokenDatato makeEnrichmentMiddlewareand
AuthzMiddlewarework without any further changes.The module ships two
rbac.PermissionProviderimplementations for the two common
architectures: JWT-embedded permissions (no runtime DB call) and TTL-cached runtime
resolution (for large or independently revocable permission sets).What's Included
SetTokenData(ctx context.Context, uid string, claims map[string]any) context.ContextInjects a verified uid and raw claims into the request context. Called by any
upstreamAuthMiddlewareafter token verification.EnrichmentMiddlewarereads
these values automatically. The context keys are unexported — callers interact
exclusively through this function and the downstream middleware.EnrichmentMiddleware(enricher IdentityEnricher, opts ...EnrichOpt) func(http.Handler) http.HandlerReads the uid and claims injected by any upstream
AuthMiddlewareviaSetTokenData,
calls the application-providedIdentityEnricher, and stores the resulting
rbac.Identityin context viarbac.SetInContext. Supports optional tenant header
extraction viaWithTenantHeader. Returns 401 if no uid is present; returns 500 if
the enricher fails.AuthzMiddleware(provider rbac.PermissionProvider, resource string, required rbac.Permission) func(http.Handler) http.HandlerReads
rbac.Identityfrom context (set byEnrichmentMiddleware), resolves the
permission mask via the providedrbac.PermissionProvider, and gates the request
against the required permission bit. Returns 401 if no identity is in context; returns
403 if the permission check fails or the provider errors. Usesrbac.PermissionProvider
directly — no local redefinition.NewClaimsPermissionProvider(claimsKey string) rbac.PermissionProviderReads pre-computed permission masks from JWT claims stored in context by
SetTokenData.
Expectsclaims[claimsKey]to be amap[string]anywhere each key is a resource name
and the value is the bitmask asint64orfloat64(JSON decodes numbers as float64).
Returns 0 without error if the claim is absent. No DB call, no network — suitable for
simple applications that embed permissions at token issuance time.Cacheinterface +NewCachedPermissionProvider(inner rbac.PermissionProvider, cache Cache, ttl time.Duration) rbac.PermissionProviderWraps any
rbac.PermissionProviderwith a TTL-based cache layer. Cache key format:
rbac:{uid}:{resource}. On cache miss, falls through toinnerand populates the
cache. On cache error, falls through silently — never fails due to cache unavailability.
TTL-based expiry only; for explicit invalidation, callers interact with theCache
directly using the known key format.Interfaces
IdentityEnricher— application-implemented; receives uid and raw claims, returnsrbac.IdentityCache— caching backend abstraction; implement with Valkey, Redis, or in-memory
Options
WithTenantHeader(header string) EnrichOpt— reads a tenant ID from a named request header and attaches it to the identity
Installation
go get code.nochebuena.dev/go/httpauth@v0.1.0Requires
code.nochebuena.dev/go/rbaconly. No external provider dependencies.Design Highlights
Provider-agnostic by design.
EnrichmentMiddlewareandAuthzMiddlewaredepend
only onSetTokenDatahaving been called upstream. AnyAuthMiddleware— Firebase,
self-issued JWT, API key, mTLS — is compatible without modifying the enrichment or
authorization layer. Provider-specific modules are thin adapters that callSetTokenData
and defer everything else to this module.rbac.PermissionProviderwithout redefinition.AuthzMiddlewareaccepts
rbac.PermissionProviderdirectly. Therbacpackage is the single source of truth
for this interface; local redefinition inhttpauth-firebasehas been removed.Two permission resolution strategies as first-class citizens.
ClaimsPermissionProvider
andCachedPermissionProvideraddress the two main architectures upfront. Applications
choose based on token size constraints and revocation requirements — not an afterthought.Cache falls through, never fails.
CachedPermissionProvidertreats cache errors as
misses. A Valkey outage degrades gracefully to the inner provider — the application
continues to serve requests with slightly higher latency. No circuit-breaker or fallback
logic required in application code.Minimal dependency surface. The only import is
code.nochebuena.dev/go/rbac. No
logger, no HTTP framework, no external service SDK. Errors surface as HTTP responses;
the enricher and provider implementations log at their own layer.Known Limitations & Edge Cases
ClaimsPermissionProvidercannot distinguish between "claim absent" and "mask is
genuinely 0". Both return(0, nil). Callers that need to distinguish should encode
a sentinel value or check for claim presence in the enricher.CachedPermissionProvideruses TTL-based expiry exclusively. After a role or
permission update, affected users retain cached masks until TTL expires. Set TTL
short enough to meet your consistency requirements (5 minutes is a typical default).
For immediate invalidation, deleterbac:{uid}:{resource}directly via yourCache.AuthzMiddlewaretreats provider errors and permission-check failures identically
(both return 403). The two cases are intentionally indistinguishable to callers.EnrichmentMiddlewarereturns a generic 500 if the enricher fails. Log the error
inside your enricher implementation; it is not surfaced to the HTTP client.
v0.1.0 → v1.0.0 Roadmap
- Evaluate adding
Delto theCacheinterface to support explicit invalidation viaCachedPermissionProvider - Consider an optional error observer hook for enricher and provider errors
- Add
ChainPermissionProviderfor OR-ing multiple providers (e.g. claims fast-path + DB fallback in the same request) - Production hardening across multiple deployed services
Downloads