Identityserver Upgrade V7 To V8

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

Migrating Duende IdentityServer from v7.4 to v8.0: breaking changes, API replacements (ICache→HybridCache, IClock→TimeProvider), CancellationToken additions, EF migrations, and step-by-step upgrade guide.

AI 生成的概览

指导将 Duende IdentityServer 从 v7.4 升级到 v8.0,涵盖破坏性 API 变更、EF 迁移与许可。

功能
该技能提供将 Duende IdentityServer 项目从 v7.4 迁移到 v8.0 的分步指南。它列出破坏性变更与 API 替换,例如 ICache 改为 HybridCache、IClock 改为 TimeProvider、新增 CancellationToken 参数、类型重命名与选项迁移,以及所需的 EF Core 数据库迁移和许可变更。它还包含迁移检查清单和常见陷阱。
适用场景
适用于将 Duende IdentityServer 项目从 v7.4 升级到 v8.0、在将 NuGet 包更新到 v8 后修复编译错误,或将自定义存储与服务迁移到新的 v8 接口。也适用于为 v8 运行 EF Core 迁移或替换已弃用 API 的场景。
运行要求
仅为说明文档,不附带脚本。前提是使用 Duende IdentityServer 的 .NET 项目、.NET 10 SDK、用于迁移的 EF Core 工具,以及访问 NuGet 以查询最新的稳定 8.x 包版本。

Upgrading IdentityServer v7 to v8

When to Use This Skill

  • Upgrading a Duende IdentityServer project from v7.4 to v8.0
  • Fixing build errors after updating NuGet packages to v8
  • Migrating custom stores/services to new v8 interfaces
  • Running EF Core database migrations for v8 (SAML tables)
  • Replacing deprecated APIs (ICache, IClock, IAuthorizationParametersMessageStore)

Core Principles

  • v8.0 requires .NET 10 — update TFM before anything else
  • All breaking changes are compile-time errors (no silent behavior changes)
  • Migration is mechanical — find/replace patterns work for most changes
  • Run EF migrations even if you don't use SAML (schema must match)
  • Always check the latest stable 8.x package version on NuGet before upgrading — do not hardcode 8.0.1; use whatever the latest stable (non-prerelease) 8.x version is at the time of the upgrade.

Docs: https://docs.duendesoftware.com/identityserver/upgrades/v7_4-to-v8_0/

Step-by-Step Migration

1. Update Target Framework

xml
<!-- ❌ Before --><TargetFramework>net8.0</TargetFramework>
<!-- ✅ After --><TargetFramework>net10.0</TargetFramework>

2. Update NuGet Packages

Check NuGet for the latest stable 8.x version. At time of writing, that is 8.0.1, but use whatever is current:

xml
<PackageReference Include="Duende.IdentityServer" Version="8.0.1" /><PackageReference Include="Duende.IdentityServer.EntityFramework" Version="8.0.1" /><!-- Update all Duende.* packages to the latest stable 8.x version -->

3. Run EF Database Migrations

Two migrations are required — one for the Configuration Store and one for the Operational Store:

bash
# Configuration Store — adds 7 SAML-related tablesdotnet ef migrations add Update_DuendeIdentityServer_v8_0 \    -c ConfigurationDbContext -o Migrations/ConfigurationDbdotnet ef database update -c ConfigurationDbContext
# Operational Store — adds 3 SAML session tablesdotnet ef migrations add Update_DuendeIdentityServer_v8_0_Saml \    -c PersistedGrantDbContext -o Migrations/PersistedGrantDbdotnet ef database update -c PersistedGrantDbContext

Both are required even if you don't use SAML (schema must match).

4. Replace ICache<T> with HybridCache

csharp
// ❌ Before (v7)public class MyService{    private readonly ICache<MyData> _cache;    public MyService(ICache<MyData> cache) => _cache = cache;
    public async Task<MyData> GetAsync(string key)    {        return await _cache.GetOrAddAsync(key,            TimeSpan.FromMinutes(5),            () => LoadFromDbAsync(key));    }}
