Duende IdentityServer v6.3 to v7.0
IdentityServer v7.0 includes support for .NET 8, pushed authorization requests, OpenTelemetry metrics, cleanup job improvements, and many other fixes and enhancements. Please see our release notes for complete details.
Step 1: Update .NET Version
Section titled “Step 1: Update .NET Version”In your IdentityServer host project, update the version of the .NET framework. For example in your project file:
<TargetFramework>net6.0</TargetFramework>would change to:
<TargetFramework>net8.0</TargetFramework>Any NuGet packages that you are using that target an older version of .NET should also be updated.
For example, the version of Microsoft.EntityFrameworkCore.SqlServer or Microsoft.AspNetCore.Authentication.Google
should be updated.
Depending on what your IdentityServer host project is using, there may or may not be code changes based on those updated
dependencies.
Step 2: Update NuGet package
Section titled “Step 2: Update NuGet package”In your IdentityServer host project, update the version of the Duende.IdentityServer package. For example in your project file:
<PackageReference Include="Duende.IdentityServer" Version="6.3.0"/>would change to:
<PackageReference Include="Duende.IdentityServer" Version="7.0"/>Step 3: Update Database Schema
Section titled “Step 3: Update Database Schema”The schema updates required in this version are:
- The server-side session entity in
Duende.IdentityServer.EntityFrameworknow uses a 64-bit long as its primary key ( previously was a 32-bit int). - Two new properties have been added to the client model for PAR support.
Client.RequirePushedAuthorizationis a new boolean property that controls if this client requires PAR. PAR is required if either the global configuration is enabled or if the client’s flag is enabled (this can’t be used to opt out of the global configuration). It is safe to initialize this column to false for existing clients, which will mean that the global configuration will be used.Client.PushedAuthorizationLifetimeis a new nullable integer property that controls the lifetime of pushed authorization requests (in seconds) for a client. If this lifetime is set, it takes precedence over the global configuration. It is safe to initialize this column tonullfor existing clients, which means the global configuration is used.
- A new table has been added to store pushed authorization requests. This new table contains a hashed identifier, the pushed parameters (as a string, serialized and data protected), and the expiration time of the request.
IdentityServer is abstracted from the data store on multiple levels, so the steps involved in updating your data store will depend on your implementation details.
Custom Store Implementations
Section titled “Custom Store Implementations”The core of IdentityServer is written against the store interfaces, which abstract all the implementation details of actually storing data. If your IdentityServer implementation includes a custom implementation of those stores, then you will have to determine how best to include the changes in the model in the underlying data store and make any necessary changes to schemas, if your data store requires that.
Duende.IdentityServer.EntityFramework
Section titled “Duende.IdentityServer.EntityFramework”We also provide a default implementation of the stores in the Duende.IdentityServer.EntityFramework package, but this
implementation is still highly abstracted because it is usable with any database that has an EF provider. Different
database vendors have very different dialects of sql that have different syntax and type systems, so we don’t provide
schema changes directly. Instead, we provide the Entity Framework entities and mappings which can be used with Entity
Framework’s migrations feature to generate the schema updates that are needed in your database.
To generate a migration for the new columns, run the command below. Note that you might need to adjust paths based on your specific organization of the migration files.
dotnet ef migrations add Update_DuendeIdentityServer_v7_0 -c ConfigurationDbContext -o Migrations/ConfigurationDbdotnet ef migrations add Update_DuendeIdentityServer_v7_0 -c PersistedGrantDbContext -o Migrations/PersistedGrantDbThe ServerSideSession.Id property has been changed from int to long. The generated migration handles the data type
change but does not take into account that this column is the primary key for the table. If the primary key constraint
is not removed, running the migration will fail. The primary key of the table needs to be dropped before the type is
altered and then recreated. The easiest way to do so is to update the generated migration file:
// Add this line before AlterColumnmigrationBuilder.DropPrimaryKey("PK_ServerSideSessions", "ServerSideSessions");
// This should already be in the generated codemigrationBuilder.AlterColumn<long>( name: "Id", table: "ServerSideSessions", type: "bigint", nullable: false, oldClrType: typeof(int), oldType: "int") .Annotation("SqlServer:Identity", "1, 1") .OldAnnotation("SqlServer:Identity", "1, 1");
// Add this after AlterColumnmigrationBuilder.AddPrimaryKey("PK_ServerSideSessions", "ServerSideSessions", "Id");Once you’ve updated that migration, both migrations should be applied to your database:
dotnet ef database update -c ConfigurationDbContextdotnet ef database update -c PersistedGrantDbContextSome organizations prefer to use other tools for managing schema changes. You’re free to manage your schema however you see fit, as long as the entities can be successfully mapped. Even if you’re not going to ultimately use Entity Framework migrations to manage your database changes, generating a migration can be a useful development step to get an idea of what needs to be done.
Step 4: Breaking changes
Section titled “Step 4: Breaking changes”Only impacts particular customizations or edge cases
Section titled “Only impacts particular customizations or edge cases”- The
DefaultCorsPolicyServicenow depends on theIConfigurationDbContextdirectly, instead of taking a dependency on theIServiceProviderand resolving that DbContext from it. If you have a customized CORS implementation that derives from theDefaultCorsPolicyService, you need to update the constructor of your derived class to use theIConfigurationDbContext. - The
DPoPProofValidationContexthas been refactored. Instead of theClientproperty, we now put the relevant details (expiration validation mode and clock skew) directly in the context. We also have added the HTTP method and URL to the context. If you have a custom implementation of theIDPoPProofValidatoror a class that derives from theDefaultDPoPProofValidator, update your usage of the context appropriately. - The
DefaultTokenServiceno longer includes anIHttpContextAccessor. This member was unused by the default implementation and marked as obsolete. Customizations that derive from theDefaultTokenServiceno longer need to pass the accessor to the base constructor. If such a customization needs the accessor, add it to the derived class. - The
ValidatedAuthorizeRequest.RequestedResourceIndicatorsproperty was misspelled and has been renamedRequestedResourceIndicators. - The reference token store now includes the session id when revoking reference tokens. Implementors of
IReferenceTokenStoreshould update their implementation of token revocation to include the session id. - Invalid prompt modes now cause validation errors that result in an HTTP 400 (Bad Request). Previously, invalid prompt modes were ignored. This complies with updates to the OpenID Connect specification.
- A new interface
IIdentityServerToolshas been introduced for theIdentityServerToolshelper class to allow mocking. Update any direct references toIdentityServerToolstoIIdentityServerTools.
Newly Deprecated
Section titled “Newly Deprecated”-
IAuthorizationParametersMessageStoreis deprecated. PAR is a more robust/standardized approach to get similar benefits. -
The
IHttpContextAccessorin theEndSessionRequestValidatoris unused and has been marked as obsolete. It will be removed in a future version.
Previously Deprecated, Now Removed
Section titled “Previously Deprecated, Now Removed”- The obsolete
IdentityServerOriginconstant has been removed. - Several obsolete extension methods on
HttpContexthave been removed. These methods are replaced by methods inIServerUrlsandIIssuerNameService. See #1457HttpContext.GetSchemeSupportsSignOutAsyncis replaced byIAuthenticationHandlerProvider.GetHandlerAsync(you will also need to check if the handler implementsIAuthenticationSignOutHandler).HttpContext.GetIdentityServerOriginandHttpContext.SetIdentityServerOriginare replaced byIServerUrls.Origin.HttpContext.GetIdentityServerBasePathandHttpContext.SetIdentityServerBasePathare replaced byIServerUrls.BasePath.GetIdentityServerHostis replaced byIServerUrls.OriginGetIdentityServerBaseUrlis replaced byIServerUrls.BaseUrlGetIdentityServerRelativeUrlis replaced byIServerUrls.GetIdentityServerRelativeUrlGetIdentityServerIssuerUriis replaced byIIssuerNameService.GetCurrentAsyncRedirectToAbsoluteUrlis replaced by redirecting to a call toIServerUrls.GetAbsoluteUrl.
- The obsolete and unused
IUserSessionExtensionsinterface has been removed. - The obsolete
IPrincipal.GetNameandIIdentity.GetNameextension methods have been removed. UseClaimsPrincipal.GetDisplayNameinstead. - The obsolete
ResourceValidationRequest.IncludeNonIsolatedApiResourceshas been removed. This flag was no longer used.
Unlikely to impact anyone
Section titled “Unlikely to impact anyone”-
The
KeyManagementOptions.SigningAlgorithmsis now anICollectionrather than anIEnumerable. If you are configuring signing algorithms using code, and setting theSigningAlgorithmsto some type that implementsIEnumerablebut notICollection, then you must change the type that you are using. In practice, we expect everyone uses a list or array (which are bothICollections). -
The value of the constant
IdentityServerAuthenticationTypehas changed from “IdentityServer4” to ” Duende.IdentityServer”. This constant is used as the value of the authentication type within the [ClaimsIdentity](https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimsidentity.-ctor?view=net-7.0#system-security-claims-claimsidentity-ctor(system-security-principal-iidentity-system-collections-generic-ienumerable((system-security-claims-claim)) -system-string-system-string-system-string)) that IdentityServer constructs. The authentication type’s value is never used by IdentityServer or ASP.NET, so this is unlikely to impact anyone. It is also the name of the default cors policy created by IdentityServer. This could theoretically impact you if you have a CORS policy named ” Duende.IdentityServer”, as the new name now conflicts.
Step 5: Done!
Section titled “Step 5: Done!”That’s it. Of course, at this point you can and should test that your IdentityServer is updated and working properly.
Step 6: Troubleshooting .NET 8 Issues Optional
Section titled “Step 6: Troubleshooting .NET 8 Issues ”OptionalYou are now done with everything related to the IdentityServer upgrade, but we would also like to add a references to breaking changes in .NET 8 and associated libraries that are likely to affect the IdentityServer setup.
- Entity Framework Core 8 has some performance improvements in the query generation for Microsoft SQL Server that relies on database features that requires a database compatibility level of at least 130. This can cause errors on database access like Microsoft.Data.SqlClient.SqlException (0x80131904): The syntax near ’$’ is incorrect. or Microsoft.Data.SqlClient.SqlException (0x80131904): Incorrect syntax near the keyword ‘WITH’. Please see https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/breaking-changes#mitigations for more information.
- For container deployments, the default ASP.NET Core port changed from 80 to 8080. This might require configuration updates to either continue using port 80 or to migrate to using 8080.
- IdentityServer and the ASP.NET Authentication packages all depends on the
Microsoft.IdentityModel.*packages. With packages that are brought in as transient dependencies there is less control of the versions being pulled in. If different packages from theMicrosoft.IdentityModel.* family end up having different versions, there will be odd bugs. We’ve seen reports where the refresh token isn’t stored and where the OIDC handler fails to redirect to an OIDC Provider because it failed reading the discovery document. Always ensure that all Microsoft.IdentityModel. packages are of exactly the same version*. If they are not, you might need to make an explicit<PackageReference>to pin an exact version.