Filter by title

ABP Version 10.7 Migration Guide

This document is a guide for upgrading ABP v10.6 solutions to ABP v10.7. This version includes explicitly marked migration-impacting changes for specific customization scenarios, while most applications can upgrade with no additional action beyond the notes below.

Package Version Changes: Before upgrading, review the Package Version Changes document to see version changes on dependent NuGet and NPM packages and align your project with ABP's internal package versions.

Open-Source (Framework)

This version contains the following changes on the open-source side:

Constructor Changes

Who is affected

  • Applications that derive from the services below, replace them with a derived class, or construct them manually.

What changed

These services take new constructor dependencies. There is no overload with the previous signature, so a derived class does not compile until its constructor is updated:

  • IdentityUserManager takes a new IUnitOfWorkManager.
  • EfCoreIdentitySessionRepository and MongoIdentitySessionRepository take a new IClock.
  • BlazorServerCurrentApplicationConfigurationCacheResetService takes a new ApplicationConfigurationChangedService.

IdentityUserManager also writes the user's last sign-in time as a best-effort update in its own unit of work now, so the value is saved after the sign-in unit of work completes instead of within it.

What to do

Add the new parameters to your derived constructors and pass them to the base constructor.

See #25905 for details.

Blazor Antiforgery Middleware Order

Who is affected

  • Blazor applications that call UseAntiforgery() before UseAuthorization(). This covers the solutions generated by the classic templates before v10.7 and the solutions generated by the current modern solution templates.

What changed

  • ASP.NET Core requires UseAntiforgery() to be called after UseAuthentication() and UseAuthorization().
  • The classic Blazor startup templates now use that order. Existing solutions keep the middleware order they were generated with, so they are not updated by the package upgrade.

What to do

Check the OnApplicationInitialization method of your Blazor module and move the calls into this order:

app.UseAuthentication();
// ...
app.UseAuthorization();
app.UseAntiforgery();

See #25874 for details.

Angular Resource API Proxies

Who is affected

  • Angular applications that regenerate their service proxies with the new --resource-api option.

What changed

  • With --resource-api, every generated GET member returns an rxResource-based ResourceRef instead of an Observable, and takes its parameters as a single Signal. A parameterless endpoint has no signal parameter, and the optional request configuration stays a normal argument. The other HTTP methods are unchanged.
  • The option is off by default, so a regular proxy regeneration keeps the current output.

What to do

Only pass --resource-api when the components that consume the GET methods are ready for the resource form. It requires Angular v22 or later.

See the Angular Service Proxies document and #25761 for details.

BLOB Encryption and Content Pipeline

Who is affected

  • Applications that enable BLOB encryption for a container that already stores plaintext BLOBs.
  • Applications that add, remove, or reorder transforming BLOB pipeline contributors for a container that already stores BLOBs.

What changed

  • ABP v10.7 adds opt-in BLOB encryption at rest and a configurable content pipeline.
  • Encryption and pipeline contributors are disabled by default, so existing containers are not affected unless you enable or configure them.
  • Encrypted BLOBs require the same passphrase that was used to write them.
  • A transforming pipeline contributor becomes part of the stored BLOB format.

What to do

No action is required if you do not enable encryption or configure transforming pipeline contributors.

If you enable encryption for a container that already contains plaintext BLOBs:

  1. Enable encryption with allowLegacyPlainText: true and configure a passphrase, either for the container or globally with AbpBlobStoringEncryptionOptions.DefaultPassPhrase.
  2. Re-save every existing BLOB through the container.
  3. Remove allowLegacyPlainText after the migration, so reads fail closed for plaintext content.
Configure<AbpBlobStoringOptions>(options =>
{
    options.Containers.Configure<ProfilePictureContainer>(container =>
    {
        container.UseEncryption(allowLegacyPlainText: true);
    });
});

If you change transforming pipeline contributors or an encryption passphrase, read and export the existing BLOBs while the old configuration is active, apply the new configuration, and save the content back. Do not change an encryption passphrase in place before migrating the existing BLOBs.