// ✅ After (v8) — use Microsoft HybridCachepublic class MyService{    private readonly HybridCache _cache;    public MyService([FromKeyedServices("ConfigurationStoreCache")] HybridCache cache)        => _cache = cache;
    public async Task<MyData> GetAsync(string key, CancellationToken ct)    {        return await _cache.GetOrCreateAsync(key,            async token => await LoadFromDbAsync(key, token),            new HybridCacheEntryOptions            {                Expiration = TimeSpan.FromMinutes(5)            }, cancellationToken: ct);    }}

Key: use keyed service "ConfigurationStoreCache" (ServiceProviderKeys.ConfigurationStoreCache). CachingOptions.CacheLockTimeout is obsolete.

5. Replace IClock with TimeProvider

csharp
// ❌ Before (v7)public class MyService{    private readonly IClock _clock;    public MyService(IClock clock) => _clock = clock;    public DateTime Now => _clock.UtcNow.UtcDateTime;}
// ✅ After (v8)public class MyService{    private readonly TimeProvider _timeProvider;    public MyService(TimeProvider timeProvider) => _timeProvider = timeProvider;    public DateTime Now => _timeProvider.GetUtcNow().UtcDateTime;}

Note: GetUtcNow() (method) replaces UtcNow (property).

6. Add CancellationToken to All Async Interfaces

All store and service interfaces now require CancellationToken ct as the last parameter:

csharp
// ❌ Before (v7)public Task<Client?> FindClientByIdAsync(string clientId)
// ✅ After (v8)public Task<Client?> FindClientByIdAsync(string clientId, CancellationToken ct)

Affected interfaces include: IClientStore, IResourceStore, IPersistedGrantStore, IDeviceFlowStore, ICorsPolicyService, IProfileService, and all custom stores/services.

Also: ICancellationTokenProvider is removed entirely.

7. Add GetAllClientsAsync to IClientStore

csharp
// ✅ New required methodpublic IAsyncEnumerable<Client> GetAllClientsAsync(CancellationToken ct)

Used by Financial-Grade Security features and conformance reports.

8. Update Refresh Token Service

csharp
// ❌ Before (v7) — individual parameterspublic Task<string> CreateRefreshTokenAsync(    ClaimsPrincipal subject, Token accessToken, Client client)
// ✅ After (v8) — request objectspublic Task<string> CreateRefreshTokenAsync(RefreshTokenCreationRequest request, CancellationToken ct)public Task<string> UpdateRefreshTokenAsync(RefreshTokenUpdateRequest request, CancellationToken ct)

9. Remove IAuthorizationParametersMessageStore

csharp
// ❌ Removed in v8 — use PAR (Pushed Authorization Requests) insteadservices.AddTransient<IAuthorizationParametersMessageStore, MyStore>();
// ✅ PAR is the replacement for passing large authorization parameters

10. Fix Return Type Changes

Nine interfaces changed IEnumerable<T> → IReadOnlyCollection<T>:

csharp
// ❌ Beforepublic Task<IEnumerable<ApiScope>> FindApiScopesByNameAsync(IEnumerable<string> scopeNames)
// ✅ Afterpublic Task<IReadOnlyCollection<ApiScope>> FindApiScopesByNameAsync(    IEnumerable<string> scopeNames, CancellationToken ct)

11. Fix DPoP Type Names

csharp
// ❌ Typo in v7DPoPProofValidatonContext  → DPoPProofValidationContextDPoPProofValidatonResult   → DPoPProofValidationResult

12. Update Licensing Code

csharp
// ❌ Before (v7)var license = IdentityServerLicense.Current;var edition = summary.LicenseEdition;
// ✅ After (v8)var info = LicenseInformation.Current;  // from Duende.IdentityServer.Licensingvar skus = summary.EntitledSkus;        // collection replaces single edition
New v8 License Key Format
  • v8 introduced a new license key file format: the v8 key is a signed JWT carrying a kid header.
  • A v7/earlier key still works with v8 core — no new purchase is needed to run v8 core on an existing key.
  • A v8 key does NOT work on v7/earlier OR on the BFF Security Framework runtime. It fails signature validation with Microsoft.IdentityModel error:
    • IDX10503: Signature validation failed. Token does not have a kid.
    • That exact error is the tell-tale sign of a v8 key loaded into a v7 or BFF runtime.
  • Add-ons require a v8-format key in production: using SAML or Duende User Management in production on v8 REQUIRES a new v8-format license key. Older-format keys run v8 core, but not these add-ons in production.
