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
48 lines
2.7 KiB
Markdown
48 lines
2.7 KiB
Markdown
# Changelog
|
|
|
|
## v1.0.0
|
|
|
|
Initial release.
|
|
|
|
### `authmw`
|
|
|
|
- `BagEnricher` type — `func(bag security.SecurityBag, r *http.Request) security.SecurityBag`;
|
|
enriches the request-scoped SecurityBag after the base Identity is built. Register via
|
|
`WithBagEnricher`. 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 by `EnrichmentMiddleware`.
|
|
- `GetClaims` — exported accessor for raw token claims stored by `SetTokenData`. Available
|
|
to custom `IdentityEnricher` implementations and `ClaimsPermissionProvider`.
|
|
- `EnrichmentMiddleware` — builds a `security.SecurityBag` from uid+claims. Calls the
|
|
application `IdentityEnricher`, wraps the Identity in a SecurityBag, runs all registered
|
|
`BagEnricher` functions in order, then stores the bag via `security.SetBagInContext`.
|
|
Accepts `logging.Logger`; routes errors through `httputil.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).
|
|
- `IdentityEnricher` interface — implemented by the application to load user data from uid+claims.
|
|
- `EnrichOpt` type — `func(*enrichConfig)`.
|
|
- `WithTenantHeader(header string) EnrichOpt` — reads Identity.TenantID from a named request
|
|
header. Implemented as a `BagEnricher` internally.
|
|
- `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 any `security.PermissionProvider` with 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 `...CachedOpt` for customization.
|
|
- `CachedOpt` type — `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.
|
|
- `Cache` interface — pluggable cache backend. Satisfied by `einherjar/cache-valkey` via duck
|
|
typing.
|