Identityserver Hosting Setup

作者 DuendeSoftwarefb32edc51982无许可证9 个星标收录于 2026年10月8日更新于 2026年10月8日仓库4周前更新

Setting up and hosting Duende IdentityServer in ASP.NET Core applications, including DI registration, middleware pipeline, hosting patterns, essential options, license configuration, and ASP.NET Identity integration.

AI 生成的概览

指导在 ASP.NET Core 中设置和托管 Duende IdentityServer,涵盖依赖注入、中间件、选项、许可证和 Identity 集成。

功能
该技能提供在 ASP.NET Core 应用程序中设置和托管 Duende IdentityServer 的分步说明。内容涵盖依赖注入中的服务注册、请求管道顺序、必要的 IdentityServerOptions、许可证密钥配置、ASP.NET Identity 集成,以及转发标头和数据保护等生产环境注意事项。它产出的是配置指导和代码片段,而非可运行脚本。
适用场景
适用于新建 Duende IdentityServer 项目、配置其中间件和依赖注入、在独立托管与共享托管之间做选择,或集成 ASP.NET Identity 进行用户管理时。在配置位于代理或负载均衡器之后的生产托管时也适用。
运行要求
需要一个 ASP.NET Core 项目以及 Duende IdentityServer 的 NuGet 包,包括 Duende.Templates,以及可选的 Duende.IdentityServer.AspNetIdentity。生产使用需要有效的 Duende 许可证密钥。仅为说明文档,不包含脚本。

Setting Up and Hosting IdentityServer

When to Use This Skill

  • Setting up a new Duende IdentityServer project from scratch
  • Configuring the ASP.NET Core DI system and middleware pipeline for IdentityServer
  • Deciding between separate vs shared hosting patterns
  • Integrating IdentityServer with ASP.NET Identity for user management
  • Configuring IdentityServerOptions (issuer, key management, endpoints)
  • Setting up proxy/load balancer forwarded headers
  • Configuring data protection for production deployments
  • Understanding the IdentityServer middleware pipeline ordering

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

Core Concepts

Duende IdentityServer is middleware that adds OpenID Connect and OAuth 2.0 endpoints to an ASP.NET Core host. It requires two setup steps: registering services in DI and adding middleware to the request pipeline.

Architecture Decision: Separate vs Shared Host

IdentityServer should be in its own dedicated application to minimize the attack surface. While it is technically possible to co-host IdentityServer with clients or APIs, this is not recommended.

Hosting PatternProsCons
Separate host (recommended)Minimal attack surface, independent scaling, clear security boundaryAdditional deployment artifact
Shared with web appFewer projectsLarger attack surface, coupled deployments
Shared with APIFewer projectsSecurity risk, conflicting middleware needs

Step 1: Install Templates and Create a Project

bash
dotnet new install Duende.Templatesdotnet new duende-is-empty -n IdentityServer

The duende-is-empty template creates a minimal project with the IdentityServer NuGet package installed and basic configuration.

Step 2: Register IdentityServer Services (DI)

Call AddIdentityServer on the service collection to register all necessary services. This method also calls AddAuthentication internally.

csharp
// Program.csvar idsvrBuilder = builder.Services.AddIdentityServer(options =>{    // Configure IdentityServerOptions here});

Adding Configuration Stores

The builder object returned by AddIdentityServer provides extension methods to add configuration stores for clients, resources, and scopes:

csharp
// Program.csvar idsvrBuilder = builder.Services.AddIdentityServer()    .AddInMemoryClients(Config.Clients)    .AddInMemoryIdentityResources(Config.IdentityResources)    .AddInMemoryApiScopes(Config.ApiScopes);

Store options:

  • In-memory stores - good for development, demos, and static configuration
  • EntityFramework stores - production-ready, supports dynamic configuration
  • Custom stores - implement the store interfaces for any backing store

Minimal Working Example

csharp
// Program.csbuilder.Services.AddIdentityServer()    .AddInMemoryApiScopes(Config.ApiScopes)    .AddInMemoryClients(Config.Clients);
var app = builder.Build();
app.UseStaticFiles();app.UseRouting();app.UseIdentityServer();app.UseAuthorization();app.MapDefaultControllerRoute();
app.Run();

Step 3: Configure the Request Pipeline

Add UseIdentityServer middleware to the pipeline. Pipeline ordering is critical.

csharp
// Program.csvar app = builder.Build();app.UseStaticFiles();
app.UseRouting();app.UseIdentityServer();app.UseAuthorization();
app.MapDefaultControllerRoute();

