Introduce AbpDefaultTokenProvider (10-min lifespan) replacing
DataProtectorTokenProvider under TokenOptions.DefaultProvider, so
challenge tokens (RequiresTwoFactor, ShouldChangePassword) become
single-active.
Document the token provider lineup and fix two factual errors in
the 2FA doc.
- SetLinkConsentAsync uses FindByIdAsync + null no-op, matching the
missing-user behaviour of Get/RemoveLinkConsentAsync.
- Decorate Set/Get/RemoveLinkConsentAsync with [UnitOfWork] so the
underlying IdentityUserStore.GetTokenAsync can EnsureCollectionLoaded
the user.Tokens collection (fixes the CI failure on
GetLinkConsentAsync_Should_Return_Null_When_No_Consent_Written).
- Clarify LinkUserTokenProvider XML doc to note that the single-active
policy is enforced per purpose (matches AbpSingleActiveTokenProvider).
- Rename LinkUserTokenProvider_Should_Be_Register to *_Registered for
consistency with sibling token-provider tests.
- Add tests covering: (a) RemoveLinkUserTokenAsync(purpose) only
invalidates the requested purpose, and (b) same-purpose GenerateLink
TokenAsync invalidates the previously issued token.
- Use UserManager.SetAuthenticationTokenAsync/GetAuthenticationTokenAsync/
RemoveAuthenticationTokenAsync so the Tokens collection is loaded via
IdentityUserStore.EnsureCollectionLoadedAsync instead of relying on the
GetByIdAsync(includeDetails) convention.
- Add a RemoveLinkUserTokenAsync(manager, user, purpose) overload to invalidate
tokens issued for purposes other than LinkUserTokenPurpose.
- Add a cross-UnitOfWork persistence test for SetLink/Get/Remove ConsentAsync.
- Drop the unused IdentityLinkUserRepository field from LinkUserTokenProvider_Tests.
- Make LinkUserTokenProvider derive from AbpSingleActiveTokenProvider
- Add AbpLinkUserTokenProviderOptions with 10 min default lifespan
- Add RemoveLinkUserTokenAsync extension on IdentityUserManager
- Add SetLinkConsentAsync/GetLinkConsentAsync/RemoveLinkConsentAsync on IdentityLinkUserManager backed by user.Tokens slot [AbpLinkUserConsent]/Consent
- MudMenu: switch ActivatorContent to MenuContext.ToggleAsync pattern (v9 breaking)
- MudForm: rename Validate to ValidateAsync (v9.1 obsoletion)
- MudInput: replace AutoGrow with Sizing for textarea
- DataGrid: add white-space:nowrap on header to prevent CJK characters stacking vertically
- BlockUI: shrink loading spinner inside mud-dialog
- Search field: switch Label to Placeholder so the text shows next to the magnifier icon
Replaces the TOTP-based Email/Phone 2FA providers under
TokenOptions.DefaultEmailProvider / DefaultPhoneProvider with
DataProtector-backed single-use equivalents.
- Encrypt the 6-digit code via IDataProtector (purpose chain isolated per
provider + token purpose), store ciphertext + absolute UTC expiration
(unix seconds) in the user token table
- Remove the stored entry on successful validation (true single-use)
- Concurrency race (ConcurrencyStamp failure) returns false instead of 500
- Configurable TokenLifespan (default 3 minutes) via Options
AbpSingleActiveTokenProvider.GenerateAsync now checks the IdentityResult
from UserManager.UpdateAsync so a silent persistence failure no longer
returns a token that was not saved.
Related to #25314.
Switching CurrentTenant to user.TenantId in PasswordSignInAsync without refreshing IdentityOptions meant that lockout, password policy and other tenant-scoped options used host values during the base sign-in call. Call IdentityOptions.SetAsync inside the tenant switch so downstream checks use the user's tenant configuration.
- Override IdentityUserManager.FindByIdAsync to fall back to a cross-tenant lookup in shared user sharing strategy so any caller that hits FindByIdAsync from a non-matching tenant context (including base SignInManager internals for TwoFactorSignInAsync and TwoFactorRecoveryCodeSignInAsync) can still resolve a tenant user by id
- Drop the now-redundant AbpSignInManager.GetTwoFactorAuthenticationUserAsync override; the base implementation works automatically through the new FindByIdAsync behavior
- Cover the new FindByIdAsync behavior with unit tests
Guards against regressing the data-access contract behind the 2FA redirect bug: login must find a tenant user by user name from a host context, and the 2FA mid-flow must then resolve the same tenant user by id from the same host context.
Exercises the full cookie round-trip: writes a TwoFactorUserId cookie carrying a tenant user id, then verifies that AbpSignInManager.GetTwoFactorAuthenticationUserAsync returns the tenant user when CurrentTenant is null.
- Add IdentityUserManager.FindSharedUserByIdAsync to resolve a user by id across tenants in shared user sharing strategy
- Override AbpSignInManager.GetTwoFactorAuthenticationUserAsync to use it so the 2FA mid-flow can still find a tenant-scoped user when CurrentTenant is host
- Cover the new method with unit tests