---
name: identityserver-usermanagement
description: "Setting up Duende User Management with IdentityServer: passwordless authentication (OTP, TOTP, passkeys), storage configuration, user lifecycle, and migration from ASP.NET Identity."
invocable: false
---

# User Management

## When to Use This Skill

- Adding user management to a Duende IdentityServer project
- Setting up passwordless authentication (OTP, TOTP, passkeys)
- Configuring storage providers (PostgreSQL, SQL Server, SQLite)
- Integrating User Management with IdentityServer for claims and login/logout
- Managing user profiles, roles, and groups
- Migrating users from ASP.NET Identity

## Core Principles

- Duende User Management is **passwordless-first** — OTP email/SMS is the default flow
- Requires `Duende.UserManagement.IdentityServer8` NuGet package + .NET 10
- Storage is **document-based** (no EF migrations needed) — schema auto-creates at startup
- Configuration goes **inside** `AddUserManagement()`, not at top level
- Use `app.UseIdentityServer()` (not `UseAuthentication()` separately)

Docs: https://docs.duendesoftware.com/identityserver/usermanagement

## Setup

### 1. Add Packages

```bash
dotnet add package Duende.IdentityServer
dotnet add package Duende.UserManagement.IdentityServer8
dotnet add package Duende.Storage.Sqlite  # or .PostgreSQL, .Mssql
```

### 2. Configure Program.cs

```csharp
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();
```

### 3. OTP Dispatcher

**Console (development):**
```csharp
builder.Services.AddSingleton<IOtpDispatcher, ConsoleOtpDispatcher>();
```

**SMTP (production):**
```csharp
options.UseSmtpOtpDispatcher(x =>
{
    x.Host = "smtp.example.com";
    x.Port = 587;
    x.Username = "noreply@example.com";
    x.Password = "secret";
    x.FromAddress = "noreply@example.com";
});
```

## Authentication Methods

| 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 |

## IdentityServer Integration

`AddUserManagement()` is called on the IdentityServer builder — it automatically:
- Registers `IProfileService` for claims delivery
- Handles login/logout flows
- Maps user attributes to identity token claims

### Claims Mapping

User profile attributes are mapped to claims based on requested scopes:
- `openid` → `sub`
- `profile` → `name`, `given_name`, `family_name`, etc.
- `email` → `email`, `email_verified`

Custom attributes are available through custom identity resources.

## Storage

| 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.

## User Lifecycle

- **Creation**: Users are created on first authentication (passwordless) or via admin APIs
- **Profiles**: Custom attributes stored as key-value pairs, organized in attribute groups
- **Roles & Groups**: RBAC support with group membership and role inheritance
- **Deletion**: Full user deletion with cascade

## Migration from ASP.NET Identity

```csharp
options.AddAspNetIdentityMigration(migrationOptions =>
{
    migrationOptions.ConnectionString = "existing-aspnet-identity-db";
});
```

Key points:
- Imports users, roles, and claims from existing ASP.NET Identity tables
- Password hashes are preserved (users can still log in with existing passwords)
- Migration runs once; subsequent runs skip already-imported users
- After migration, users can enroll in passwordless methods

## Common Anti-Patterns

❌ 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

## Common Pitfalls

1. **Storage configuration location**: `AddSqliteStore()`/`AddPostgreSqlStore()` must be called inside the `AddUserManagement(options => { })` lambda, not on the top-level builder.
2. **.NET 10 required**: User Management requires .NET 10 SDK or later.
3. **OTP dispatcher required**: Without an `IOtpDispatcher`, the default OTP flow cannot send codes. Register `ConsoleOtpDispatcher` for development.
4. **LoginUrl/LogoutUrl**: Must be set in IdentityServer options to point to your account pages.
5. **Schema creation**: Call `IDatabaseSchema.CreateIfNotExistsAsync()` before the app starts handling requests.

## Related Skills

- `identityserver-configuration` — IdentityServer host configuration and options
- `identityserver-ui-flows` — Login/logout UI flows
- `identityserver-upgrade-v7-to-v8` — Migration guide for v8 (includes User Management as new feature)
- `aspnetcore-authentication` — ASP.NET Core authentication fundamentals
