Csag.Blueprint.Infrastructure 0.5.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Csag.Blueprint.Infrastructure --version 0.5.0
                    
NuGet\Install-Package Csag.Blueprint.Infrastructure -Version 0.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Csag.Blueprint.Infrastructure" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Csag.Blueprint.Infrastructure" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="Csag.Blueprint.Infrastructure" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Csag.Blueprint.Infrastructure --version 0.5.0
                    
#r "nuget: Csag.Blueprint.Infrastructure, 0.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Csag.Blueprint.Infrastructure@0.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Csag.Blueprint.Infrastructure&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=Csag.Blueprint.Infrastructure&version=0.5.0
                    
Install as a Cake Tool

Csag.Blueprint.Infrastructure

Overview

This package provides the shared infrastructure implementation layer for CSAG Blueprint-based applications.

It contains the reusable EF Core persistence backbone, session/auth infrastructure, localization infrastructure, tenancy helpers, authorization transformation, and table-view execution components that consuming applications compose into their own host.

What this package owns

Persistence backbone

Component Purpose
BlueprintDbContext<TAppTenant, TAppUser, TAppRole> Shared EF Core base context that owns the blueprint persistence model.
Blueprint*Configuration classes EF Core mappings for blueprint-owned entities and inheritance roots.
MultiTenancyModelBuilderExtensions Applies tenant filters/indexing/model conventions for tenant-scoped entities. See Data isolation topology for the database-per-tenant option.

Data isolation topology

ConfigureBlueprintMultiTenancy implements the pooled topology: every entity implementing IMustHaveTenant gets a global query filter on TenantId, an index on that column, and a foreign key to the tenant table.

modelBuilder.ConfigureBlueprintMultiTenancy<ApplicationTenant, ApplicationDbContext>(this);

Because isolation is enforced by the filter and not the database, any table that cannot carry a tenant discriminator — the user table, since identity is shared — is a permanent sharp edge that must be scoped by membership on every query.

Moving to database-per-tenant. Pass addTenantForeignKey: false when tenant-owned data lives in a different database from the tenant table; a foreign key cannot cross databases, so emitting one there produces an invalid model. The filter and index still apply — only referential integrity moves from the database to the application.

modelBuilder.ConfigureBlueprintMultiTenancy<ApplicationTenant, BusinessDbContext>(
    this, addTenantForeignKey: false);

Note that IMustHaveTenant is not a synonym for "shardable". Some tenant-owned entities are identity concerns — a service account is tenant-scoped but authentication needs it, so it must stay in the central database. Classify each entity by plane before splitting anything.

Interceptors

Interceptor Purpose
AuditableTimestampInterceptor Sets CreatedAt/UpdatedAt automatically for IAuditable entities.
TenantSaveInterceptor Assigns and protects TenantId for IMustHaveTenant entities.

Both are registered as singletons by AddBlueprintTenancyRuntime(). Resolve them from the container when wiring the pooled DbContext factory rather than constructing them — they are singleton-safe because they read ambient AsyncLocal state and a singleton TimeProvider, never scoped services. AuditableTimestampInterceptor stamps from that TimeProvider, so a FakeTimeProvider registered before AddBlueprintTenancyRuntime() controls the timestamps it writes.

Session and authorization infrastructure

Component Purpose
DistributedCacheTicketStore ASP.NET Core ITicketStore implementation for server-side session storage. Owns session tracking: every ticket it stores or renews is mirrored into the session's tracking row.
ActiveSessionTracker<TContext> Writes the tracking rows (BlueprintActiveSessions) for the ticket store: create or re-attribute on sign-in, follow the ticket on renewal, delete on removal.
TicketCacheService Serialization/cache wrapper for authentication tickets.
PostConfigureCookieAuthenticationOptions Injects the ticket store into cookie authentication options.
ISessionLifecycle implementation The intents applications call; registered by AddBlueprintSessionInfrastructure.
ExpiredSessionCleanupService<TContext> Hosted job deleting the tracking rows of expired sessions, user and service-account sessions alike.
IServiceAccountCredentials / IServiceAccountSessionLifecycle implementations The service-account intents; registered by AddBlueprintServiceAccountAuthentication. See Service-account credentials.
PermissionClaimsTransformation Expands role claims into permission claims after authentication.
UserManagerAuthorizationExtensions Loads roles and permissions for users.

