ASP.NET Core Identity
Optional ASP.NET Core Identity integration that composes onto a plain DbContext — no IdentityDbContext inheritance, snake_case-ready.
Elarion's ASP.NET Core Identity support is split across two optional packages:
Elarion.EntityFrameworkCore.Identity— the web-free Identity model: the[GenerateElarionIdentity]marker and theApplyElarionIdentityhelper. It depends only on EF Core + the Identity stores package, so the project that owns yourDbContextcan model Identity without referencing the ASP.NET shared framework.Elarion.AspNetCore.Identity— the host wiring:AddElarionIdentity, the IdentityICurrentUserclaim mapping, and the transport-neutral authorizer. This one pulls in ASP.NET Core.
Identity is one authentication provider among many — authorization itself
does not depend on either package. The distinguishing trait is composition over inheritance: your
application DbContext stays a plain DbContext and composes the Identity model, rather than deriving from
IdentityDbContext.
In a layered solution, reference Elarion.EntityFrameworkCore.Identity from the persistence/application
project (where the DbContext and [GenerateElarionIdentity] live) and Elarion.AspNetCore.Identity from
the web host (where AddElarionIdentity runs). The data layer composes the Identity model with no
web-framework dependency. In a single-project host, reference both.
Wiring
// Program.cs
builder.Services.AddElarionIdentity<ApplicationUser, ApplicationRole, Guid, AppDbContext>();
builder.Services.AddDbContext<AppDbContext>(o => o.UseNpgsql(connectionString));AddElarionIdentity<TUser, TRole, TKey, TDbContext>:
- registers
AddIdentity<TUser, TRole>()+AddEntityFrameworkStores<TDbContext>()(+ default token providers), - maps
ICurrentUserto the Identity claim types (NameIdentifier/Email/Role, overridable), and - registers the transport-neutral authorizer via
AddElarionAuthorization, so handler[Require*]attributes are enforced.
Cookie/authentication policy (ConfigureApplicationCookie, login endpoints, UseAuthentication,
UseElarionCurrentUser) stays the host's job — the package wires Identity and authorization, not your sign-in UX.
The context composes Identity
AddEntityFrameworkStores only needs the Identity entities mapped — it does not require
IdentityDbContext. Annotate a plain [GenerateDbSets] context (the [GenerateElarionIdentity] marker and
its generator come from Elarion.EntityFrameworkCore.Identity, so this assembly needs no web dependency):
[GenerateDbSets]
[GenerateElarionIdentity<ApplicationUser, ApplicationRole, Guid>(SnakeCase = true)]
public sealed partial class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options) {
protected override void OnModelCreating(ModelBuilder modelBuilder) => ConfigureEntities(modelBuilder);
}
public sealed class ApplicationUser : IdentityUser<Guid>;
public sealed class ApplicationRole : IdentityRole<Guid>;The bundled generator emits the seven Identity DbSets (db.Users, db.Roles, db.UserClaims, …) and
applies the full Identity model configuration — keys, composite keys, unique normalized indexes,
relationships, max-lengths — through the EF generator's neutral OnEntitiesConfigured seam. So the single
ConfigureEntities(modelBuilder) call configures both your domain entities (from [EntityConfiguration])
and Identity, with no <TUser, TRole, TKey> repeated at the call site and no IdentityDbContext in the
hierarchy. (Requires [GenerateDbSets] on the same context, else ELIDN001.)
snake_case
SnakeCase = true (the default) makes the generated Identity model use snake_case table, column, and
index names directly — users, roles, user_claims, normalized_user_name,
ix_users_normalized_user_name, … — instead of the ASP.NET AspNet* PascalCase defaults. It is
self-contained: no EFCore.NamingConventions dependency, and the names match what that convention would
produce, so an app already using it for domain tables sees a consistent schema. Set SnakeCase = false to
keep the Identity defaults. The underlying primitive, modelBuilder.ApplyElarionIdentity<TUser, TRole, TKey>(snakeCase)
(also in Elarion.EntityFrameworkCore.Identity), is public for advanced hosts that prefer to call it directly.
What stays in your app
The package is deliberately minimal. Role→permission policy, seeding, sign-in/registration endpoints, and the
user-management surface stay app-owned — they are domain decisions, not framework mechanics. Permissions are
typically issued as a permission claim (the default
AuthorizationOptions.PermissionClaimType) on roles, and enforced on handlers
with [RequirePermission]. You no longer hand-maintain the list of permissions, though: inject
IPermissionCatalog to enumerate every [RequirePermission]/[RequireRole]
across your modules when seeding.
See also
- Authorization — the attributes and the provider-neutral decision path.
- Current user — the
ICurrentUserabstraction Identity populates. - Why Elarion → Composition over inheritance.