Runtime License Enforcement Changed (behavioral reversal)

v8 validates feature usage at runtime. When a license IS present but lacks the entitlement, behavior splits into two tiers:

TierBehavior when unlicensedFeatures
ATHROWS during startup validationServer-Side Sessions, Automatic Key Management, SAML (IdP and Service Provider)
BLOGS a warning (rate-limited ~once/5 min)DPoP, Resource Isolation, CIBA, Dynamic Identity Providers, Financial-grade/Conformance, User Management
  • If NO license is configured (local dev / non-prod), Tier-A features downgrade to logging instead of throwing.
  • Guidance: use your production license key in lower environments so entitlement gaps (e.g. Server-Side Sessions) surface before production.
  • Contrast with v7 and earlier: those versions disabled some features at runtime when unlicensed (Server-Side Sessions, DPoP, Resource Isolation, PAR, Dynamic Identity Providers, CIBA). v8 no longer disables — it logs or throws per the tiers above.
Editions → Plans

The product moved from fixed Starter / Business / Enterprise editions to generic plans. The old three editions are still honored for legacy/long-term customers only. The Community edition remains. Update any code or docs that hard-code "three editions" to reflect the plan model.

13. Update EF Identity Provider Store

csharp
// ❌ Before (v7)public IdentityProviderStore(IServiceProvider sp, ConfigurationDbContext ctx)
// ✅ After (v8) — new required parameterpublic IdentityProviderStore(    IServiceProvider sp, ConfigurationDbContext ctx, IIdentityProviderFactory factory)

14. Rename AuthorizationError → InteractionError

csharp
// ❌ Before (v7)if (result.Error == AuthorizationError.LoginRequired) { }
// ✅ After (v8)if (result.Error == InteractionError.LoginRequired) { }

Values remain the same: AccessDenied, LoginRequired, InteractionRequired.

15. Rename DenyAuthorizationAsync → DenyAuthenticationAsync

csharp
// ❌ Before (v7)await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
// ✅ After (v8) — now accepts IAuthenticationContext (protocol-agnostic for OIDC/SAML)await _interaction.DenyAuthenticationAsync(context, InteractionError.AccessDenied);

16. Rename ProfileDataRequestContext.Client → .Application

csharp
// ❌ Before (v7)var client = context.Client;
// ✅ After (v8)var client = context.Application;

17. Update ITokenValidator.ValidateAccessTokenAsync

csharp
// ❌ Before (v7)await _validator.ValidateAccessTokenAsync(token);
// ✅ After (v8) — new expectedScope parameterawait _validator.ValidateAccessTokenAsync(token, expectedScope: null, ct);

18. Relocate PreviewFeatureOptions

PreviewFeatureOptions and IdentityServerOptions.Preview are removed. Options relocated:

csharp
// ❌ Before (v7)options.Preview.EnableDiscoveryDocumentCache = true;options.Preview.DiscoveryDocumentCacheDuration = TimeSpan.FromMinutes(10);options.Preview.StrictClientAssertionAudienceValidation = true;
// ✅ After (v8)options.Discovery.EnableDiscoveryDocumentCache = true;options.Discovery.DiscoveryDocumentCacheDuration = TimeSpan.FromMinutes(10);options.StrictClientAssertionAudienceValidation = true;  // default changed to false!

