External Publication
Visit Post

MongoDB-backed ASP.NET Core Identity, without the EF Core detour

DEV Community [Unofficial] June 24, 2026
Source

If you're already running MongoDB and you reach for ASP.NET Core Identity, the official story points you at Entity Framework Core. That's a fine answer if you have a relational database. If you don't, you end up bolting a second data access stack onto an app that has exactly one store. You don't need it.

AspNetCoreIdentity.MongoDriver is a store provider that talks to Mongo through the official MongoDB.Driver directly — no EF Core, no SQL, no second persistence model. It implements the Identity store interfaces, ships a UserStore and RoleStore, and wires up UserManager/RoleManager the way you already expect.

The 15-line version

Register the provider in Program.cs:

builder.Services.AddIdentityMongoDbProvider<MongoUser<Guid>, MongoRole<Guid>, Guid>(identity =>
{
    identity.User.RequireUniqueEmail = true;
}, mongo =>
{
    mongo.ConnectionString = builder.Configuration.GetConnectionString("MongoDb")!;
});

A connection string like mongodb://localhost:27017/Identity creates an Identity database and stores the collections there. That's the whole setup. Now inject the managers wherever you need them:

public class AccountController(UserManager<MongoUser<Guid>> userManager) : Controller
{
    // ...
}

They're registered as scoped services — let the container manage their lifetime. Don't call BuildServiceProvider() yourself, and don't cache the managers in long-lived objects.

Your key type is yours

The MongoUser and MongoRole classes are generic, so the primary key isn't forced to be a Guid. Want string keys? Use MongoUser<string>. If you use string keys and don't assign an Id on create, the store generates an ObjectId-style string for you.

If you do go with Guid, register the serializer once before the code above so Mongo stores them in the standard representation:

BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));

Migrations that survive a rolling deploy

Here's the part that usually bites people who hand-roll a Mongo store. On the first store operation — not during service registration — the library:

  • applies any pending schema migrations, guarded by a distributed lock so that if several app instances boot at once, the migrations apply exactly once; and
  • creates its indexes: a unique index on NormalizedUserName, an index on NormalizedEmail, a compound index on Logins.LoginProvider / Logins.ProviderKey, and a unique index on the role NormalizedName.

That distributed lock matters the moment you run more than one instance. Two pods starting simultaneously won't race each other into a half-applied schema.

Because the NormalizedUserName index enforces real uniqueness, index creation will fail if your existing data already contains duplicate user names — clean those up before you upgrade. If you'd rather own these concerns yourself, set mongo.DisableIndexCreation = true and/or mongo.DisableAutoMigrations = true. And if you want the work done at startup instead of on first request, resolve MongoIdentityInitializer and await EnsureInitializedAsync().

It won't silently clobber concurrent writes

Updates and deletes use optimistic concurrency through Identity's ConcurrencyStamp. If the document changed since you loaded your copy, the operation returns a ConcurrencyFailure instead of overwriting the other write. Reload, reapply, retry — the normal Identity dance.

One honest limitation

options.Stores.ProtectPersonalData is not supported. The store doesn't encrypt personal data at rest, and rather than letting you flip that switch and quietly store unprotected data anyway, enabling it throws at runtime. If you need encryption-at-rest for PII, handle it at a different layer.

Try it

dotnet add package AspNetCoreIdentity.MongoDriver

If you're on Mongo and Identity, this is the short path. Issues and PRs welcome.

Discussion in the ATmosphere

Loading comments...