Introduces code.nochebuena.dev/einherjar/auth — the provider-agnostic HTTP
authentication and authorization layer of the Einherjar framework. Absorbs
two micro-lib packages (httpauth, rbac) as sub-packages, replacing the
Identity-only context model with a SecurityBag-native design and adding a
composable enrichment chain.
authmw:
- BagEnricher function type — enriches the request-scoped SecurityBag after
the base Identity is built; registered via WithBagEnricher; multiple
enrichers run in order, each receiving the bag from the previous
- IdentityEnricher interface — application-layer contract for loading user
data from uid+claims
- EnrichmentMiddleware — builds SecurityBag from uid+claims, runs enricher
chain, stores via security.SetBagInContext; 401 on missing uid, 500 on
enricher error; routes all errors through httputil.Error
- AuthzMiddleware — per-route permission gate; 401 on missing identity,
403 on provider error (fail-closed) or insufficient permissions
- EnrichOpt type + WithTenantHeader (reads TenantID from header, implemented
as a BagEnricher) + WithBagEnricher (registers custom enrichers for
hardware IDs, grant codes, or any bag attribute)
- SetTokenData / GetClaims — integration contract for auth-jwt / auth-firebase
rbac:
- NewClaimsPermissionProvider — reads flat JWT claim bitmasks from context;
wildcard "*" fallback; handles int64/float64/json.Number; zero DB calls
- NewCachedPermissionProvider — TTL cache wrapping any PermissionProvider;
default key "rbac:{uid}:{resource}" or "rbac:{tenantID}:{uid}:{resource}";
TenantID sourced from SecurityBag automatically; accepts ...CachedOpt
- CachedOpt type + WithCacheKey — overrides the key function for extra
dimensions (hardware IDs, grant codes read from bag attributes)
- NewChainPermissionProvider — tries providers in order; first non-zero wins;
errors short-circuit; typical pattern: claims → cached DB fallback
- Cache interface — pluggable backend satisfied by cache-valkey via duck typing
Compliance test (package auth_test) enforces CT-6 (≤1 exported TypeSpec/file),
compile-time interface satisfaction, and behavioural coverage across the full
middleware and provider surface: enrichment success/failure, tenant header,
custom BagEnricher, bag-in-context, authz allowed/denied/error, claims
hit/wildcard/missing/float64, cached hit/miss/error/tenant-key/custom-key,
chain first-non-zero/fallthrough/error.
Depends on contracts v1.0.0, core v1.0.0, web v1.0.0.
- identifiable.go: package-level Module variable (observability.Identifiable) for version
identification — auth is middleware-only; not registered with the launcher
2.7 KiB
2.7 KiB
Changelog
v1.0.0
Initial release.
authmw
BagEnrichertype —func(bag security.SecurityBag, r *http.Request) security.SecurityBag; enriches the request-scoped SecurityBag after the base Identity is built. Register viaWithBagEnricher. Multiple enrichers run in registration order, each receiving the bag returned by the previous one.SetTokenData— integration contract for provider packages (auth-jwt, auth-firebase). Stores uid and raw claims in context via typed keys; consumed byEnrichmentMiddleware.GetClaims— exported accessor for raw token claims stored bySetTokenData. Available to customIdentityEnricherimplementations andClaimsPermissionProvider.EnrichmentMiddleware— builds asecurity.SecurityBagfrom uid+claims. Calls the applicationIdentityEnricher, wraps the Identity in a SecurityBag, runs all registeredBagEnricherfunctions in order, then stores the bag viasecurity.SetBagInContext. Acceptslogging.Logger; routes errors throughhttputil.Error(401 on missing token, 500 on enricher failure).AuthzMiddleware— per-route permission gate. Returns 401 on missing identity, 403 on provider error or insufficient permissions (fail-closed).IdentityEnricherinterface — implemented by the application to load user data from uid+claims.EnrichOpttype —func(*enrichConfig).WithTenantHeader(header string) EnrichOpt— reads Identity.TenantID from a named request header. Implemented as aBagEnricherinternally.WithBagEnricher(fn BagEnricher) EnrichOpt— registers a custom enricher. Use for any attribute beyond TenantID: hardware IDs, grant codes, etc.
rbac
NewClaimsPermissionProvider— reads pre-computed bitmasks from JWT claims in context. Flat format:claims[claimsKey][resource] = mask. Wildcard"*"fallback. Handles int64, float64, json.Number.NewCachedPermissionProvider— wraps anysecurity.PermissionProviderwith TTL caching. Default cache key:"rbac:{uid}:{resource}"(single-tenant) or"rbac:{tenantID}:{uid}:{resource}"(multi-tenant). TenantID sourced from the SecurityBag in context automatically. Accepts...CachedOptfor customization.CachedOpttype —func(*cachedConfig).WithCacheKey(fn func(security.SecurityBag, string, string) string) CachedOpt— overrides the default cache key function. Use when additional bag attributes (hardware IDs, grant codes) must be part of the key.NewChainPermissionProvider— tries providers in order; returns first non-zero mask. Errors short-circuit.Cacheinterface — pluggable cache backend. Satisfied byeinherjar/cache-valkeyvia duck typing.