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

檢舉或申請下架