Other Notable Changes

  • NRT enabled: All assemblies use nullable reference types. Fix nullable warnings.
  • HTTP 303: POST endpoint redirects now unconditionally use 303 (FAPI 2.0 compliance).
  • PersistedGrantFilter.ClientIds/Types: Now non-nullable with empty collection defaults. Replace null checks with .Count > 0.
  • IUserSession: Three new SAML session methods added (implement as no-op if not using SAML):
    • AddSamlSessionAsync, GetSamlSessionListAsync, RemoveSamlSessionAsync
  • Log levels: Secret validation failures changed from Error to Debug — update alerting to watch for Warning-level entries at endpoint level instead.
  • Device flow consent: "Remember My Decision" no longer offered — RememberConsent always false during device flow (RFC 8628 security).
  • License key from IConfiguration: IdentityServer now reads license key automatically from Duende:IdentityServer:LicenseKey or Duende:LicenseKey in configuration.
  • DPoPExtensions → DPoPServiceCollectionExtensions: Class renamed in JwtBearer package.
  • Token cleanup performance: When no IOperationalStoreNotification registered, uses single ExecuteDeleteAsync call (automatic improvement, no action needed).
  • Orphaned grants revoked on session overwrite: When server-side sessions enabled and session cookie reused by different user, previous user's grants are automatically revoked.

Migration Checklist

  1. ☐ Update TFM to net10.0
  2. ☐ Update all Duende.* packages to latest stable 8.x (check NuGet)
  3. ☐ Run EF migrations (both ConfigurationDbContext and PersistedGrantDbContext)
  4. ☐ Replace ICache<T> → keyed HybridCache
  5. ☐ Replace IClock → TimeProvider
  6. ☐ Add CancellationToken to all async store/service methods
  7. ☐ Remove ICancellationTokenProvider references
  8. ☐ Add GetAllClientsAsync to custom IClientStore (returns IAsyncEnumerable<Client>)
  9. ☐ Update IRefreshTokenService implementations (request objects)
  10. ☐ Remove IAuthorizationParametersMessageStore (use PAR)
  11. ☐ Fix IEnumerable<T> → IReadOnlyCollection<T> return types
  12. ☐ Fix DPoP type name typos
  13. ☐ Update licensing references (IdentityServerLicense → LicenseInformation)
  14. ☐ Rename AuthorizationError → InteractionError
  15. ☐ Rename DenyAuthorizationAsync → DenyAuthenticationAsync
  16. ☐ Rename ProfileDataRequestContext.Client → .Application
  17. ☐ Update ITokenValidator.ValidateAccessTokenAsync calls (add expectedScope param)
  18. ☐ Relocate PreviewFeatureOptions settings
  19. ☐ Fix nullable reference type warnings
  20. ☐ Test build and run

Common Pitfalls

  1. Forgetting EF migration: Even without SAML, the schema must be updated or EF will throw at runtime.
  2. HybridCache keyed service: Must use [FromKeyedServices("ConfigurationStoreCache")] — plain HybridCache injection gets a different instance.
  3. CancellationToken propagation: Don't pass CancellationToken.None everywhere — propagate from the method parameter for proper request cancellation.
  4. GetAllClientsAsync performance: Return all clients from your store; used rarely but must be implemented.
  5. PAR migration: If you used IAuthorizationParametersMessageStore for large auth requests, switch clients to use PAR (require_pushed_authorization_requests).
  6. IDX10503 after dropping in a v8 key: A v8-format license key (signed JWT with a kid header) fails signature validation on v7/earlier or the BFF Security Framework runtime with IDX10503: Signature validation failed. Token does not have a kid. Keep the v7-format key for those runtimes — it still works on v8 core; only SAML/User Management add-ons in production require the new v8-format key.
  7. Entitlement gaps surface late: v8 no longer silently disables unlicensed features — Server-Side Sessions, Automatic Key Management, and SAML now throw at startup when a license is present but missing the entitlement. Run lower environments with the production license key to catch this before deploying.

Related Skills

  • identityserver-configuration — IdentityServer host configuration and options
  • identityserver-stores — Store implementation patterns (affected by CancellationToken changes)
  • identityserver-saml — SAML 2.0 support (new in v8, requires EF migration)
  • identityserver-usermanagement — User Management (new in v8)

来源与署名

来源:DuendeSoftware/duende-skills位于skills/identityserver-upgrade-v7-to-v8提交fb32edc

许可证: 无许可证

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

举报或申请下架