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.EntityFramework
now 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.RequirePushedAuthorization
is 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.PushedAuthorizationLifetime
is 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 tonull
for 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/PersistedGrantDb
The 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 PersistedGrantDbContext
Some 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
DefaultCorsPolicyService
now depends on theIConfigurationDbContext
directly, instead of taking a dependency on theIServiceProvider
and 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
DPoPProofValidationContext
has been refactored. Instead of theClient
property, 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 theIDPoPProofValidator
or a class that derives from theDefaultDPoPProofValidator
, update your usage of the context appropriately. - The
DefaultTokenService
no longer includes anIHttpContextAccessor
. This member was unused by the default implementation and marked as obsolete. Customizations that derive from theDefaultTokenService
no 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.RequestedResourceIndicators
property was misspelled and has been renamedRequestedResourceIndicators
. - The reference token store now includes the session id when revoking reference tokens. Implementors of
IReferenceTokenStore
should 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
IIdentityServerTools
has been introduced for theIdentityServerTools
helper class to allow mocking. Update any direct references toIdentityServerTools
toIIdentityServerTools
.
Newly Deprecated
Section titled “Newly Deprecated”-
IAuthorizationParametersMessageStore
is deprecated. PAR is a more robust/standardized approach to get similar benefits. -
The
IHttpContextAccessor
in theEndSessionRequestValidator
is 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
IdentityServerOrigin
constant has been removed. - Several obsolete extension methods on
HttpContext
have been removed. These methods are replaced by methods inIServerUrls
andIIssuerNameService
. See #1457HttpContext.GetSchemeSupportsSignOutAsync
is replaced byIAuthenticationHandlerProvider.GetHandlerAsync
(you will also need to check if the handler implementsIAuthenticationSignOutHandler
).HttpContext.GetIdentityServerOrigin
andHttpContext.SetIdentityServerOrigin
are replaced byIServerUrls.Origin
.HttpContext.GetIdentityServerBasePath
andHttpContext.SetIdentityServerBasePath
are replaced byIServerUrls.BasePath
.GetIdentityServerHost
is replaced byIServerUrls.Origin
GetIdentityServerBaseUrl
is replaced byIServerUrls.BaseUrl
GetIdentityServerRelativeUrl
is replaced byIServerUrls.GetIdentityServerRelativeUrl
GetIdentityServerIssuerUri
is replaced byIIssuerNameService.GetCurrentAsync
RedirectToAbsoluteUrl
is replaced by redirecting to a call toIServerUrls.GetAbsoluteUrl
.
- The obsolete and unused
IUserSessionExtensions
interface has been removed. - The obsolete
IPrincipal.GetName
andIIdentity.GetName
extension methods have been removed. UseClaimsPrincipal.GetDisplayName
instead. - The obsolete
ResourceValidationRequest.IncludeNonIsolatedApiResources
has been removed. This flag was no longer used.
Unlikely to impact anyone
Section titled “Unlikely to impact anyone”-
The
KeyManagementOptions.SigningAlgorithms
is now anICollection
rather than anIEnumerable
. If you are configuring signing algorithms using code, and setting theSigningAlgorithms
to some type that implementsIEnumerable
but 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
IdentityServerAuthenticationType
has 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.