See the BLOB Encryption and BLOB Content Pipeline documents and #25836 for details.

Aggregate Root Update on Foreign Key Only Relations

Who is affected

  • Applications with a relation that has no navigation property on the principal side, such as HasOne<T>().WithMany(), where the principal entity is tracked while one of its dependents is added, modified or deleted.

What changed

  • ABP treated any dependent change as a navigation change of the principal, so the principal was updated (ConcurrencyStamp, audit properties) and its entity updated event was published, even if the principal had no navigation for that relation. Concurrent changes on such dependents conflicted on the principal row and failed with AbpDbConcurrencyException.
  • The principal is now updated only when it really has a navigation property for the changed relation. Relations with navigation properties keep their current behavior.
  • IUnitOfWork.DisableUpdateAggregateRootWhenNavigationChanges() is added to disable updating the aggregate root for a unit of work, without disabling its entity updated event. It overrides the AbpEntityChangeOptions.UpdateAggregateRootWhenNavigationChanges option.

What to do

Add a navigation property to the principal entity if you rely on the previous behavior, or update the principal in your own code.

See #25937 for details.

Identity Token Providers Moved to the Domain Layer

Who is affected

  • Solutions where password reset, email confirmation or change email tokens are generated on one host and validated on another, such as a separate authentication server or a microservice solution.
  • Hosts that load only the Identity domain layer and relied on the ASP.NET Core Identity providers for the Default, Email, Phone or AbpLinkUser keys.
  • Applications that derive from AbpSingleActiveTokenProvider, AbpTwoFactorTokenProvider or from one of the ABP token providers, or that call IdentityUserManagerSingleActiveTokenExtensions.
  • Applications that treat one of the ABP token provider options classes as a DataProtectionTokenProviderOptions.

What changed

  • The ABP token providers and their options moved from the Volo.Abp.Identity.AspNetCore assembly to Volo.Abp.Identity.Domain, out of the Volo.Abp.Identity.AspNetCore namespace and into Volo.Abp.Identity, and are now registered by AbpIdentityDomainModule instead of AbpIdentityAspNetCoreModule. An assembly compiled against an earlier version and not rebuilt cannot resolve them. The types that stay in the ASP.NET Core layer, such as AbpSignInManager and AbpIdentityAspNetCoreOptions, keep their namespace.
  • Every host that loads AbpIdentityDomainModule therefore resolves the same providers, unless it registers something else itself. Previously the providers were only registered on hosts loading AbpIdentityAspNetCoreModule, so a host that generated a token could end up on a different provider than the host that validated it, and the link was rejected as an invalid token.
  • AbpSingleActiveTokenProvider no longer derives from ASP.NET Core's DataProtectorTokenProvider<IdentityUser>; it implements IUserTwoFactorTokenProvider<IdentityUser> and re-implements the same protected payload, so the token format is unchanged. Code that casts a provider to DataProtectorTokenProvider<IdentityUser> or uses it as a generic constraint no longer compiles, and the Logger property the old base class exposed publicly is now protected.
  • The provider options classes derive from AbpDataProtectionTokenProviderOptions instead of DataProtectionTokenProviderOptions. The Name and TokenLifespan properties are unchanged, but code that assigns one of them to DataProtectionTokenProviderOptions, passes it to a method taking that type, returns it, or uses it as a generic constraint no longer compiles.
  • IdentityUserManagerSingleActiveTokenExtensions moved with the providers, and its Remove*TokenAsync helpers changed in two ways. They now follow the provider's options Name instead of the key it is registered under, so an application that renamed a provider gets the hash it actually wrote removed. And they throw an AbpException instead of reporting success when the key is not served by an AbpSingleActiveTokenProvider, which is the case once the ABP providers are turned off: there is no stored hash to remove then, and a token that was never single-active cannot be revoked this way.
  • The constructors changed accordingly. AbpSingleActiveTokenProvider takes IOptions<AbpDataProtectionTokenProviderOptions>, which each provider satisfies with its own concrete options class, and ILogger<AbpSingleActiveTokenProvider> instead of IOptions<DataProtectionTokenProviderOptions> and ILogger<DataProtectorTokenProvider<IdentityUser>>. The five DataProtector-based providers (AbpDefaultTokenProvider, AbpPasswordResetTokenProvider, AbpEmailConfirmationTokenProvider, AbpChangeEmailTokenProvider, LinkUserTokenProvider) take the new logger type as well. The email and phone 2FA providers moved unchanged. Constructing a provider by hand also behaves differently at the edges: a null options now throws instead of falling back to the ASP.NET Core defaults, which carry the wrong provider name, and a null logger falls back to NullLogger instead of throwing.
  • AbpIdentityDomainModule calls AddDataProtection(), because UserManager instantiates every provider in Tokens.ProviderMap when it is resolved and the DataProtector-based providers need IDataProtectionProvider. Hosts that never issue a token, such as a DbMigrator console application, do not register it themselves. Data Protection registers a hosted service that loads the key ring when the host starts and creates one if the store is empty, so such a host now does that too. That is a side effect for such a host, not a reason to configure it: only a host that generates or validates a token needs the same key ring and SetApplicationName as the rest of the solution.

