Look up single-active token providers through IdentityUserManager
- The public ProviderType is the last type registered under a key, not the one the manager uses
- Add cross-host coverage for a second user type and for both hosts opting out
Each method removes the stored hash under `"[AbpSingleActiveToken]"` for the corresponding purpose. Validation afterwards returns `false` even if the token blob itself is still within its DataProtector lifespan and the `SecurityStamp` is unchanged. They throw an `AbpException` when they cannot see an `AbpSingleActiveTokenProvider` on the key: with the ABP providers turned off there is no stored hash to remove, and a token that was never single-active cannot be revoked this way. A key that also carries a provider registered for a second user type reads as the same case, because the one `IdentityUserManager` picks out of it is not visible to these helpers.
Each method removes the stored hash under `"[AbpSingleActiveToken]"` for the corresponding purpose. Validation afterwards returns `false` even if the token blob itself is still within its DataProtector lifespan and the `SecurityStamp` is unchanged. They throw an `AbpException` when the key is not served by an `AbpSingleActiveTokenProvider`: with the ABP providers turned off there is no stored hash to remove, and a token that was never single-active cannot be revoked this way.
For tokens issued by `AbpDefaultTokenProvider` (e.g. `RequiresTwoFactor`, `ShouldChangePasswordOnNextLogin`, `PeriodicallyChangePassword`), call `UserManager.RemoveAuthenticationTokenAsync` directly. The name is built from the provider's options `Name`, which is `TokenOptions.DefaultProvider` unless you changed it:
@ -149,7 +149,7 @@ Add a navigation property to the principal entity if you rely on the previous be
- 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 they cannot see an `AbpSingleActiveTokenProvider` on the key. That is the case once the ABP providers are turned off, where there is no stored hash to remove and a token that was never single-active cannot be revoked this way. It is also the case when the key carries a provider for a second user type, which `IdentityUserManager` skips and these helpers cannot: they read the key's public `ProviderType`, while the one the manager picks is only reachable through an internal ASP.NET Core API.
- `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, 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.