Class IdentityDirectoryServiceCollectionExtensions
- Namespace
- Microsoft.Extensions.DependencyInjection
- Assembly
- Stratara.Identity.EntityFrameworkCore.dll
DI extensions that register the Stratara identity-directory plane: the EF-backed tenant membership store, the membership-backed authorization provider, and the membership-backed cross-tenant authorizer.
public static class IdentityDirectoryServiceCollectionExtensions
- Inheritance
-
IdentityDirectoryServiceCollectionExtensions
- Inherited Members
Methods
AddApiKeyStoreFromContextFactory<TContext>(IServiceCollection)
Register the EF Core IApiKeyStore against TContext, taking
a fresh context for each operation from the registered context factory.
public static IServiceCollection AddApiKeyStoreFromContextFactory<TContext>(this IServiceCollection services) where TContext : DbContext
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TContextThe DbContext hosting the directory tables.
Examples
Needs AddDbContextFactory<TContext>(); one context per operation:
services.AddDbContextFactory<DirectoryDbContext>();
services.AddApiKeyStoreFromContextFactory<DirectoryDbContext>();
Remarks
Each operation creates its own context from the registered IDbContextFactory<TContext> and disposes it afterwards, so directory operations issued concurrently within one scope do not contend and a store's commit reaches only its own rows. In exchange, a store write does not take part in a transaction the consumer opened on their own scoped context.
Requires AddDbContextFactory<TContext>(); a missing registration fails at first
resolution rather than silently. Keep the factory's options aligned with the scoped
registration — interceptors, query filters and conventions are configured per registration,
not per context type.
Calling this together with AddApiKeyStore<TContext>(IServiceCollection) leaves whichever ran first in place; they do not compose.
AddApiKeyStore<TContext>(IServiceCollection)
Register the EF Core IApiKeyStore against
TContext — issuance (raw key shown once, hash stored), idempotent
import of a caller-supplied key for bootstrap scenarios, fail-closed validation, revocation,
and per-tenant/per-user erasure sweeps. Machine keys are materialized as memberships of
their tenant, so they flow through the same role/permission plane as human actors.
public static IServiceCollection AddApiKeyStore<TContext>(this IServiceCollection services) where TContext : DbContext
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TContextThe DbContext hosting the directory tables.
Examples
services.AddApiKeyStore<DirectoryDbContext>();
Remarks
The store shares the context registered for the request, so directory operations issued concurrently within one scope fail on whichever arrives second — a database context serves one operation at a time — and the store's own commit also commits whatever else the consumer has left unsaved on that context. Register the store with AddApiKeyStoreFromContextFactory<TContext>(IServiceCollection) instead to give each operation its own context.
Calling both registrations leaves whichever ran first in place; they do not compose.
AddCatalogPermissionResolver(IServiceCollection)
Register CatalogPermissionResolver as the IPermissionResolver — permissions derive from tenant-scoped membership roles mapped through the catalog's role grants. Use the generic overload (AddCatalogPermissionResolver<TUser>(IServiceCollection)) when global ASP.NET Identity roles should also grant permissions.
public static IServiceCollection AddCatalogPermissionResolver(this IServiceCollection services)
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
Resolves a principal's permissions by running its membership roles through the catalog:
services.AddPermissionCatalog(c => c.Add("sims.read"));
services.AddCatalogPermissionResolver();
AddCatalogPermissionResolver<TUser>(IServiceCollection)
Register CatalogPermissionResolver<TUser> as the
IPermissionResolver — permissions derive from tenant-scoped membership roles
and global ASP.NET Identity roles, mapped through the catalog's role grants.
Requires the host's Identity registration (UserManager<TUser> resolvable).
public static IServiceCollection AddCatalogPermissionResolver<TUser>(this IServiceCollection services) where TUser : class
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TUserThe host's ASP.NET Identity user entity.
Examples
The generic overload also considers the user's global ASP.NET Core Identity roles:
services.AddCatalogPermissionResolver<ApplicationUser>();
AddMembershipAuthorization(IServiceCollection)
Register MembershipAuthorizationProvider as the IAuthorizationProvider — role checks pass on tenant-scoped membership roles only. Use the generic overload (AddMembershipAuthorization<TUser>(IServiceCollection)) when the host also gates on global ASP.NET Identity roles.
public static IServiceCollection AddMembershipAuthorization(this IServiceCollection services)
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
Resolves roles from tenant-scoped membership. Pair it with the authorizing mediator, which is what enforces the attributes:
services.AddMembershipAuthorization();
services.AddAuthorizingMediator<MembershipAuthorizationProvider>();
Remarks
Hosts that dispatch through the authorizing mediator pass the provider type there instead
(AddAuthorizingMediator<MembershipAuthorizationProvider>()); this registration
serves components that resolve IAuthorizationProvider directly, such as the
authorizing outbox dispatcher.
Exceptions
- InvalidOperationException
No membership authorization options are registered yet and the host was already built from
services, which is read-only.
AddMembershipAuthorization(IServiceCollection, Action<MembershipAuthorizationOptions>?)
Register MembershipAuthorizationProvider as the IAuthorizationProvider, naming the roles that resolve in the actor's own tenant.
public static IServiceCollection AddMembershipAuthorization(this IServiceCollection services, Action<MembershipAuthorizationOptions>? configure)
Parameters
servicesIServiceCollectionThe service collection to mutate.
configureAction<MembershipAuthorizationOptions>Callback to name the roles resolved in the actor's own tenant.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
A service that holds one machine key and names the tenant per request: the key's role is held in the tenant the key was issued for, and the request concerns another.
services.AddMembershipAuthorization(o => o.HomeTenantRoles.Add("Service"));
services.AddAuthorizingMediator<MembershipAuthorizationProvider>();
Exceptions
- InvalidOperationException
The host was already built from
services, which is read-only: the options the running host reads would change under it.
AddMembershipAuthorizationOptions(IServiceCollection, Action<MembershipAuthorizationOptions>?)
Register the membership authorization options alone — for a host that passes the provider to
AddAuthorizingMediator<TProvider>() rather than registering it here.
public static IServiceCollection AddMembershipAuthorizationOptions(this IServiceCollection services, Action<MembershipAuthorizationOptions>? configure)
Parameters
servicesIServiceCollectionThe service collection to mutate.
configureAction<MembershipAuthorizationOptions>Callback to name the roles resolved in the actor's own tenant.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
Binds no configuration key — the roles are named in code. The provider itself is registered by the authorizing mediator here, so only the options come from this call:
services.AddMembershipAuthorizationOptions(o => o.HomeTenantRoles.Add("Service"));
services.AddAuthorizingMediator<MembershipAuthorizationProvider>();
Remarks
The options are registered once and every call configures that one instance, so neither the order of the calls nor their number loses a role: a module naming one role and a module naming another end up with both, and the parameterless registration adds nothing.
Exceptions
- InvalidOperationException
The host was already built from
services, which is read-only, andconfigurewould change the options the running host reads — or no options are registered yet.
AddMembershipAuthorization<TUser>(IServiceCollection)
Register MembershipAuthorizationProvider<TUser> as the
IAuthorizationProvider — role checks pass on tenant-scoped membership roles
or global ASP.NET Identity roles (platform roles). Requires the host's Identity
registration (UserManager<TUser> resolvable).
public static IServiceCollection AddMembershipAuthorization<TUser>(this IServiceCollection services) where TUser : class
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TUserThe host's ASP.NET Identity user entity.
Examples
The generic overload adds the user's global ASP.NET Core Identity roles to the membership roles:
services.AddMembershipAuthorization<ApplicationUser>();
Exceptions
- InvalidOperationException
No membership authorization options are registered yet and the host was already built from
services, which is read-only.
AddMembershipAuthorization<TUser>(IServiceCollection, Action<MembershipAuthorizationOptions>?)
Register MembershipAuthorizationProvider<TUser> as the IAuthorizationProvider, naming the roles that resolve in the actor's own tenant.
public static IServiceCollection AddMembershipAuthorization<TUser>(this IServiceCollection services, Action<MembershipAuthorizationOptions>? configure) where TUser : class
Parameters
servicesIServiceCollectionThe service collection to mutate.
configureAction<MembershipAuthorizationOptions>Callback to name the roles resolved in the actor's own tenant.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TUserThe host's ASP.NET Identity user entity.
Exceptions
- InvalidOperationException
The host was already built from
services, which is read-only: the options the running host reads would change under it.
AddMembershipCrossTenantAuthorizer(IServiceCollection, Action<MembershipCrossTenantAuthorizerOptions>?)
Register MembershipCrossTenantAuthorizer as the ICrossTenantAuthorizer consulted by strict tenant isolation: cross-tenant operations are allowed for actors with an active membership in the data-owner tenant or with one of the configured cross-tenant roles.
public static IServiceCollection AddMembershipCrossTenantAuthorizer(this IServiceCollection services, Action<MembershipCrossTenantAuthorizerOptions>? configure = null)
Parameters
servicesIServiceCollectionThe service collection to mutate.
configureAction<MembershipCrossTenantAuthorizerOptions>Optional callback to configure the permitted cross-tenant roles.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
Strict isolation where only platform administrators may cross tenants without membership:
builder.Services
.AddStrataraTenantIsolation(o => o.Mode = TenantIsolationMode.Strict)
.AddMembershipCrossTenantAuthorizer(o => o.CrossTenantRoles.Add("PlatformAdmin"));
AddPermissionCatalog(IServiceCollection, Action<PermissionCatalog>)
Build and register the application's PermissionCatalog (singleton) — the declared permission vocabulary plus its role→permission grants, consumed by the catalog permission resolvers and the HTTP permission policies.
public static IServiceCollection AddPermissionCatalog(this IServiceCollection services, Action<PermissionCatalog> configure)
Parameters
servicesIServiceCollectionThe service collection to mutate.
configureAction<PermissionCatalog>Callback that declares permissions and role grants.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
The catalog is the vocabulary: granting a permission it does not declare throws at start-up rather than silently granting nothing:
services.AddPermissionCatalog(c =>
{
c.Add("sims.read", "sims.write");
c.GrantToRole("TenantAdmin", "sims.read", "sims.write");
});
Remarks
The catalog may be declared in parts, one call per module: every call adds to the catalog registered by the calls before it, a redeclared permission has no effect, and grants to one role accumulate across parts. A grant is checked against the permissions declared so far, so a part may grant a permission an earlier part declared.
Exceptions
- InvalidOperationException
A PermissionCatalog is already registered, but as a factory or a type rather than as an instance, so there is nothing this call can add to — or the host was already built from
services, which is read-only, and the catalog it reads would change under it.
AddSettingCatalog(IServiceCollection, Action<SettingCatalog>)
Build and register the application's SettingCatalog (singleton) — the declared setting vocabulary with defaults and flags, consumed by the setting provider and the encrypting store decorator.
public static IServiceCollection AddSettingCatalog(this IServiceCollection services, Action<SettingCatalog> configure)
Parameters
servicesIServiceCollectionThe service collection to mutate.
configureAction<SettingCatalog>Callback that declares the settings.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Examples
Declares each setting with its default, whether it inherits down the scope chain, and whether it is stored encrypted:
services.AddSettingCatalog(c => c.Add(
new SettingDefinition("ui.theme", DefaultValue: "light"),
new SettingDefinition("billing.vatId", IsInherited: false)));
Remarks
The catalog may be declared in parts, one call per module: every call adds to the catalog registered by the calls before it and by the setting store, whichever ran first. A name declared in two parts fails as a name declared twice in one part does.
Exceptions
- InvalidOperationException
A SettingCatalog is already registered, but as a factory or a type rather than as an instance, so there is nothing this call can add to — or the host was already built from
services, which is read-only, and the catalog it reads would change under it.
AddSettingStoreFromContextFactory<TContext>(IServiceCollection)
Register the EF Core ISettingStore against TContext and the
scope-fallback ISettingProvider read facade, taking a fresh context for each
operation from the registered context factory. The encrypting decorator is applied exactly as
it is for the shared registration.
public static IServiceCollection AddSettingStoreFromContextFactory<TContext>(this IServiceCollection services) where TContext : DbContext
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TContextThe DbContext hosting the directory tables.
Examples
Needs AddDbContextFactory<TContext>(); one context per operation:
services.AddDbContextFactory<DirectoryDbContext>();
services.AddSettingStoreFromContextFactory<DirectoryDbContext>();
Remarks
Each operation creates its own context from the registered IDbContextFactory<TContext> and disposes it afterwards, so directory operations issued concurrently within one scope do not contend and a store's commit reaches only its own rows. In exchange, a store write does not take part in a transaction the consumer opened on their own scoped context.
Requires AddDbContextFactory<TContext>(); a missing registration fails at first
resolution rather than silently. Keep the factory's options aligned with the scoped
registration — interceptors, query filters and conventions are configured per registration,
not per context type.
Calling this together with AddSettingStore<TContext>(IServiceCollection) leaves whichever ran first in place; they do not compose.
Declaring settings is optional, exactly as for AddSettingStore<TContext>(IServiceCollection).
AddSettingStore<TContext>(IServiceCollection)
Register the EF Core ISettingStore against
TContext, plus the scope-fallback
ISettingProvider read facade. When the
registered SettingCatalog declares encrypted
settings, the store is wrapped in a transparent AES-GCM decorator (requires the security
plane's ISecureBlobEncryptor registration —
missing it fails at first resolution, not silently).
public static IServiceCollection AddSettingStore<TContext>(this IServiceCollection services) where TContext : DbContext
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TContextThe DbContext hosting the directory tables.
Examples
Registers the store and the provider facade that walks the fallback chain. Declare the vocabulary — a setting the catalog does not know has no default to fall back to:
services.AddSettingCatalog(c => c.Add(new SettingDefinition("ui.theme", DefaultValue: "light")));
services.AddSettingStore<DirectoryDbContext>();
Remarks
The store shares the context registered for the request, so directory operations issued concurrently within one scope fail on whichever arrives second — a database context serves one operation at a time — and the store's own commit also commits whatever else the consumer has left unsaved on that context. Register the store with AddSettingStoreFromContextFactory<TContext>(IServiceCollection) instead to give each operation its own context.
Calling both registrations leaves whichever ran first in place; they do not compose.
Declaring settings is optional. Without AddSettingCatalog(IServiceCollection, Action<SettingCatalog>) the store registers an empty catalog: the store works, the removal of a subject's settings included, and reading any setting through ISettingProvider fails as undeclared. AddSettingCatalog(IServiceCollection, Action<SettingCatalog>) may run before or after this call.
AddTenantMembershipStoreFromContextFactory<TContext>(IServiceCollection)
Register the EF Core ITenantMembershipStore against TContext,
taking a fresh context for each operation from the registered context factory.
public static IServiceCollection AddTenantMembershipStoreFromContextFactory<TContext>(this IServiceCollection services) where TContext : DbContext
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TContextThe DbContext hosting the directory tables.
Examples
Needs AddDbContextFactory<TContext>(). Each operation takes a fresh context, so
directory work issued concurrently in one scope no longer collides — in exchange, a store write
does not join a transaction opened on your own scoped context:
services.AddDbContextFactory<DirectoryDbContext>();
services.AddTenantMembershipStoreFromContextFactory<DirectoryDbContext>();
Remarks
Each operation creates its own context from the registered IDbContextFactory<TContext> and disposes it afterwards, so directory operations issued concurrently within one scope do not contend and a store's commit reaches only its own rows. In exchange, a store write does not take part in a transaction the consumer opened on their own scoped context.
Requires AddDbContextFactory<TContext>(); a missing registration fails at first
resolution rather than silently. Keep the factory's options aligned with the scoped
registration — interceptors, query filters and conventions are configured per registration,
not per context type.
Calling this together with AddTenantMembershipStore<TContext>(IServiceCollection) leaves whichever ran first in place; they do not compose.
AddTenantMembershipStore<TContext>(IServiceCollection)
Register the EF Core ITenantMembershipStore against TContext.
The context's model must include the identity-directory tables — derive from
IdentityDirectoryDbContext<TContext> or call
ApplyIdentityDirectoryModel(ModelBuilder) in the
context's OnModelCreating. The context itself must already be registered
(for example via AddDbContext or a pooled factory).
public static IServiceCollection AddTenantMembershipStore<TContext>(this IServiceCollection services) where TContext : DbContext
Parameters
servicesIServiceCollectionThe service collection to mutate.
Returns
- IServiceCollection
The same service collection, to enable chaining.
Type Parameters
TContextThe DbContext hosting the directory tables.
Examples
TContext is any context whose model includes the directory tables — derive from
IdentityDirectoryDbContext<TContext> or call ApplyIdentityDirectoryModel() in
OnModelCreating. Every directory store in a request shares this one context:
services.AddTenantMembershipStore<DirectoryDbContext>();
Remarks
The store shares the context registered for the request, so directory operations issued concurrently within one scope fail on whichever arrives second — a database context serves one operation at a time — and the store's own commit also commits whatever else the consumer has left unsaved on that context. Register the store with AddTenantMembershipStoreFromContextFactory<TContext>(IServiceCollection) instead to give each operation its own context.
Calling both registrations leaves whichever ran first in place; they do not compose.