Skip to content

Duende IdentityServer v7.4 to v8.0

IdentityServer v8.0 includes support for .NET 10, SAML 2.0 Identity Provider support, conformance reporting, and many other fixes and enhancements.

IdentityServer 8.0 targets .NET 10 only. In your IdentityServer host project, update the target framework. For example, in your project file:

<TargetFramework>net8.0</TargetFramework>

or

<TargetFramework>net9.0</TargetFramework>

would change to:

<TargetFramework>net10.0</TargetFramework>

Any NuGet packages you use that target an older version of .NET should also be updated. For example, Microsoft.EntityFrameworkCore.SqlServer or Microsoft.AspNetCore.Authentication.Google should be updated to their .NET 10-compatible versions. Depending on your IdentityServer host project, there may or may not be code changes from those updated dependencies.

In your IdentityServer host project, update the version of the Duende.IdentityServer package. For example, in your project file:

<PackageReference Include="Duende.IdentityServer" Version="7.4.0"/>

would change to:

<PackageReference Include="Duende.IdentityServer" Version="8.0.0-*"/>

If you use any of the other Duende.IdentityServer packages, update those as well:

<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="8.0.0-*"/>
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="8.0.0-*"/>
<PackageReference Include="Duende.IdentityServer.Configuration" Version="8.0.0-*"/>

If you are using the SAML 2.0 Identity Provider feature and the Entity Framework storage packages, new tables are required to store SAML Service Provider configurations. Run the following migration commands to update your database schema:

Terminal window
dotnet ef migrations add Update_DuendeIdentityServer_v8_0 -c ConfigurationDbContext -o Migrations/ConfigurationDb
dotnet ef database update -c ConfigurationDbContext

The migration creates five new tables in the configuration database: SamlServiceProviders, SamlServiceProviderAssertionConsumerServices, SamlServiceProviderSigningCertificates, SamlServiceProviderEncryptionCertificates, and SamlServiceProviderClaimMappings.

SQL Server database objects created by this migration
-- saml-service-providers.sql
CREATE TABLE [SamlServiceProviders] (
[Id] int NOT NULL IDENTITY,
[EntityId] nvarchar(200) NOT NULL,
[DisplayName] nvarchar(200) NULL,
[Description] nvarchar(1000) NULL,
[Enabled] bit NOT NULL DEFAULT CAST(1 AS bit),
[ClockSkewTicks] bigint NULL,
[RequestMaxAgeTicks] bigint NULL,
[AssertionConsumerServiceBinding] int NOT NULL,
[SingleLogoutServiceUrl] nvarchar(2000) NULL,
[SingleLogoutServiceBinding] int NULL,
[RequireSignedAuthnRequests] bit NOT NULL,
[EncryptAssertions] bit NOT NULL,
[RequireConsent] bit NOT NULL,
[AllowIdpInitiated] bit NOT NULL,
[DefaultNameIdFormat] nvarchar(500) NOT NULL DEFAULT N'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
[DefaultPersistentNameIdentifierClaimType] nvarchar(500) NULL,
[SigningBehavior] int NULL,
[Created] datetime2 NOT NULL,
[Updated] datetime2 NULL,
[LastAccessed] datetime2 NULL,
[NonEditable] bit NOT NULL,
CONSTRAINT [PK_SamlServiceProviders] PRIMARY KEY ([Id])
);
CREATE UNIQUE INDEX [IX_SamlServiceProviders_EntityId]
ON [SamlServiceProviders] ([EntityId]);
CREATE TABLE [SamlServiceProviderAssertionConsumerServices] (
[Id] int NOT NULL IDENTITY,
[Url] nvarchar(2000) NOT NULL,
[SamlServiceProviderId] int NOT NULL,
CONSTRAINT [PK_SamlServiceProviderAssertionConsumerServices] PRIMARY KEY ([Id]),
CONSTRAINT [FK_SamlServiceProviderAssertionConsumerServices_SamlServiceProviders_SamlServiceProviderId]
FOREIGN KEY ([SamlServiceProviderId]) REFERENCES [SamlServiceProviders] ([Id]) ON DELETE CASCADE
);
CREATE UNIQUE INDEX [IX_SamlServiceProviderACS_ProviderId_Url]
ON [SamlServiceProviderAssertionConsumerServices] ([SamlServiceProviderId], [Url]);
CREATE TABLE [SamlServiceProviderSigningCertificates] (
[Id] int NOT NULL IDENTITY,
[Data] nvarchar(4000) NOT NULL,
[SamlServiceProviderId] int NOT NULL,
CONSTRAINT [PK_SamlServiceProviderSigningCertificates] PRIMARY KEY ([Id]),
CONSTRAINT [FK_SamlServiceProviderSigningCertificates_SamlServiceProviders_SamlServiceProviderId]
FOREIGN KEY ([SamlServiceProviderId]) REFERENCES [SamlServiceProviders] ([Id]) ON DELETE CASCADE
);
CREATE TABLE [SamlServiceProviderEncryptionCertificates] (
[Id] int NOT NULL IDENTITY,
[Data] nvarchar(4000) NOT NULL,
[SamlServiceProviderId] int NOT NULL,
CONSTRAINT [PK_SamlServiceProviderEncryptionCertificates] PRIMARY KEY ([Id]),
CONSTRAINT [FK_SamlServiceProviderEncryptionCertificates_SamlServiceProviders_SamlServiceProviderId]
FOREIGN KEY ([SamlServiceProviderId]) REFERENCES [SamlServiceProviders] ([Id]) ON DELETE CASCADE
);
CREATE TABLE [SamlServiceProviderClaimMappings] (
[Id] int NOT NULL IDENTITY,
[ClaimType] nvarchar(250) NOT NULL,
[SamlAttributeName] nvarchar(250) NOT NULL,
[SamlServiceProviderId] int NOT NULL,
CONSTRAINT [PK_SamlServiceProviderClaimMappings] PRIMARY KEY ([Id]),
CONSTRAINT [FK_SamlServiceProviderClaimMappings_SamlServiceProviders_SamlServiceProviderId]
FOREIGN KEY ([SamlServiceProviderId]) REFERENCES [SamlServiceProviders] ([Id]) ON DELETE CASCADE
);
CREATE UNIQUE INDEX [IX_SamlServiceProviderClaimMappings_ProviderId_ClaimType]
ON [SamlServiceProviderClaimMappings] ([SamlServiceProviderId], [ClaimType]);