What to do

  • Replace using Volo.Abp.Identity.AspNetCore; with using Volo.Abp.Identity; where you use one of the moved types, or add the second one when the file also uses a type that stayed behind.
  • Rebuild the solution. No further action is required for the common case: configuring the options and the provider names works exactly as before, and the compiler points at the source level changes below. The assembly move is not one of them, so a package you depend on that was built against the old assembly has to be rebuilt and republished against v10.7 as well.
  • If you derive from one of the five DataProtector-based providers, change the logger parameter to ILogger<AbpSingleActiveTokenProvider>.
  • If you derive from AbpSingleActiveTokenProvider directly, give your provider its own options class deriving from AbpDataProtectionTokenProviderOptions and inject IOptions<YourTokenProviderOptions>, the way the built-in providers do. IOptions<T> is covariant, so it satisfies the base constructor. Do not inject IOptions<AbpDataProtectionTokenProviderOptions>: the base options class is abstract and the options system cannot create it.
  • If a host of yours registered these providers by hand, you can drop that code. IdentityBuilder.AddAbpTokenProviders() is public if you want to call it explicitly.
  • Remove an AddDefaultTokenProviders() call a domain-only host made for itself, unless you turn the ABP providers off everywhere. Application actions run after the framework registration, so that call now takes the Default, Email and Phone keys back to the ASP.NET Core providers while the other keys stay on the ABP ones, and a host that keeps it no longer agrees with one that does not.
  • Tell the users of a domain-only host to request a new link. The tokens it issued through the stock providers before the upgrade are rejected afterwards.
  • Move a Configure<DataProtectionTokenProviderOptions> a domain-only host relied on to the corresponding ABP options class, on every host that validates a token. It stops applying there, the way it stopped applying on hosts loading AbpIdentityAspNetCoreModule in v10.2.
  • Give a domain-only host write access to the shared identity database. It now stores a user token for every token it generates through these providers. The Authenticator provider is unchanged and stores nothing.
  • To keep the ASP.NET Core Identity providers instead, turn the ABP ones off and register them yourself, on every host that generates or validates a token:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
    PreConfigure<AbpIdentityTokenProviderOptions>(options =>
    {
        options.UseAbpTokenProviders = false;
    });

    PreConfigure<IdentityBuilder>(builder =>
    {
        builder.AddDefaultTokenProviders();
    });
}

Both have to be PreConfigure, not Configure. The flag turns off the registration itself on every host alike, so the Identity module registers no token provider at all and a flow whose key has no provider throws NotSupportedException on the first call. AddDefaultTokenProviders() covers the Default, Email, Phone and Authenticator keys, and nothing covers the AbpLinkUser key either way.