Pipeline Ordering Rules

OrderMiddlewareNotes
1UseStaticFiles()Before IdentityServer
2UseRouting()Before IdentityServer
3UseIdentityServer()Includes UseAuthentication() internally
4UseAuthorization()Required after IdentityServer, must not be omitted
5MapDefaultControllerRoute()UI framework endpoints

Common Pipeline Anti-Patterns

csharp
// ❌ WRONG: UseAuthentication is redundant (UseIdentityServer includes it)app.UseAuthentication();app.UseIdentityServer();
// ✅ CORRECT: UseIdentityServer already calls UseAuthenticationapp.UseIdentityServer();app.UseAuthorization();
csharp
// ❌ WRONG: Missing UseAuthorization - required for the Duende UI templateapp.UseIdentityServer();app.MapDefaultControllerRoute();
// ✅ CORRECT: Always include UseAuthorization after UseIdentityServerapp.UseIdentityServer();app.UseAuthorization();app.MapDefaultControllerRoute();
csharp
// ❌ WRONG: IdentityServer before routingapp.UseIdentityServer();app.UseRouting();
// ✅ CORRECT: Routing before IdentityServerapp.UseRouting();app.UseIdentityServer();

Step 4: Configure Essential IdentityServerOptions

csharp
// Program.csvar idsvrBuilder = builder.Services.AddIdentityServer(options =>{    // IssuerUri: Not recommended to set; inferred from request URL by default.    // Set only when IdentityServer is accessed on a different address than the    // expected issuer (e.g., internal Kubernetes address).    // options.IssuerUri = "https://identity.example.com";
    // Emit scopes as space-delimited string per RFC 9068    options.EmitScopesAsSpaceDelimitedStringInJwt = false; // default, array format
    // Emit static audience claim in format {issuer}/resources    options.EmitStaticAudienceClaim = false; // default
    // Emit iss response parameter on authorize responses (RFC 9207)    options.EmitIssuerIdentificationResponseParameter = true; // default});

Key Configuration Properties

PropertyDefaultPurpose
IssuerUriinferred from URLToken issuer name in discovery and tokens
LowerCaseIssuerUritrueLowercase inferred issuer URIs
AccessTokenJwtType"at+jwt"typ header in JWT access tokens (RFC 9068)
EmitScopesAsSpaceDelimitedStringInJwtfalseScope claim format in JWTs
EmitStaticAudienceClaimfalseStatic aud claim in {issuer}/resources format
EmitIssuerIdentificationResponseParametertrueiss param on authorize responses (RFC 9207)

Step 5: Configure the License Key

Duende IdentityServer requires a valid license for production use. Without a license key, IdentityServer runs in trial/community mode and will log a warning on startup.

Set the license key via options.LicenseKey or via configuration:

csharp
// Option 1: Inline in AddIdentityServer (not recommended for production — keep out of source control)builder.Services.AddIdentityServer(options =>{    options.LicenseKey = "YOUR_LICENSE_KEY";});
// Option 2: From configuration (recommended)builder.Services.AddIdentityServer(options =>{    options.LicenseKey = builder.Configuration["IdentityServer:LicenseKey"];});

Store the key in a secret manager, environment variable, or key vault — never in source-controlled appsettings.json.

Step 6: ASP.NET Identity Integration

To use ASP.NET Identity as the user store for IdentityServer, install the integration package and configure both systems:

bash
dotnet add package Duende.IdentityServer.AspNetIdentity
csharp
// Program.csbuilder.Services.AddIdentity<ApplicationUser, IdentityRole>()    .AddEntityFrameworkStores<ApplicationDbContext>()    .AddDefaultTokenProviders();
builder.Services.AddIdentityServer()    .AddAspNetIdentity<ApplicationUser>();

What AddAspNetIdentity Configures

AddAspNetIdentity<TUser> registers the following IdentityServer implementations:

  • IProfileService - uses IUserClaimsPrincipalFactory to add claims to tokens
  • IResourceOwnerPasswordValidator - supports the password grant type
  • IUserClaimsPrincipalFactory - a wrapper implementation that calls through to the previously registered factory and adds extra IdentityServer-specific claims

Custom IUserClaimsPrincipalFactory

If you register a custom IUserClaimsPrincipalFactory before calling AddAspNetIdentity, the IdentityServer registration will resolve your factory and call through to it, layering additional claims on top:

