AspNetCore.Identity.CosmosDb is a Cosmos DB-backed implementation of ASP.NET Core Identity built on Entity Framework Core Cosmos. It gives ASP.NET Core applications a non-relational Identity store with support for users, roles, claims, tokens, external logins, and passkeys.
Important
Breaking change: The repository has moved to .NET 10 and now includes first-class ASP.NET Core Identity passkey support.
See .NET 10 + Passkey Upgrade Summary for migration-impact details and change scope.
This repository is part of the SkyCMS ecosystem and contains the core package, a runnable demo site, passkey page templates, and the test suites used to validate the library.
AspNetCore.Identity.CosmosDb/: the main library and NuGet packageAspNetCore.Identity.CosmosDb.Demo/: sample ASP.NET Core app showing the package in useAspNetCore.Identity.CosmosDb.Demo.Template/:dotnet newproject template for scaffolding the complete demo appAspNetCore.Identity.Razor.PassKeyPage/:dotnet newtemplates for passkey login and passkey management pagesAspNetCore.Identity.CosmosDb.Tests/: primary automated test suiteAspNetCore.Identity.CosmosDbCompat.Tests/: API compatibility regression suite used during framework and package upgradesPASSKEY_DEVELOPER_GUIDE.md: passkey integration guidance
AspNetCore.Identity.CosmosDb: main Identity provider packageCWALabs.AspNetCore.Identity.CosmosDb.DemoTemplate: full demo appdotnet newproject templateCWALabs.AspNetCore.Identity.CosmosDb.PasskeyTemplates: Razor page templates for passkey UI integration
This repository uses a shared solution version stored in Directory.Build.props. All projects in the solution inherit that version so package, demo, and test artifacts stay aligned.
For local release preparation, use the PowerShell helpers in scripts/README.md instead of manually editing version numbers or creating tags by hand.
Both release helpers require a clean git working tree. If there are any uncommitted changes, they halt immediately before updating the version, creating a commit, or tagging a release.
New-ReleaseTag.ps1 also only runs from main by default. If you ever need to release from another branch intentionally, use -AllowNonMainBranch.
Recommended release flow:
# Interactive release flow: bump version, commit it, create tag, optionally push
.\scripts\New-ReleaseTag.ps1Useful command-line examples:
# Preview the next patch release without changing anything
.\scripts\New-ReleaseTag.ps1 -ChangeType Patch -NoPush -WhatIf
# Create and push a stable minor release
.\scripts\New-ReleaseTag.ps1 -ChangeType Minor -Push
# Create and push a release candidate tag
.\scripts\New-ReleaseTag.ps1 -ChangeType Patch -ReleaseCandidateNumber 1 -Push
# Only bump the shared repo version without tagging
.\scripts\Set-RepoVersion.ps1 -ChangeType Patch -CommitThe NuGet publish workflow validates that the pushed tag matches RepoVersion before packaging and publishing. In practice, that means:
- stable release tags should look like
v12.0.1 - release candidate tags should look like
v12.0.1-rc1 - the
12.0.1part must matchRepoVersionin Directory.Build.props
- Persists ASP.NET Core Identity data in Azure Cosmos DB through EF Core Cosmos
- Implements the standard ASP.NET Core Identity store abstractions for
UserManager<TUser>andRoleManager<TRole> - Supports generic key types
- Adds passkey / WebAuthn persistence through
IUserPasskeyStore<TUser> - Provides reusable passkey API endpoints and packaged browser-side JavaScript
- Supports older Cosmos-backed Identity databases through backward-compatibility mode
- .NET 10
- Azure Cosmos DB or the Azure Cosmos DB Emulator
- ASP.NET Core Identity
The registration is the same for MVC and Razor Pages. The only difference is whether you add AddControllersWithViews() or AddRazorPages() and how you map routes.
If you want a complete working reference before wiring this up yourself, see the demo app in AspNetCore.Identity.CosmosDb.Demo/README.md.
Install-Package AspNetCore.Identity.CosmosDbor:
dotnet add package AspNetCore.Identity.CosmosDb{
"ConnectionStrings": {
"CosmosDb": "AccountEndpoint=https://<account>.documents.azure.com:443/;AccountKey=<key>;"
},
"CosmosDb": {
"DatabaseName": "MyIdentityDb"
},
"Passkeys": {
"ServerDomain": "localhost"
}
}using AspNetCore.Identity.CosmosDb;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : CosmosIdentityDbContext<IdentityUser, IdentityRole, string>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}using AspNetCore.Identity.CosmosDb;
using AspNetCore.Identity.CosmosDb.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("CosmosDb")!;
var databaseName = builder.Configuration["CosmosDb:DatabaseName"]!;
var passkeyServerDomain = builder.Configuration["Passkeys:ServerDomain"];
if (string.IsNullOrWhiteSpace(passkeyServerDomain))
{
if (builder.Environment.IsDevelopment())
{
passkeyServerDomain = "localhost";
}
else
{
throw new InvalidOperationException("Passkeys:ServerDomain must be configured outside Development.");
}
}
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseCosmos(connectionString, databaseName));
builder.Services.Configure<IdentityPasskeyOptions>(options =>
{
options.ServerDomain = passkeyServerDomain;
options.AuthenticatorTimeout = TimeSpan.FromMinutes(3);
options.ChallengeSize = 32;
});
builder.Services
.AddCosmosIdentity<ApplicationDbContext, IdentityUser, IdentityRole, string>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 8;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedAccount = true;
}, TimeSpan.FromHours(8), slidingExpiration: true)
.AddDefaultTokenProviders()
.AddDefaultUI();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await db.Database.EnsureCreatedAsync();
}app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();The package includes passkey support for .NET 10 ASP.NET Core Identity.
builder.Services.AddCosmosPasskeyUiIntegration(options =>
{
options.RoutePrefix = "/identity/passkeys";
options.ClientScriptPath = "/identity/passkeys/client.js";
options.RequireAntiforgery = true;
options.MaxPasskeysPerUser = 100;
options.MaxPasskeyNameLength = 200;
});
app.MapCosmosPasskeyUiEndpoints<IdentityUser>();Install the templates package:
dotnet new install CWALabs.AspNetCore.Identity.CosmosDb.PasskeyTemplatesThen scaffold the pages:
dotnet new cosmos-passkeys-login --RootNamespace MyApp --UserType IdentityUser
dotnet new cosmos-passkeys-ui --RootNamespace MyAppThe templates package is provider-agnostic at the page-model level and is documented in AspNetCore.Identity.Razor.PassKeyPage/README.md.
The repository includes AspNetCore.Identity.CosmosDb.Demo, a runnable example that shows how to configure the package, provision Cosmos DB, and use the passkey integration end to end.
If you want the demo without cloning this repository, two options are available:
- Install and scaffold the full demo app template package:
dotnet new install CWALabs.AspNetCore.Identity.CosmosDb.DemoTemplate
dotnet new cosmos-identity-demo -n MyIdentityCosmosDemo- Download
AspNetCore.Identity.CosmosDb.Demo-source.zipfrom the latest GitHub Release assets (created by .github/workflows/demo-package.yml).
The demo README includes setup instructions, API usage notes, and page screenshots for the home, login, and passkey management flows: AspNetCore.Identity.CosmosDb.Demo/README.md.
Older EF Core Cosmos databases can be read by constructing the context with backwardCompatibility: true when needed.
using var dbContext = new ApplicationDbContext(optionsBuilder.Options, backwardCompatibility: true);This is primarily for databases created before EF Core Cosmos discriminator behavior changed in EF Core 9.
- AspNetCore.Identity.CosmosDb/README.md
- AspNetCore.Identity.CosmosDb.Demo/README.md
- AspNetCore.Identity.CosmosDb.Demo.Template/README.md
- AspNetCore.Identity.CosmosDb.Tests/README.md
- AspNetCore.Identity.CosmosDbCompat.Tests/README.md
- PASSKEY_DEVELOPER_GUIDE.md
- PASSKEY_IMPLEMENTATION_STATUS.md
- AspNetCore.Identity.Razor.PassKeyPage/README.md


