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 從公開儲存庫中收錄這些內容。

檢舉或申請下架