IdentityServer4 to Duende IdentityServer v7.2
This upgrade guide covers upgrading from IdentityServer4 to Duende IdentityServer v7.2. IdentityServer4 reached its end of life (EOL) on December 13, 2022. It is strongly advised to migrate to Duende IdentityServer.
Depending on your current version of IdentityServer4, different steps may be required.
You can determine the version of IdentityServer4 by running the dotnet list
command at the root of your IdentityServer host project, or using NuGet tooling in Visual Studio or JetBrains Rider.
dotnet list package | sls "IdentityServer4"
dotnet list package | grep IdentityServer4
This command will print a list of packages you are using in your solution, along with their version.
> IdentityServer4 3.1.4 3.1.4 > IdentityServer4.EntityFramework 3.1.4 3.1.4
Depending on the package version shown, your next steps will be different:
- If you are on IdentityServer v3.x, we recommend first upgrading to IdentityServer4 v4.x, and then to Duende IdentityServer. The configuration object model changed between the two major versions of IdentityServer4, and we recommend upgrading step-by-step.
- If you are on IdentityServer v4.x, you can immediately upgrade to Duende IdentityServer.
IdentityServer4 v3.x to IdentityServer v4.x
Section titled “IdentityServer4 v3.x to IdentityServer v4.x”The most straightforward upgrade path is to first update to the latest version of IdentityServer4 v4, and then continue to the latest Duende IdentityServer.
Between IdentityServer4 v3.x and v4.x, the configuration object model was updated:
- The relation between
ApiResources
andApiScopes
was changed from parent-child to many-to-many. - A number of configuration types were renamed:
ApiProperties
toApiResourceProperties
ApiSecrets
toApiResourceSecrets
IdentityClaims
toIdentityResourceClaims
IdentityProperties
toIdentityResourceProperties
ApiScopes
toApiResourceScopes
IdentityServer4 projects that use the IdentityServer4.EntityFramework
package or implement their own stores will need to update their code and/or database to reflect these changes.
Step 1: Update NuGet Packages
Section titled “Step 1: Update NuGet Packages”Update the IdentityServer4 dependencies in your IdentityServer host project to version 4.1.2
.
<PackageReference Include="IdentityServer4" Version="3.1.4" /><PackageReference Include="IdentityServer4" Version="4.1.2" />
Make sure to change the version number for all IdentityServer4 packages, including IdentityServer4.EntityFramework
and IdentityServer4.AspNetIdentity
.
Step 2: Make Code Changes
Section titled “Step 2: Make Code Changes”It’s likely some code changes will be required, especially when using the IdentityServer UI templates. Visual Studio and JetBrains Rider provide great code completion that can be of help here.
A couple of compilation errors and required changes you may encounter:
-
The
IIdentityServerInteractionService.GetAllUserConsentsAsync
method was renamed toIIdentityServerInteractionService.GetAllUserGrantsAsync
-
ConsentResponse.Denied
was removed. Use theDenyAuthorizationAsync
instead:*.cs await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); -
No overload method
SignInAsync
takes N arguments. TheHttpContext.SignInAsync
signature changed:*.cs // issue authentication cookie with subject ID and usernameawait HttpContext.SignInAsync(user.SubjectId, user.Username, props);var isuser = new IdentityServerUser(user.SubjectId){DisplayName = user.Username};await HttpContext.SignInAsync(isuser);
-
AuthorizationRequest
doesn’t contain definition forClientId
:*.cs var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);var client = await _clientStore.FindEnabledClientByIdAsync(request.Client.ClientId); -
AuthorizationRequest
doesn’t contain definition forScopesRequested
:*.cs var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ValidatedResources.RawScopeValues); -
IClientStore
doesn’t contain definition forIsPkceClientAsync
:*.cs if (await _clientStore.IsPkceClientAsync(context.ClientId))if (context.IsNativeClient()) -
The name
ProcessLoginCallbackForOidc
does not exist in the current context:*.cs ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps);ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps);ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps);ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); -
ConsentResponse
does not contain a definition forScopesConsented
:*.cs grantedConsent = new ConsentResponse{RememberConsent = model.RememberConsent,ScopesConsented = scopes.ToArray()ScopesValuesConsented = scopes.ToArray()};
Step 3: Update Database Schema
Section titled “Step 3: Update Database Schema”If you are using the IdentityServer4.EntityFramework
package to store configuration and operational data in a database,
you’ll need to create two database migrations that update the database schema.
Note that you may want to change the database migration paths in the examples below to reflect your project structure.
For the operational data, you can create and apply an Entity Framework Core migration that targets the PersistedGrantDbContext
database context.
-
Create the migration:
Terminal dotnet ef migrations add Grants_v4 -c PersistedGrantDbContext -o Migrations/PersistedGrantDb -
Apply the migration to your database:
Terminal dotnet ef database update -c PersistedGrantDbContext
For your configuration data, the conversation from the old schema to the new will need to be performed by applying a custom SQL script.
We’ll start with creating a migration that targets the ConfigurationDbContext
database context:
-
Create the migration:
Terminal dotnet ef migrations add Config_v4 -c ConfigurationDbContext -o Migrations/ConfigurationDbYou will see a message “An operation was scaffolded that may result in the loss of data. Please review the migration for accuracy.” in the output. To avoid data loss, the migration will need to be updated.
-
To ensure no data is lost, make sure to include the
ConfigurationDb_v4_delta.sql
script in your project.You can add the script as an embedded resource by updating the
.csproj
file:.csproj <ItemGroup><EmbeddedResource Include="ConfigurationDb_v4_delta.sql" /></ItemGroup> -
Modify the migration class that was just created and replace it with the following code:
Config_v4.cs using System.IO;using Microsoft.EntityFrameworkCore.Migrations;namespace IdentityServerMigrationSample.Migrations.ConfigurationDb{public partial class Config_v4 : Migration{protected override void Up(MigrationBuilder migrationBuilder){var assembly = typeof(Program).Assembly;using (var s = assembly.GetManifestResourceStream("IdentityServerMigrationSample.ConfigurationDb_v4_delta.sql")){using (StreamReader sr = new StreamReader(s)){var sql = sr.ReadToEnd();migrationBuilder.Sql(sql);}}}protected override void Down(MigrationBuilder migrationBuilder){}}} -
Apply the migration to your database:
Terminal dotnet ef database update -c ConfigurationDbContext
Your database schema should now be updated. You can verify all data is intact, using the queries outlined in query_v4.sql
.
These queries may return a lot of data, consider adding a TOP
clause if you want to only sample some of the migrated data.
View SQL queries to validate migration succeeded
SELECT * FROM __EFMigrationsHistory
SELECT * FROM ApiResourceClaimsSELECT * FROM ApiResourcePropertiesSELECT * FROM ApiResourcesSELECT * FROM ApiResourceScopesSELECT * FROM ApiResourceSecrets
SELECT * FROM ApiScopeClaimsSELECT * FROM ApiScopesSELECT * FROM ApiScopeProperties
SELECT * FROM ClientClaimsSELECT * FROM ClientCorsOriginsSELECT * FROM ClientGrantTypesSELECT * FROM ClientIdPRestrictionsSELECT * FROM ClientPostLogoutRedirectUrisSELECT * FROM ClientPropertiesSELECT * FROM ClientRedirectUrisSELECT * FROM ClientsSELECT * FROM ClientScopesSELECT * FROM ClientSecrets
SELECT * FROM DeviceCodesSELECT * FROM PersistedGrants
SELECT * FROM IdentityResourceClaimsSELECT * FROM IdentityResourcePropertiesSELECT * FROM IdentityResources
With the migration to IdentityServer v4.x complete, you can upgrade to Duende IdentityServer.
IdentityServer4 v4.x to Duende IdentityServer
Section titled “IdentityServer4 v4.x to Duende IdentityServer”Upgrading from IdentityServer4 v4.x to Duende IdentityServer consists of several tasks: updating the target .NET version, updating NuGet packages, and performing database schema migrations.
Step 1: Update the .NET Version
Section titled “Step 1: Update the .NET Version”In this guide, we’ll update to .NET 8 LTS. If your application targets a newer .NET version, you can use that newer version for the IdentityServer host as well.
Update the IdentityServer target framework:
<TargetFramework>netcoreapp3.1</TargetFramework><TargetFramework>net8.0</TargetFramework>
Some of your project dependencies may need updating as part of changing the target framework.
For example, Microsoft.EntityFrameworkCore.SqlServer
and Microsoft.AspNetCore.Authentication.Google
will need to be updated to the version matching your target framework.
Step 2: Update NuGet Packages
Section titled “Step 2: Update NuGet Packages”Update the IdentityServer4 dependencies in your IdentityServer host project to Duende IdentityServer.
<PackageReference Include="IdentityServer4" Version="4.1.2" /><PackageReference Include="Duende.IdentityServer" Version="7.2.0" />
You’ll need to make a similar change for all IdentityServer4 packages, including IdentityServer4.EntityFramework
and IdentityServer4.AspNetIdentity
. For example:
<PackageReference Include="IdentityServer4.EntityFramework" Version="4.1.2" /><PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.2.0" />
The IdentityModel package was renamed to Duende IdentityModel, and needs updating if you reference it directly:
<PackageReference Include="IdentityModel" Version="x.y.z" /><PackageReference Include="Duende.IdentityModel" Version="7.0.0" />
Step 3: Update Namespaces
Section titled “Step 3: Update Namespaces”In your project source code, replace all IdentityServer4
namespace usages with Duende.IdentityServer
:
using IdentityServer4;using IdentityServer4.Models;using Duende.IdentityServer;using Duende.IdentityServer.Models;
Replace all IdentityModel
namespace usages with Duende.IdentityModel
:
using IdentityModel;using Duende.IdentityModel;
If you are using fully-qualified names in your code, those will need to be updated as well.
Step 4: Remove AddDeveloperSigningCredential Optional
Section titled “Step 4: Remove AddDeveloperSigningCredential ”OptionalIn your application startup code, typically found in the ConfigureServices
method in Startup.cs
, consider removing AddDeveloperSigningCredential
.
You can use Duende IdentityServer’s built-in manual or automatic key management instead.
Step 5: Update Database Schema
Section titled “Step 5: Update Database Schema”Whether you are using a database or a custom store implementation for your configuration and operational data, you’ll need to make some changes. The exact steps involved in updating your data store will depend on your implementation details.
In this section, we’ll look at updating the database schema based on the stores provided in the Duende.IdentityServer.EntityFramework
package:
-
Create a new
Keys
table for the automatic key management feature in the operational database. -
Create a new
RequireResourceIndicator
boolean column on theApiResources
table in the configuration database. -
Create a new index on the
ConsumedTime
column in thePersistedGrants
table (more details). -
Create a new table called
IdentityProviders
for storing the OIDC provider details (more details). -
Add missing columns for created, updated, etc. to EF entities (more details).
-
Add unique constraints to EF tables where duplicate records are not allowed (more details).
-
The server-side sessions feature requires a new table (more details).
-
The session coordination feature adds a column to the
Clients
table (more details). -
Improve primary keys on the persisted grants table (more details).
-
Add new properties to the
Duende.IdentityServer.Models.Client
model:InitiateLoginUri
is a nullable string used for Third Party Initiated Login.RequireDPoP
is a non-nullable boolean flag that controls if a client is required to use DPoP.DPoPValidationMode
is a non-nullable column that controls the DPoP validation mechanism. Existing clients that are not using DPoP can set its value to0
.DPoPClockSkew
is a non-nullable timespan that controls how much clock skew is allowed for a particular DPoP client. Existing clients that are not using DPoP can set its value to a timespan of length “0.
-
Two new properties have been added to the
Client
model:Client.RequirePushedAuthorization
is a new boolean property that controls if this client requires pushed authorization requests (PAR). It is safe to initialize this column tofalse
for existing clients, which will mean that the global PAR 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. It is safe to initialize this column tonull
for existing clients, which means the global value is used.
-
A new
PushedAuthorizationRequest
table has been added to store pushed authorization requests.
You’ll need to create two database migrations that update the database schema: one that targets the PersistedGrantDbContext
(for operational data), and one that targets the ConfigurationDbContext
(for configuration data).
Note that you may want to change the database migration paths in the examples below to match your project structure.
-
Create the migrations for the operational and configuration database context:
Terminal dotnet ef migrations add UpdateToDuende_v7_0 -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDbdotnet ef migrations add UpdateToDuende_v7_0 -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb -
Apply the migrations to your database:
Terminal dotnet ef database update -c PersistedGrantDbContextdotnet ef database update -c ConfigurationDbContext
Step 6: Migrate Signing Keys Optional
Section titled “Step 6: Migrate Signing Keys ”OptionalIf your IdentityServer4 implementation is using a signing key, consider using automatic key management which is included in the Business license.
Client apps and APIs typically cache the key material published from IdentityServer’s discovery document. When upgrading, consider how those applications will handle an upgraded token server with a new and different signing key.
- If you can restart all client apps and APIs that depend on your current signing key, you can remove the old signing key and start to use automatic key management. A restart reloads the discovery document and the new signing key.
- If you can not restart client apps and APIs, check the manual and automatic key rotation topics to learn how to announce new signing key material while still supporting the old signing key for a period of time.
Step 7: Validate Your Deployment
Section titled “Step 7: Validate Your Deployment”Congratulations! Your upgrade is complete.
Make sure to validate and test your Duende IdentityServer is working as expected, and check integrations with your client applications and APIs.
Breaking Changes
Section titled “Breaking Changes”Duende IdentityServer is a Software Development Kit (SDK) that you can use to build your own identity and access management solutions (IAM). Being an SDK, there is a lot of potential for customization during the implementation. Depending on your specific project, breaking changes might affect your use of IdentityServer.
As part of your upgrade from IdentityServer4 to Duende IdentityServer, we recommend reviewing these breaking changes to understand if any of them affect you.
- Quickstart UI updated to use Razor Pages
- Addition of cancellation token to store APIs
- Store DbContext constructors to support DbContext pooling
- CustomRedirectResult returnUrl changes
- Add missing columns for created, updated, etc to EF entities
- Add unique constraints to EF tables where duplicate records not allowed
- Added grant handle versioning suffix
- Many HttpContext extensions marked obsolete
- New APIs to the ICache interface
- New Client columns for CIBA
- openid no longer implicit in OidcProvider scope collection
- JwtRequestValidator signature changes
- Changes in AppAuth URL validator for logout
- Use of EmailClaimType option in ASP.NET Identity integration
ClientConfigurationStore
now usesIConfigurationDbContext
to allow for customization. If you have a customized Entity Framework Core-based store, you may need to update your constructors.