Skip to main content
Table of Contents

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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TContext

The 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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TContext

The 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

services IServiceCollection

The 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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TUser

The 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

services IServiceCollection

The 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

services IServiceCollection

The service collection to mutate.

configure Action<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

services IServiceCollection

The service collection to mutate.

configure Action<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, and configure would 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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TUser

The 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

services IServiceCollection

The service collection to mutate.

configure Action<MembershipAuthorizationOptions>

Callback to name the roles resolved in the actor's own tenant.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TUser

The 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

services IServiceCollection

The service collection to mutate.

configure Action<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

services IServiceCollection

The service collection to mutate.

configure Action<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

services IServiceCollection

The service collection to mutate.

configure Action<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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TContext

The 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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TContext

The 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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TContext

The 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

services IServiceCollection

The service collection to mutate.

Returns

IServiceCollection

The same service collection, to enable chaining.

Type Parameters

TContext

The 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.