See the Identity Token Providers document for details.

OpenIddict Client Assertion Audience Validation

Who is affected

  • Applications that authenticate OpenIddict clients with client assertions, that is private_key_jwt or client_secret_jwt, and use the same client signing key on more than one OpenIddict authorization server.

What changed

  • OpenIddict is upgraded from 7.5.0 to 7.7.0, which fixes GHSA-925x-4h4v-2792. The server stack compared only the path of the aud claim with the issuer of the authorization server, so a client assertion minted for one server was accepted by another one that shared the client's signing key.
  • The audience of a client assertion still has to be the issuer URI of the server that receives it, with or without the trailing slash, so an assertion that was already addressed to the right server keeps working. A correctly configured client needs no change.
  • A single authorization server instance, or several instances with their own cryptographic material, was never affected.

What to do

Upgrade the packages. There is no configuration change.

Until you can upgrade, give every authorization server its own client signing key, so an assertion cannot be replayed across them.

Tenant of Dynamic Background Jobs and Events

Who is affected

  • Applications that enqueue jobs through IDynamicBackgroundJobManager with a runtime registered handler.
  • Applications that publish string named dynamic events and handle them after an outbox or a message broker.
  • Applications that derive from DefaultDynamicBackgroundJobManager, or that override DistributedEventBusBase.AddToInboxAsync or AzureDistributedEventBus.PublishAsync(string, object).

What changed

  • A dynamic handler job now runs under the tenant that enqueued it. DynamicBackgroundJobArgs implements IMultiTenant and DefaultDynamicBackgroundJobManager fills it from ICurrentTenant, so the existing BackgroundJobExecuter restores the tenant before the handler runs. Previously the transport args carried no tenant and the job ran under the host, because the worker thread has none.
  • A string named dynamic event now runs under the tenant that published it. Its payload is arbitrary user data with no place for a tenant, so the tenant travels on the transport as the X-Tenant-Id header, next to the correlation id, and the message body is unchanged. Typed events keep carrying their tenant in the event data itself and are not touched: no header is written for them and their handlers behave exactly as before.
  • DefaultDynamicBackgroundJobManager takes a new ICurrentTenant dependency, and AddToInboxAsync and the Azure PublishAsync(string, object) take a new optional tenant parameter. A derived class does not compile until its constructor or its override is updated.
  • A tenant header that is present but is not a GUID fails the message instead of handling it under the host.
  • Dapr is unchanged, because it does not support dynamic event subscriptions.

What to do

  • Add the new parameters to your derived constructors and overrides.
  • Review the dynamic handlers you register at runtime. A handler that reads host wide data while the job is enqueued from a tenant request now sees that tenant's data. Wrap such a handler in using (CurrentTenant.Change(null)) if it has to run under the host, or enqueue the job inside the same scope.
  • Drain or re-enqueue the jobs and the events that are still waiting from before the upgrade. They carry no tenant and keep running under the host.
  • Upgrade the consumers before the producers in a tiered or microservice solution. A message published by an upgraded producer loses its tenant on a consumer that is not upgraded yet, the same way it did before the upgrade.

See #26148 for details.

Dependency Updates

Who is affected

  • Applications that pin any of the packages below in their own .csproj files. The startup templates pin several of them, so a solution generated before v10.7 has them.
  • Applications that use EF Core with MySQL, or directly reference MudBlazor or Blazorise.