The packages own the cookie session lifecycle; the application registers it and states intents.

services.AddBlueprintSessionInfrastructure<ApplicationUser, ApplicationDbContext>(options =>
    options.ClientIpHeader = "X-Client-IP");
services.AddBlueprintCookieSessions(cookie =>
{
    cookie.SecurePolicy = securitySettings.CookieSecurePolicy;
    cookie.SameSite = SameSiteMode.Lax;
});

AddBlueprintCookieSessions turns the Identity application cookie into the session cookie, after every other Configure (including AddIdentity's) in either registration order. The callback shapes the cookie; the package then applies what it owns:

  • the ticket store, with the session tracked server-side and the cookie carrying only its key;
  • an HTTP-only, persistent cookie whose lifetime is BlueprintSessionOptions.Lifetime — set from Blueprint:Security:SessionExpirationHours by Csag.Blueprint.Web — with sliding expiration, so sign-in code sets no expiry;
  • 401 and 403 instead of login and access-denied redirects;
  • no Identity security-stamp validation: the session principal carries no stamp, and sessions end through the lifecycle instead;
  • ISessionEligibility asked at every sign-in and sliding renewal.

Cookie event handlers the application assigns on CookieAuthenticationOptions.Events keep running, before the package's. A scheme that sets EventsType, or whose events object overrides the events the package relies on, fails when its options are built.

The tracking row records the client address with the rule the rate limiter uses: ClientIpHeader when present and parseable, otherwise the connection's remote address. A sign-in on a request that still carries a live session cookie reuses that cookie's key; the row is then attributed to the signing-in account.

Applications report what happened through ISessionLifecycle, after their database commit:

Intent Typical caller
CredentialsChangedAsync password change or reset, second factor enabled or disabled
AccessRevokedAsync (optionally per tenant) account disabled or deleted, sign-in identity changed, membership removed
ClaimsChangedAsync (optionally per tenant) profile, language, roles or permissions changed
TenantRemovedAsync tenant deleted
SwitchTenantAsync the caller switches tenant
ListMySessionsAsync / RevokeMySessionAsync the caller's own session management; RevokeMySessionAsync returns false when the caller's account has no session with that id, for the application to answer 404

Hooks the application keeps: ISessionRevocationParticipants for its own credentials, an ISessionEligibility, the cookie callback, its own cookie event handlers, and decorators around ITicketStore or IActiveSessionTracker (resolve DistributedCacheTicketStore or ActiveSessionTracker<TContext> as the inner instance). The expired-session cleanup runs hourly by default; set ExpiredSessionCleanup.Interval, or turn it off with ExpiredSessionCleanup.Enabled = false.

Service-account credentials

The packages own a service account's credentials from end to end: the secret and its hash, issuing bearer tokens, the lockout, rotation, revocation, and validating the tokens. The application keeps its endpoints (route, rate limit, OpenAPI text) and the validation of the account data it accepts, and states intents.

services.AddBlueprintServiceAccountAuthentication<ApplicationDbContext>();

The settings come from Blueprint:Security:Jwt and Blueprint:Security:ServiceAccountLockout, which Csag.Blueprint.Web applies to BlueprintServiceAccountOptions. The registration needs no signing key, so it can run unconditionally — also in a host started only to export its OpenAPI document. A configured key must have at least 32 characters besides surrounding white space, checked at startup; a missing key fails the first issuance or validation.

Intent Effect
IssueTokenAsync(clientId, clientSecret) Returns a token (AccessToken, TokenType = Bearer, ExpiresIn in seconds) or a refusal: InvalidCredentials (401) for an unknown client, a wrong secret or a locked account, AccountInactive (403) only for the correct secret of a deactivated account.
CreateCredentialsAsync(serviceAccount) Generates the client id and secret, stores the account with the secret's hash, returns the secret once.
RotateSecretAsync(serviceAccountId) Generates a new secret; the previous secret and its tokens stop working at once. Looks the account up within the ambient tenant; null when there is none.
AccountDeactivatedAsync(serviceAccountId) / AccountDeletedAsync(serviceAccountId) Ends the account's sessions, after the application committed the change.

Issuance verifies every secret with the same key derivation, unknown clients included. A wrong secret counts towards the lockout in one statement, so concurrent attempts all count; a locked account is refused whatever secret it is given. Every time — issuance, expiry, lockout — comes from the injected TimeProvider. Rotation, deactivation and deletion tell the ISessionRevocationParticipants, with a service-account SessionSubject.

Each token refers to a server-side session and carries only ServiceAccountClaimTypes claims (sub, name, type, sid). The service-account scheme pins HS256, validates issuer, audience and lifetime with no clock skew, signs and validates over the key's UTF-8 bytes, and does not map inbound claims. Every request resolves the token's session and builds the principal from the account's current name, tenant, roles and permissions; register IServiceAccountClaimsEnrichers to add the application's own claims.

Secrets are hashed in ASP.NET Core Identity's PasswordHasher format with fixed parameters, so hashes the application wrote with a default PasswordHasher keep verifying. A data seed sets a known secret with serviceAccount.SetClientSecret(secret).

Three authentication schemes are registered. Each request is authenticated by exactly one scheme:

Scheme Role
BlueprintAuthenticationSchemes.ServiceAccountToken Validates service-account tokens. Endpoints do not list it.
BlueprintAuthenticationSchemes.Bearer ("Bearer") Forwards each request to the handler of its token kind; the service-account scheme unless an application token kind added through BlueprintBearerOptions.AddTokenKind claims the token.
BlueprintAuthenticationSchemes.SessionOrBearer Forwards a request that carries a bearer token to Bearer, every other request to the session cookie.

Expired service-account sessions are deleted by the job AddBlueprintSessionInfrastructure registers.

Tenancy and localization infrastructure

Component Purpose
TenantService / TenantManager Reusable tenant access and membership logic.
BlueprintDbStringLocalizer / BlueprintDbStringLocalizerFactory Database-backed localization infrastructure.
PassThroughStringLocalizer Generation-mode localizer that returns keys as-is.
TranslationCacheKeys Cache key helpers for localization caching.
MigrationBuilderExtensions Translation seeding helpers for migrations.

Table view infrastructure

Component Purpose
TableViewExecutor Executes filtering, sorting, counting, pagination, and projection for table-view queries.
TableViewCatalogService Discovers and filters registered table views by permission.
BlueprintTableViewPreferencesService Persists per-user table view preferences.

Ownership Boundary

This package owns reusable infrastructure, not application composition.

The consuming application still owns:

  • the concrete ApplicationDbContext
  • DI composition and host setup
  • app-specific options and policies
  • app-specific entities and migrations
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Csag.Blueprint.Infrastructure:

Package Downloads
Csag.Blueprint.Web

ASP.NET Core middleware, validated options, security configuration, and FastEndpoints extensions for CSAG Blueprint applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0 52 10/30/2026
0.7.0 91 10/28/2026
0.6.0 217 10/23/2026
0.5.0 469 10/20/2026
0.4.0 812 10/14/2026
0.3.0 1,084 9/15/2026
0.2.0 323 9/8/2026
0.1.3 755 8/20/2026
0.1.2 193 8/19/2026
0.1.1 162 8/13/2026
0.1.0 166 7/3/2026