csharp
// Program.cs
// Register custom factory BEFORE AddAspNetIdentitybuilder.Services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomClaimsPrincipalFactory>();
builder.Services.AddIdentityServer()    .AddAspNetIdentity<ApplicationUser>();

Inactive User Handling

ASP.NET Identity has no built-in concept of inactive users. The default IsActiveAsync implementation returns true. To support enable/disable functionality:

csharp
public class CustomProfileService : ProfileService<ApplicationUser>{    public CustomProfileService(        UserManager<ApplicationUser> userManager,        IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)        : base(userManager, claimsFactory)    { }
    protected override Task<bool> IsUserActiveAsync(ApplicationUser user)    {        return Task.FromResult(user.IsEnabled); // your custom property    }}

Template Alternative

Use the duende-is-aspid template for a pre-configured ASP.NET Identity integration:

bash
dotnet new duende-is-aspid -n IdentityServer

Production Deployment: Proxy and Load Balancer Configuration

When behind a reverse proxy or load balancer, the proxy obscures request scheme and IP address. This causes common symptoms:

  • HTTPS downgraded to HTTP in discovery document
  • Incorrect host names in discovery or redirects
  • Cookies missing the secure attribute

Solution: Forwarded Headers Middleware

Option 1: Environment variable (simple) Set ASPNETCORE_FORWARDEDHEADERS_ENABLED=true for cloud/Kubernetes environments.

Option 2: Explicit configuration (production)

csharp
// Program.csbuilder.Services.Configure<ForwardedHeadersOptions>(options =>{    options.ForwardedHeaders = ForwardedHeaders.XForwardedHost |                                ForwardedHeaders.XForwardedProto;
    options.KnownProxies.Add(IPAddress.Parse("203.0.113.42"));    options.ForwardLimit = 1;});

Add UseForwardedHeaders() early in the pipeline, before UseIdentityServer().

Production Deployment: Data Protection

Data protection is critical for IdentityServer. It protects signing keys at rest, persisted grants, server-side sessions, and authentication cookies. See ASP.NET Core Data Protection for comprehensive guidance covering all Duende SDKs.

csharp
// Program.csbuilder.Services.AddDataProtection()    .PersistKeysToFoo()       // Choose persistence (FileSystem, DbContext, Azure, Redis, etc.)    .ProtectKeysWithBar()     // Choose key protection (Certificate, Azure Key Vault, etc.)    .SetApplicationName("My.IdentityServer"); // Prevent key isolation issues

Data Protection Checklist

RequirementWhy
Persist keys to durable storageKeys are lost on restart without persistence
Share keys across load-balanced instancesEach instance must read data protected by other instances
Set explicit application namePrevents key isolation across deployments
Ensure storage durabilityRedis without persistence or ephemeral filesystems lose keys

Data Protection Keys vs Signing Keys

These are completely separate:

Data Protection KeysIdentityServer Signing Keys
PurposeEncrypt/sign sensitive data (cookies, grants)Sign tokens (JWT, id_token)
CryptographySymmetric (private key)Asymmetric (public/private key pair)
FrameworkASP.NET Core Data ProtectionIdentityServer Key Management
PublicNoPublic keys published in discovery

Common Pitfalls

  1. Missing UseAuthorization() - The Duende UI template requires authorization middleware. Omitting it causes authorization failures in the UI pages.

  2. Redundant UseAuthentication() - UseIdentityServer() already includes UseAuthentication(). Adding both is unnecessary but not harmful.

  3. Data protection not configured for production - The default file-based key storage does not survive container restarts or work across load-balanced instances. Always configure persistent, shared key storage.

  4. Issuer mismatch - If IssuerUri is set manually, clients must know this exact value. Prefer letting IdentityServer infer the issuer from request URLs.

  5. Keys directory in source control - The ~/keys directory created by automatic key management contains cryptographic secrets and must be excluded from source control via .gitignore.

  6. Shared hosting with APIs/clients - Co-hosting IdentityServer with other applications increases the attack surface. Use a dedicated host.

  7. Not calling AddAspNetIdentity after AddIdentity - When using ASP.NET Identity, you must call both. AddIdentity configures ASP.NET Identity; AddAspNetIdentity bridges it to IdentityServer.


Related Skills

  • identityserver-configuration — client definitions, resources, scopes
  • identityserver-deployment — production deployment, data protection, health checks
  • identityserver-aspire — orchestrating IdentityServer in Aspire AppHost

来源与署名

来源:DuendeSoftware/duende-skills位于skills/identityserver-hosting-setup提交fb32edc

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架