Compendium.Adapters.AspNetCore
ASP.NET Core integration: tenant validation middleware, security headers, problem-details mapping, and authentication helpers.
Install
dotnet add package Compendium.Adapters.AspNetCore
Configuration
The adapter exposes several middleware and helper registrations. Most projects wire the tenant middleware first.
using Compendium.Adapters.AspNetCore.Security;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<TenantValidationMiddlewareOptions>(
builder.Configuration.GetSection("Compendium:Tenant"));
var app = builder.Build();
app.UseMiddleware<TenantValidationMiddleware>();
// ...
app.Run();
TenantValidationMiddlewareOptions (see TenantValidationMiddleware.cs):
| Option | Default | Description |
|---|---|---|
TenantHeaderName |
X-Tenant-ID |
HTTP header to read the tenant from |
EnableSubdomainResolution |
true |
Whether to fall back to the host's subdomain |
IgnoredSubdomains |
www, api, admin, app, dashboard, console, portal, staging, dev, test |
Subdomains not treated as tenant identifiers |
TenantClaimTypes |
tenant_id, tid, org_id, organization_id, urn:zitadel:iam:org:id |
JWT claim names checked, in order |
ExcludedPaths |
/health, /healthz, /ready, /live, /metrics, /.well-known, /swagger, /api-docs |
Paths skipped by tenant validation |
Usage
The middleware extracts the tenant from header / subdomain / JWT, validates that all sources agree, looks up the tenant via ITenantStore, and sets TenantContext for the rest of the pipeline. See the multi-tenancy concept page for the full data flow.
Downstream handlers receive the tenant via DI:
public sealed class GetOrdersHandler(TenantContext tenant, IOrderRepository repo)
: IQueryHandler<GetOrdersQuery, IReadOnlyList<Order>>
{
public Task<IReadOnlyList<Order>> Handle(GetOrdersQuery q, CancellationToken ct)
=> repo.ListAsync(tenant.Current.Id, ct);
}
Gotchas
- Order matters. Place the tenant middleware after authentication (so the JWT claim is available) but before authorization (so policies can read
TenantContext). ExcludedPathsare prefix matches./healthmatches/healthand/healthzand/health-check. Pick paths intentionally.- CRLF in
Path. The middleware sanitizes user-controlled paths before logging (POM-175). If you mirror the pattern in your own middleware, do the same. - Subdomain resolution requires at least 3 host parts.
acme.example.comworks;localhost, IPs, and bare-domain (example.com) don't.