Skip to content

Implement single active token providers for email change, email confirmation, and password reset#24926

Merged
EngincanV merged 3 commits into
devfrom
AbpSingleActiveTokenProvider
Feb 24, 2026
Merged

Implement single active token providers for email change, email confirmation, and password reset#24926
EngincanV merged 3 commits into
devfrom
AbpSingleActiveTokenProvider

Conversation

@maliming

@maliming maliming commented Feb 23, 2026

Copy link
Copy Markdown
Member

Summary

Introduces a "single active token" policy for password reset, email confirmation, and change-email flows by adding three new token providers backed by AbpSingleActiveTokenProvider.

How it works

Each time a new token is generated, a SHA-256 hash is stored in user.Tokens using a fixed internal login provider [AbpSingleActiveToken] and a key of {ProviderName}:{purpose}. On validation, the incoming token is re-hashed and compared against the stored hash using a constant-time comparison. Issuing a new token overwrites the hash, immediately invalidating all previously issued tokens for that purpose.

The bracketed [AbpSingleActiveToken] login provider name clearly distinguishes these internal entries from real external login providers (e.g. Google, GitHub) stored in the same AbpUserTokens table — following the same convention used by ASP.NET Core Identity's own [AspNetUserStore] internal provider.

Example rows in AbpUserTokens:

LoginProvider Name Value
[AbpSingleActiveToken] AbpPasswordReset:ResetPassword <SHA-256 hex hash>
[AbpSingleActiveToken] AbpEmailConfirmation:EmailConfirmation <SHA-256 hex hash>

After a successful operation:

  • Password reset / Change email – the SecurityStamp is updated by ASP.NET Core Identity, which causes all outstanding tokens for the user to fail base validation. No manual cleanup is needed.
  • Email confirmationConfirmEmailAsync does not update the SecurityStamp. Callers that require single-use semantics must explicitly revoke the hash:
    var result = await userManager.ConfirmEmailAsync(user, token);
    if (result.Succeeded)
        await userManager.RemoveEmailConfirmationTokenAsync(user);

New providers

Provider Default token lifespan Replaces
AbpPasswordResetTokenProvider 2 hours Default
AbpEmailConfirmationTokenProvider 2 hours Default
AbpChangeEmailTokenProvider 2 hours Default

All three are registered and set as the default providers in AbpIdentityAspNetCoreModule.

Configuration

Token lifespan can be customized via the respective options classes:

Configure<AbpPasswordResetTokenProviderOptions>(options =>
{
    options.TokenLifespan = TimeSpan.FromHours(1);
});

Configure<AbpEmailConfirmationTokenProviderOptions>(options =>
{
    options.TokenLifespan = TimeSpan.FromDays(1);
});

Configure<AbpChangeEmailTokenProviderOptions>(options =>
{
    options.TokenLifespan = TimeSpan.FromHours(6);
});

Copilot AI review requested due to automatic review settings February 23, 2026 11:08
@maliming maliming added this to the 10.2-preview milestone Feb 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements a "single active token" policy for ASP.NET Core Identity password reset, email confirmation, and change email operations in the ABP Identity module. The implementation uses SHA-256 hash storage to ensure that generating a new token automatically invalidates all previously issued tokens for the same purpose.

Changes:

  • Introduces AbpSingleActiveTokenProvider as a base class that extends DataProtectorTokenProvider with hash-based token validation
  • Adds three concrete token providers (AbpPasswordResetTokenProvider, AbpEmailConfirmationTokenProvider, AbpChangeEmailTokenProvider) with configurable 2-hour default lifespans
  • Provides extension methods (IdentityUserManagerSingleActiveTokenExtensions) for explicit token hash revocation when needed

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
AbpSingleActiveTokenProvider.cs Base class implementing hash-based single active token validation using SHA-256, with automatic hash storage during generation and comparison during validation
AbpPasswordResetTokenProvider.cs Password reset token provider inheriting from base with provider name "AbpPasswordReset"
AbpEmailConfirmationTokenProvider.cs Email confirmation token provider with detailed documentation about manual revocation requirement
AbpChangeEmailTokenProvider.cs Change email token provider inheriting from base with provider name "AbpChangeEmail"
AbpPasswordResetTokenProviderOptions.cs Configuration class setting 2-hour default lifespan for password reset tokens
AbpEmailConfirmationTokenProviderOptions.cs Configuration class setting 2-hour default lifespan for email confirmation tokens
AbpChangeEmailTokenProviderOptions.cs Configuration class setting 2-hour default lifespan for change email tokens
IdentityUserManagerSingleActiveTokenExtensions.cs Extension methods providing explicit token hash revocation for all three token types
AbpIdentityAspNetCoreModule.cs Module configuration registering the three new providers and setting them as defaults
AbpPasswordResetTokenProvider_Tests.cs Comprehensive test coverage for password reset token generation, validation, invalidation, and persistence
AbpEmailConfirmationTokenProvider_Tests.cs Comprehensive test coverage for email confirmation tokens including explicit revocation testing
AbpChangeEmailTokenProvider_Tests.cs Comprehensive test coverage for change email tokens
IdentityUserManagerSingleActiveTokenExtensions_Tests.cs Unit tests verifying the extension methods correctly remove token hashes

…ctive token providers and improve token verification logic

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@maliming maliming requested a review from EngincanV February 24, 2026 01:22
@maliming maliming modified the milestones: 10.2-preview, 10.3-preview Feb 24, 2026
@EngincanV EngincanV merged commit 40e3609 into dev Feb 24, 2026
7 checks passed
@EngincanV EngincanV deleted the AbpSingleActiveTokenProvider branch February 24, 2026 06:36
@maliming maliming modified the milestones: 10.3-preview, 10.2-preview Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants