From duende-skills
Sets up Duende User Management with IdentityServer: passwordless auth (OTP, TOTP, passkeys), storage config, user lifecycle, and migration from ASP.NET Identity.
How this skill is triggered — by the user, by Claude, or both
Slash command
/duende-skills:identityserver-usermanagementThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Adding user management to a Duende IdentityServer project
Duende.UserManagement.IdentityServer8 NuGet package + .NET 10AddUserManagement(), not at top levelapp.UseIdentityServer() (not UseAuthentication() separately)Docs: https://docs.duendesoftware.com/identityserver/usermanagement
dotnet add package Duende.IdentityServer
dotnet add package Duende.UserManagement.IdentityServer8
dotnet add package Duende.Storage.Sqlite # or .PostgreSQL, .Mssql
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Logout";
})
.AddInMemoryClients(Config.Clients)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddUserManagement(options =>
{
// Storage (pick one)
options.AddSqliteStore("Data Source=users.db");
// options.AddPostgreSqlStore(connectionString);
// options.AddSqlServerStore(connectionString);
// OTP delivery
options.UseSmtpOtpDispatcher(smtp =>
builder.Configuration.GetSection("Smtp").Bind(smtp));
});
var app = builder.Build();
// Auto-create database schema
var schema = app.Services.GetRequiredService<IDatabaseSchema>();
await schema.CreateIfNotExistsAsync();
app.UseIdentityServer();
app.MapRazorPages();
app.Run();
Console (development):
builder.Services.AddSingleton<IOtpDispatcher, ConsoleOtpDispatcher>();
SMTP (production):
options.UseSmtpOtpDispatcher(x =>
{
x.Host = "smtp.example.com";
x.Port = 587;
x.Username = "[email protected]";
x.Password = "secret";
x.FromAddress = "[email protected]";
});
| Method | Description | Setup |
|---|---|---|
| OTP (default) | One-time codes via email/SMS | IOtpDispatcher implementation |
| TOTP | Authenticator apps (RFC 6238) | Built-in, user enrollment required |
| Passkeys | WebAuthn/FIDO2 phishing-resistant | Built-in, browser support required |
| Passwords | Traditional username/password (PBKDF2) | Opt-in, not recommended as primary |
| External | OAuth 2.0 / OIDC federated login | Standard ASP.NET Core auth handlers |
| Recovery codes | Single-use backup codes | Auto-generated during 2FA setup |
AddUserManagement() is called on the IdentityServer builder — it automatically:
IProfileService for claims deliveryUser profile attributes are mapped to claims based on requested scopes:
openid → subprofile → name, given_name, family_name, etc.email → email, email_verifiedCustom attributes are available through custom identity resources.
| Provider | Package | Connection |
|---|---|---|
| SQLite | Duende.Storage.Sqlite | Data Source=users.db |
| PostgreSQL | Duende.Storage.PostgreSQL | Standard connection string |
| SQL Server | Duende.Storage.Mssql | Standard connection string |
| In-Memory | (built-in) | Data Source=:memory: (testing only) |
Storage is document-based — no EF Core migrations needed. Call IDatabaseSchema.CreateIfNotExistsAsync() at startup to ensure schema exists.
options.AddAspNetIdentityMigration(migrationOptions =>
{
migrationOptions.ConnectionString = "existing-aspnet-identity-db";
});
Key points:
❌ Configuring storage outside AddUserManagement() — storage config must be inside the options lambda
❌ Using UseAuthentication() instead of UseIdentityServer() — IdentityServer middleware handles auth
❌ Skipping CreateIfNotExistsAsync() — database tables won't exist on first run
❌ Using in-memory storage in production — data is lost on restart
AddSqliteStore()/AddPostgreSqlStore() must be called inside the AddUserManagement(options => { }) lambda, not on the top-level builder.IOtpDispatcher, the default OTP flow cannot send codes. Register ConsoleOtpDispatcher for development.IDatabaseSchema.CreateIfNotExistsAsync() before the app starts handling requests.identityserver-configuration — IdentityServer host configuration and optionsidentityserver-ui-flows — Login/logout UI flowsidentityserver-upgrade-v7-to-v8 — Migration guide for v8 (includes User Management as new feature)aspnetcore-authentication — ASP.NET Core authentication fundamentalsnpx claudepluginhub duendesoftware/duende-skills --plugin duende-skillsProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.