If you are not using the SAML 2.0 feature, no schema changes are required.

If your IdentityServer implementation uses a custom IClientStore, you must add the new GetAllClientsAsync method (see Breaking Change below).

If you are using Duende.IdentityServer.EntityFramework, the package migration will handle schema updates automatically when you run the EF migrations command above.

Duende.IdentityServer.IClock has been removed. Replace it with the standard .NET System.TimeProvider (available since .NET 8).

// Before (v7.x)
public class MyService
{
public MyService(IClock clock) { }
}
// After (v8.0)
public class MyService
{
public MyService(TimeProvider timeProvider) { }
}

To get the current time, use timeProvider.GetUtcNow() instead of clock.UtcNow.

Internal IdentityServer services such as DefaultTokenCreationService and DefaultTokenService have been updated to accept TimeProvider in place of IClock.

CancellationToken Now Required on All Interface Methods

Section titled “CancellationToken Now Required on All Interface Methods”

All store and service interfaces now include a CancellationToken parameter on every async method. The parameter name is ct (not cancellationToken).

// Before (v7.x)
public class MyClientStore : IClientStore
{
public Task<Client?> FindClientByIdAsync(string clientId)
{
// ...
}
}
// After (v8.0)
public class MyClientStore : IClientStore
{
public Task<Client?> FindClientByIdAsync(string clientId, CancellationToken ct)
{
// ...
}
public IAsyncEnumerable<Client> GetAllClientsAsync(CancellationToken ct)
{
// ...
}
}

This applies to all store interfaces in Duende.IdentityServer.Stores and service interfaces in Duende.IdentityServer.Services. Update all custom implementations to add the CancellationToken ct parameter.

ICancellationTokenProvider has been removed from both Duende.IdentityServer and Duende.IdentityServer.Configuration.EntityFramework. If you injected this service, remove that dependency. Use the CancellationToken passed directly to interface methods instead.

UserInteractionOptions.UseHttp303Redirects has been removed. IdentityServer now always uses HTTP 303 (See Other) for redirects from POST endpoints, in compliance with FAPI 2.0 Section 5.3.2.2.

No action is needed unless you explicitly set UseHttp303Redirects = false in a previous version. If so, remove that setting — the redirect behavior can no longer be changed.

IClientStore.GetAllClientsAsync Now Required

Section titled “IClientStore.GetAllClientsAsync Now Required”

IClientStore now includes a second required method:

IAsyncEnumerable<Client> GetAllClientsAsync(CancellationToken ct);

All custom IClientStore implementations must add this method. It is used by the conformance report and configuration validation features. Return an async enumerable of all configured clients.

For the in-memory store, this returns all clients passed to AddInMemoryClients. For Entity Framework, it queries the Clients table. Custom implementations should return all clients without filtering.

The Client.DPoPValidationMode property (of type DPoPTokenExpirationValidationMode) is unchanged in IdentityServer’s client model.

In the JwtBearer package (Duende.AspNetCore.Authentication.JwtBearer), the DPoP configuration was restructured for v8.0:

  • New enum DPoPProofExpirationMode with values IssuedAt, Nonce, and Both
  • DPoPOptions.ProofTokenExpirationMode property (default: IssuedAt)
  • New properties: ProofTokenIssuedAtClockSkew, ProofTokenNonceClockSkew, EnableReplayDetection
  • New interface IDPoPNonceValidator with default implementation DefaultDPoPNonceValidator
  • DPoPExtensions class replaced by DPoPServiceCollectionExtensions

Update any code that references DPoPExtensions to use DPoPServiceCollectionExtensions instead.

IdentityServer 8.0 adds support for acting as a SAML 2.0 Identity Provider, enabling integration with enterprise applications and legacy systems that use SAML. See the SAML 2.0 documentation for setup and configuration details.

A new Duende.IdentityServer.ConformanceReport package generates an HTML report assessing your IdentityServer deployment against OAuth 2.1 and FAPI 2.0 specifications. See the Conformance Report documentation for details.

That’s it. Of course, at this point you can and should test that your IdentityServer is updated and working properly.