diff --git a/docs/en/release-info/migration-guides/abp-10-7.md b/docs/en/release-info/migration-guides/abp-10-7.md index 9b586204e1..1b377aa848 100644 --- a/docs/en/release-info/migration-guides/abp-10-7.md +++ b/docs/en/release-info/migration-guides/abp-10-7.md @@ -151,7 +151,7 @@ Add a navigation property to the principal entity if you rely on the previous be - 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`, which each provider satisfies with its own concrete options class, and `ILogger` instead of `IOptions` and `ILogger>`. 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, and now load the key ring on startup and create one if the store is empty. 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. +- `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** diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs index 303f6d84b0..175d7fc36e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs @@ -185,9 +185,11 @@ public abstract class AbpSingleActiveTokenProvider : IUserTwoFactorTokenProvider return false; } } - catch (Exception ex) + catch (Exception) { - Logger.LogDebug(ex, "Could not read the '{ProviderName}' token. It was protected under another provider name, key ring or application name, or the payload is not a token this provider produced.", Options.Name); + // Without the exception, the way ASP.NET Core's provider logs this: it carries the key id and + // the key ring location. + Logger.LogDebug("Could not read the '{ProviderName}' token. It was protected under another provider name, key ring or application name, or the payload is not a token this provider produced.", Options.Name); return false; } } diff --git a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/CrossHostTokenProvider_Tests.cs b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/CrossHostTokenProvider_Tests.cs index 31a9ed7e07..e5ba40c0d9 100644 --- a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/CrossHostTokenProvider_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/CrossHostTokenProvider_Tests.cs @@ -270,6 +270,11 @@ public class CrossHostTokenProvider_Tests .ShouldBe(typeof(OtherUserTokenProvider)); var userManager = sp.GetRequiredService(); + + // What the map says and what the manager uses come apart here. + userManager.FindTokenProvider(AbpPasswordResetTokenProvider.ProviderName) + .ShouldBeOfType(); + var user = await userManager.GetByIdAsync(userId); await userManager.GeneratePasswordResetTokenAsync(user);