{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiegdq3ywrggc7kzlwyu5bnkrqse5wjixu6x27rooat47l6gbm7vmq",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mp2oq4ak7ah2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreidimkmqjzquckyo3ei2gdyqroazjggx5quirzbmux63aqikhzqc7i"
    },
    "mimeType": "image/webp",
    "size": 70560
  },
  "path": "/michael_jordan_87eaf96f24/mongodb-backed-aspnet-core-identity-without-the-ef-core-detour-848",
  "publishedAt": "2026-06-24T19:42:41.000Z",
  "site": "https://dev.to",
  "tags": [
    "dotnet",
    "csharp",
    "mongodb",
    "webdev",
    "https://www.nuget.org/packages/AspNetCoreIdentity.MongoDriver",
    "https://github.com/lxman/AspNetCoreIdentity.MongoDriver"
  ],
  "textContent": "If you're already running MongoDB and you reach for ASP.NET Core Identity, the\nofficial story points you at Entity Framework Core. That's a fine answer if you\nhave a relational database. If you don't, you end up bolting a second data\naccess stack onto an app that has exactly one store. You don't need it.\n\n`AspNetCoreIdentity.MongoDriver` is a store provider that talks to Mongo through\nthe official `MongoDB.Driver` directly — no EF Core, no SQL, no second\npersistence model. It implements the Identity store interfaces, ships a\n`UserStore` and `RoleStore`, and wires up `UserManager`/`RoleManager` the way\nyou already expect.\n\n##  The 15-line version\n\nRegister the provider in `Program.cs`:\n\n\n\n    builder.Services.AddIdentityMongoDbProvider<MongoUser<Guid>, MongoRole<Guid>, Guid>(identity =>\n    {\n        identity.User.RequireUniqueEmail = true;\n    }, mongo =>\n    {\n        mongo.ConnectionString = builder.Configuration.GetConnectionString(\"MongoDb\")!;\n    });\n\n\nA connection string like `mongodb://localhost:27017/Identity` creates an\n`Identity` database and stores the collections there. That's the whole setup.\nNow inject the managers wherever you need them:\n\n\n\n    public class AccountController(UserManager<MongoUser<Guid>> userManager) : Controller\n    {\n        // ...\n    }\n\n\nThey're registered as scoped services — let the container manage their\nlifetime. Don't call `BuildServiceProvider()` yourself, and don't cache the\nmanagers in long-lived objects.\n\n##  Your key type is yours\n\nThe `MongoUser` and `MongoRole` classes are generic, so the primary key isn't\nforced to be a `Guid`. Want `string` keys? Use `MongoUser<string>`. If you use\n`string` keys and don't assign an `Id` on create, the store generates an\nObjectId-style string for you.\n\nIf you do go with `Guid`, register the serializer once before the code above so\nMongo stores them in the standard representation:\n\n\n\n    BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));\n\n\n##  Migrations that survive a rolling deploy\n\nHere's the part that usually bites people who hand-roll a Mongo store. On the\nfirst store operation — not during service registration — the library:\n\n  * 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\n  * 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`.\n\n\n\nThat distributed lock matters the moment you run more than one instance. Two\npods starting simultaneously won't race each other into a half-applied schema.\n\nBecause the `NormalizedUserName` index enforces real uniqueness, index creation\nwill fail if your existing data already contains duplicate user names — clean\nthose up before you upgrade. If you'd rather own these concerns yourself, set\n`mongo.DisableIndexCreation = true` and/or `mongo.DisableAutoMigrations = true`.\nAnd if you want the work done at startup instead of on first request, resolve\n`MongoIdentityInitializer` and await `EnsureInitializedAsync()`.\n\n##  It won't silently clobber concurrent writes\n\nUpdates and deletes use optimistic concurrency through Identity's\n`ConcurrencyStamp`. If the document changed since you loaded your copy, the\noperation returns a `ConcurrencyFailure` instead of overwriting the other\nwrite. Reload, reapply, retry — the normal Identity dance.\n\n##  One honest limitation\n\n`options.Stores.ProtectPersonalData` is **not** supported. The store doesn't\nencrypt personal data at rest, and rather than letting you flip that switch and\nquietly store unprotected data anyway, enabling it throws at runtime. If you\nneed encryption-at-rest for PII, handle it at a different layer.\n\n##  Try it\n\n\n    dotnet add package AspNetCoreIdentity.MongoDriver\n\n\n  * Package: https://www.nuget.org/packages/AspNetCoreIdentity.MongoDriver\n  * Source: https://github.com/lxman/AspNetCoreIdentity.MongoDriver\n\n\n\nIf you're on Mongo and Identity, this is the short path. Issues and PRs welcome.",
  "title": "MongoDB-backed ASP.NET Core Identity, without the EF Core detour"
}