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.
- Replace FormattedStringValueExtracter.Extract with LastIndexOf in
PermissionGrantCacheItem and ResourcePermissionGrantCacheItem to
eliminate repeated string tokenization and object allocations on
every cache key parse (~12,000 calls per request with 4000+ permissions)
- Add fast-path in SimpleStateCheckerManager.InternalIsEnabledAsync to
skip DI scope creation when both StateCheckers and GlobalStateCheckers
are empty, avoiding thousands of unnecessary scope allocations
- Optimize PermissionChecker.IsGrantedAsync(string[]) and
ResourcePermissionChecker.IsGrantedAsync(string[], resourceName, resourceKey)
to load all permission definitions once via GetPermissionsAsync /
GetResourcePermissionsAsync instead of N individual GetOrNullAsync calls,
and use batch StateCheckerManager.IsEnabledAsync for state checking
- Optimize AbpApplicationConfigurationAppService.GetAuthConfigAsync to
pre-load all permission names into a HashSet for O(1) lookup instead
of N async GetOrNullAsync calls inside the loop
- Fix GetResourcePermissionsAsync to deduplicate by (ResourceName, Name)
instead of Name only, matching the actual uniqueness constraint of
resource permissions defined in PermissionDefinitionContext
Production impact (customer with 4000+ permissions): 10s+ -> ~682ms
Remove the ownership-based fallback that allowed post creators to delete their own posts in Detail.cshtml. Deletion now strictly requires BloggingPermissions.Posts.Delete, centralizing authorization on explicit permissions to enforce consistent access control.
Wrap JsonDocument usage in explicit using blocks for anonymous job handlers to ensure deterministic disposal and avoid capturing/disposal issues. Also rename the compile-time handler parameter from `context` to `ctx` to prevent potential shadowing. Changes applied to DemoAppSharedModule.cs and SampleJobCreator.cs.
Introduce AnonymousJobExecutionContext and switch anonymous job handler APIs to accept it (Func<AnonymousJobExecutionContext, CancellationToken, Task> / Action<AnonymousJobExecutionContext, CancellationToken>). Update AbpBackgroundJobOptions, IAnonymousJobHandlerRegistry, AnonymousJobHandlerRegistry and AnonymousJobExecutorAsyncBackgroundJob to use the new context and to obtain a cancellation token via ICancellationTokenProvider. Update all callsites (tests, demo module, sample job creator) and documentation to show registering/enqueuing anonymous handlers by name and explain Hangfire display behavior. Also add a .cursor hooks state file. The demo no longer skips enqueuing anonymous jobs for RabbitMQ in this change.
Make anonymous job JSON parsing more robust and fix DI registration for Quartz adapter. Add TryGetJobNameElement to accept both "JobName" and "jobName"; update anonymous job handlers to dispose JsonDocument and to prefer lowercase "value" with a fallback to "Value" for compatibility with different serializers. Also register a non-generic QuartzJobExecutionAdapter in DI and remove an extraneous BOM from a using line.
Introduce anonymous/background-job-by-name support: add AnonymousJobArgs, IAnonymousJobHandlerRegistry and AnonymousJobHandlerRegistry, and an AnonymousJobExecutorAsyncBackgroundJob to execute JSON-based anonymous handlers. AbpBackgroundJobOptions now stores anonymous handlers and exposes registration/query helpers. Updated background job managers (Default, Hangfire, Quartz, RabbitMQ, TickerQ) to wrap registered anonymous jobs into AnonymousJobArgs when enqueuing and to depend on the handler registry and JSON serializer where needed. Removed the old dynamic handler types/APIs (DynamicBackgroundJobContext, IDynamicBackgroundJobHandlerProvider, DynamicBackgroundJobHandlerProvider) and related dynamic handling code; BackgroundJobConfiguration simplified (JobType non-nullable) and JobExecutionContext no longer carries JobName. Tests and demo code updated to use anonymous handlers and tracking. This change centralizes runtime-registered handlers keyed by job name and standardizes anonymous job payloads as JSON.
Introduce dynamic background job support so jobs can be registered and executed without a compile-time job type. Key changes:
- Add DynamicBackgroundJobContext, IDynamicBackgroundJobHandlerProvider and DynamicBackgroundJobHandlerProvider to allow registering/unregistering dynamic handlers at runtime.
- Extend BackgroundJobConfiguration with DynamicHandler and IsDynamic, and make JobType nullable for dynamic scenarios.
- Update AbpBackgroundJobOptions to use a ConcurrentDictionary for name lookup, and add methods to Add/Remove dynamic jobs and GetJobOrNull.
- Extend JobExecutionContext with JobName and propagate it through Hangfire/Quartz/RabbitMQ/TickerQ adapters and worker code.
- Update BackgroundJobExecuter to detect and execute dynamic handlers, deserialize/ensure dictionary args, and retain existing typed execution path.
- Add tests (DynamicJobExecutionTracker, runtime/compile-time dynamic handler tests) and register a sample dynamic job in test module.
- Update demo SampleJobCreator and DemoAppSharedModule to demonstrate compile-time and runtime dynamic job registration and enqueueing.
These changes enable flexible, dictionary-based job arguments and runtime registration of background job handlers while preserving existing typed job execution.