What changed

  • The Microsoft.AspNetCore.*, Microsoft.EntityFrameworkCore.*, Microsoft.Extensions.* and System.* packages are upgraded from 10.0.9 to 10.0.11, and the Microsoft.IdentityModel.* packages and System.IdentityModel.Tokens.Jwt from 8.19.1 to 8.19.2. OpenIddict 7.7.0 requires these versions.
  • Microsoft.AspNetCore.DataProtection is added as a direct dependency.
  • MudBlazor is upgraded from 9.4.0 to 9.7.0, and the Blazorise packages from 2.2.1 to 2.3.0.
  • MySql.EntityFrameworkCore is upgraded from 10.0.1 to 10.0.9.
  • Npgsql.EntityFrameworkCore.PostgreSQL is upgraded from 10.0.0 to 10.0.3.
  • MongoDB.Driver is upgraded from 3.10.0 to 3.11.2, and AWSSDK.S3 and AWSSDK.SecurityToken to their current versions.
  • ABP maps Guid[] query parameters through its own type mapping plugin on MySQL, and stores IdentityUserPasskey.Data as a serialized json column, because the MySQL providers support neither EF Core JSON columns nor primitive collections. The column name and the stored content stay the same.

What to do

Upgrading the ABP packages does not move the versions you pin yourself, and abp update rewrites Volo.* references only. A reference that stays below what ABP requires fails the restore, so align those versions in your own .csproj files.

These are the ones a solution generated by the startup templates hits:

Package Align to
Microsoft.AspNetCore.Authentication.OpenIdConnect 10.0.11
Microsoft.AspNetCore.Components.WebAssembly 10.0.11
Microsoft.AspNetCore.Components.WebAssembly.Authentication 10.0.11
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 10.0.11
Microsoft.EntityFrameworkCore 10.0.11
Microsoft.Extensions.FileProviders.Embedded 10.0.11
System.IdentityModel.Tokens.Jwt 8.19.2

A solution that pins other packages of these families can hit the same error on them, so see the Package Version Changes document for the full list and align the rest as well, to keep your solution on one set of versions.

Review direct MudBlazor and Blazorise references, align them with ABP's package versions, and re-test customized components and forms after upgrading.

If you use the MySQL provider, align your own MySql.EntityFrameworkCore reference with ABP's version.

Pro

This version contains the following changes on the PRO side:

Identity Pro

Who is affected

  • Applications that derive from Volo.Abp.Identity.IdentityProUserManager, replace it with a derived class, or construct it manually.
  • Applications that use Identity Pro with MySQL.

What changed

  • IdentityProUserManager takes the new IUnitOfWorkManager dependency, like its base class.
  • UserInvitation.AssignedRoles is stored as a serialized json column on MySQL, because the MySQL providers do not support primitive collections. The column name and the stored content stay the same.

What to do

Add the parameter to your derived constructor and pass it to the base constructor.

AI Management Web Page Data Sources

Who is affected

  • Applications that use the AI Management module with Entity Framework Core.
  • Applications that implement IWorkspaceDataSourceAppService or IWorkspaceAppService, or derive from WorkspaceDataSourceAppService or WorkspaceAppService.

What changed

  • Workspace data sources can be created from a web page URL, so the WorkspaceDataSource entity has the new SourceType, SourceUrl, SourceTitle, and FetchedAt properties.
  • IWorkspaceDataSourceAppService has the new CreateFromUrlAsync and RefreshFromUrlAsync methods, and IWorkspaceAppService has the new GetModelsAsync overloads.
  • WorkspaceDataSourceAppService takes the new IWebPageMarkdownConverter dependency and WorkspaceAppService takes the new IEnumerable<IAIProviderModelCatalog> dependency.

What to do

Create a new EF Core migration and apply it to your database after upgrading.

If you implement these interfaces yourself, implement the new methods. If you derive from the application services, add the new parameters to your constructors.

ABP Suite Extensibility

Who is affected

  • Applications or tools that implement ICodeFormatter, or that derive from, replace, or manually construct CrudPageGenerator.

What changed

  • ICodeFormatter has the new FormatTypescriptCodeAsync member.
  • CrudPageGenerator takes the new ReactUiGenerateCommand dependency.

What to do

Implement the new formatter method in your own ICodeFormatter, and pass the new parameter to the CrudPageGenerator constructor.

Contributors


Last updated: September 14, 2026 Edit this page on GitHub

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.