rbac is a Tier 0 module (no micro-lib dependencies). The dependency line incorrectly cited it as Tier 1. The module's own tier (4) is unchanged — it remains the auth layer above the transport infrastructure.
2.7 KiB
2.7 KiB
ADR-002: rbac.Identity as the Output Contract
Status: Accepted Date: 2026-03-18
Context
AuthMiddleware verifies a Firebase JWT and extracts a UID and claims map from the
decoded token. Downstream code needs a richer identity: application-specific role,
tenant, display name, email. Several design options exist:
- Expose Firebase token fields (
auth.Token) directly in context — ties all downstream code to Firebase types. - Define a custom identity struct in
httpauth-firebase— decouples from Firebase but creates a module-specific contract that otherhttpauth-*modules cannot share. - Use
rbac.Identityas the shared identity type — allhttpauth-*modules produce the same type; downstream code andAuthzMiddlewaredepend onrbaconly, not on any Firebase types.
Decision
EnrichmentMiddleware calls rbac.SetInContext(ctx, identity) to store the
enriched identity. AuthzMiddleware reads it with rbac.FromContext(ctx).
Downstream business logic and service handlers use rbac.FromContext directly —
they never import httpauth-firebase.
The flow is:
AuthMiddleware(this module): verifies Firebase JWT → storesuid+claimsin context under unexported keys local to this package.EnrichmentMiddleware(this module): readsuid+claims→ callsIdentityEnricher.Enrich→ storesrbac.Identityviarbac.SetInContext.AuthzMiddleware(this module): readsrbac.Identityviarbac.FromContext→ callsPermissionProvider.ResolveMask→ allows or rejects.
The intermediate uid + claims context values are stored under unexported typed
keys (ctxUIDKey{}, ctxClaimsKey{}). They are internal to httpauth-firebase
and not part of the public API.
Consequences
- Business logic and service layers only need to import
rbacto read the caller's identity. They have no knowledge of Firebase, JWTs, or token claims. - Switching from Firebase to another provider (e.g. Auth0) requires replacing the
AuthMiddlewaremodule.EnrichmentMiddleware,AuthzMiddleware, and all downstream code remain unchanged because they operate onrbac.Identity. IdentityEnricheris the application's extension point: it receives the Firebase UID and raw claims and returns a fully populatedrbac.Identitywith role and tenant. This is the only place where app-specific user store queries should occur.rbac.Identity.WithTenant(tenantID)is called inEnrichmentMiddlewareif a tenant header is configured. The base identity fromEnrichis immutable; a new value is returned.- Global ADR-003 (context helpers live with data owners) is applied here:
rbacownsSetInContextandFromContextbecauserbac.Identityis a RBAC concern, not an auth transport concern.