# httpauth Provider-agnostic HTTP middleware for identity enrichment and RBAC authorization. ## Purpose `httpauth` is the shared foundation for all `httpauth-*` provider modules. It provides `EnrichmentMiddleware`, `AuthzMiddleware`, two `rbac.PermissionProvider` implementations (`ClaimsPermissionProvider` and `CachedPermissionProvider`), and `SetTokenData` — the bridge between a provider-specific `AuthMiddleware` and the rest of the auth stack. Any `AuthMiddleware` that calls `SetTokenData` after token verification is compatible. Downstream code reads identity exclusively via `rbac.FromContext` — no provider type leaks past the middleware boundary. ## Tier & Dependencies **Tier:** 3 (transport auth layer; depends only on Tier 0 `rbac`; no external SDK) **Module:** `code.nochebuena.dev/go/httpauth` **Direct imports:** `code.nochebuena.dev/go/rbac` only `httpauth` does not import `logz`, `httpmw`, `httputil`, Firebase, or any JWT library. It has no logger parameter — errors are returned as HTTP responses. ## Key Design Decisions - **`rbac.PermissionProvider` without redefinition:** `AuthzMiddleware` accepts `rbac.PermissionProvider` directly. `rbac` is the single source of truth for this interface. Provider-specific modules (e.g. `httpauth-firebase`) previously defined their own `PermissionProvider` locally — that duplication is removed. - **`SetTokenData` as the integration contract:** Provider-specific `AuthMiddleware` implementations call `SetTokenData(ctx, uid, claims)` after verifying the token. The context keys are unexported typed structs. `EnrichmentMiddleware` reads them via the unexported helpers `getUID` and `getClaims` in the same package. - **Two permission strategies:** `ClaimsPermissionProvider` (JWT-embedded, no DB call) and `CachedPermissionProvider` (TTL-backed runtime resolution) are first-class implementations. Choose based on token size and revocation requirements. - **Cache falls through on error:** `CachedPermissionProvider` treats cache errors as misses — a cache outage degrades gracefully to the inner provider. ## Patterns **Full stack with self-issued JWT:** ```go r.Use(jwtauth.AuthMiddleware(signer, publicPaths, nil)) r.Use(httpauth.EnrichmentMiddleware(myEnricher, httpauth.WithTenantHeader("X-Tenant-ID"))) // Simple app — permissions embedded in JWT: claimsProvider := httpauth.NewClaimsPermissionProvider("permisos") r.With(httpauth.AuthzMiddleware(claimsProvider, "usuarios", rbac.Permission(1))). Get("/usuarios", handler) // Complex app — runtime resolution with cache: cachedProvider := httpauth.NewCachedPermissionProvider(dbProvider, valkeyCache, 5*time.Minute) r.With(httpauth.AuthzMiddleware(cachedProvider, "usuarios", rbac.Permission(1))). Get("/usuarios", handler) ``` **Provider-specific AuthMiddleware calling SetTokenData:** ```go // Inside httpauth-jwt or httpauth-firebase AuthMiddleware, after token verification: ctx := httpauth.SetTokenData(r.Context(), verified.UID, verified.Claims) next.ServeHTTP(w, r.WithContext(ctx)) ``` **Reading identity in a handler:** ```go identity, ok := rbac.FromContext(r.Context()) if !ok { // should not happen if EnrichmentMiddleware is in the chain } ``` **Implementing Cache (e.g. with Valkey):** ```go type valkeyCache struct{ client valkey.Client } func (c *valkeyCache) Get(ctx context.Context, key string) (int64, bool, error) { ... } func (c *valkeyCache) Set(ctx context.Context, key string, val int64, ttl time.Duration) error { ... } ``` **Cache key for manual invalidation:** `rbac:{uid}:{resource}` ## What to Avoid - Do not call `SetTokenData` from application or domain layer code. It is the exclusive responsibility of provider-specific `AuthMiddleware` implementations. - Do not put `AuthzMiddleware` before `EnrichmentMiddleware` in the chain. `AuthzMiddleware` reads `rbac.Identity` from context; if enrichment has not run, it will return 401. - Do not import `httpauth` from service or domain layers. It is a transport package. - Do not define a local `PermissionProvider` interface in provider modules that import this package — use `rbac.PermissionProvider` directly. ## Testing Notes - `compliance_test.go` verifies at compile time that mock types satisfy `IdentityEnricher` and `Cache`, and that `rbac.PermissionProvider` is satisfied by the two built-in provider implementations. - `EnrichmentMiddleware` tests use `injectTokenData(uid, claims, next)` — a helper that calls `SetTokenData` to bypass a real upstream `AuthMiddleware`. - `AuthzMiddleware` tests pre-populate context with `rbac.SetInContext` — no enrichment middleware needed. - `ClaimsPermissionProvider` tests exercise both `float64` (JSON decode) and `int64` paths for the mask value. - `CachedPermissionProvider` tests exercise cache hit, miss, cache error fallthrough, and inner provider error propagation.