Files
httpauth-firebase/enrichment.go

67 lines
1.9 KiB
Go
Raw Permalink Normal View History

package httpauth
import (
"context"
"net/http"
"code.nochebuena.dev/go/rbac"
)
// IdentityEnricher builds an rbac.Identity from verified token claims.
// The application provides the implementation — typically reads from a user store.
type IdentityEnricher interface {
Enrich(ctx context.Context, uid string, claims map[string]any) (rbac.Identity, error)
}
// EnrichOpt configures EnrichmentMiddleware.
type EnrichOpt func(*enrichConfig)
type enrichConfig struct {
tenantHeader string
}
// WithTenantHeader configures the request header from which TenantID is read.
// If the header is absent on a request, TenantID remains "" — no error is returned.
func WithTenantHeader(header string) EnrichOpt {
return func(c *enrichConfig) {
c.tenantHeader = header
}
}
// EnrichmentMiddleware reads uid + claims injected by AuthMiddleware, calls
// enricher.Enrich to build a full rbac.Identity, and stores it in the context.
// Returns 401 if no uid is present (AuthMiddleware was not in the chain).
// Returns 500 if the enricher fails.
func EnrichmentMiddleware(enricher IdentityEnricher, opts ...EnrichOpt) func(http.Handler) http.Handler {
cfg := &enrichConfig{}
for _, o := range opts {
o(cfg)
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
uid, ok := getUID(r.Context())
if !ok {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
claims, _ := getClaims(r.Context())
identity, err := enricher.Enrich(r.Context(), uid, claims)
if err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
if cfg.tenantHeader != "" {
if tenantID := r.Header.Get(cfg.tenantHeader); tenantID != "" {
identity = identity.WithTenant(tenantID)
}
}
ctx := rbac.SetInContext(r.Context(), identity)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}