docs(identity): document 2FA verification code mechanics and customization#25314
docs(identity): document 2FA verification code mechanics and customization#25314maliming merged 2 commits intoabpframework:devfrom
Conversation
|
Hi @bsogulcan, thanks for the PR — the "2FA verification code is not single-use" behavior is something we see asked about repeatedly, and putting it in the docs is useful. After discussing internally, we'd like to address the underlying issue in the framework itself, similar to
We'll open a separate PR for the framework change. Could you keep this PR open in the meantime? Once ours is merged, you could update the docs here to align with the new built-in behavior — probably something much shorter like "ABP's 2FA email/phone codes are single-use by default, configure lifespan via Two small corrections on the current text, so they don't get lost:
I'll link the framework PR here once it's up. Thanks again for raising this! |
|
Hi @maliming, this sounds like a solid approach. Handling it at the framework level with single-use tokens will definitely make things cleaner. I’ll keep this PR open for now and wait for the framework changes. Also, thanks for pointing out the corrections regarding the phone modifier and the security stamp, I’ll make sure those are fixed in the meantime. Looking forward to the update. Thank again! |
|
hi #25316 merged. You can now update your PR. Thanks. |
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 abpframework#25314.
|
Hi @maliming, thanks for landing #25316! Updated the PR to follow the new built-in providers — rewrote the verification-code section, swapped the custom IDistributedCache example for TokenLifespan/CodeLength configuration and a short provider-replacement note. Let me know what you think when you get a chance! |
maliming
left a comment
There was a problem hiding this comment.
Thanks for the thoughtful rewrite, @bsogulcan! The content is accurate, concise and aligned with the new providers. Really appreciate the quick turnaround.
Description
Adds two new sections to the Two Factor Authentication guide
(
docs/en/modules/identity/two-factor-authentication.md) covering anarea the document did not previously address: how the Email/SMS
verification codes are actually produced, and how to replace the
provider when the default behaviour is not sufficient.
New sections (appended after "2FA From the End Users' Perspective"):
How the Verification Code Is Generated
Identity's built-in
EmailTokenProvider<TUser>/PhoneNumberTokenProvider<TUser>, registered viaAddDefaultTokenProviders()inAbpIdentityAspNetCoreModuleandnot replaced by ABP, while
Authenticatoris overridden byAbpAuthenticatorTokenProvider.(
HMAC-SHA1(SecurityStamp, timestep || modifier), 3-minute step).that users regularly ask about on the forums / GitHub (same code
returned within a timestep, codes not being single-use, a new
request not invalidating the previous code, effective 3–6 minute
window, security-stamp rotation as the only natural invalidation
— including the caveat that rotating mid-login breaks the
RequiresTwoFactortoken produced bySignInManager).Customizing the Verification Code Provider
can be obtained by implementing
IUserTwoFactorTokenProvider<IdentityUser>and registering itunder the existing
Email/Phonekeys, which overrides theprevious descriptor in
TokenOptions.ProviderMap.IDistributedCache-backed example(cryptographically-secure 6-digit code, overwrite on regenerate,
removed on successful validation, 3-minute TTL).
AccountAppService.SendTwoFactorCodeAsyncandSignInManager.TwoFactorSignInAsyncdispatch throughUserManager.GenerateTwoFactorTokenAsync/VerifyTwoFactorTokenAsync, so no further wiring is required,and that the
RequiresTwoFactorsentinel (usingTokenOptions.DefaultProvider) is unaffected.Placement: the new sections are appended at the end of the document
so it now flows from administrator configuration → provider overview →
end-user experience → developer customization, which is the same
progression the existing
Identity.TwoFactorRememberMecookieoverride example already establishes on its closing paragraph.
Not a breaking change. Documentation-only; no source code or
public API is touched.
Checklist
(documentation-only change; no code paths modified, so no tests apply)
How to test it?
docs/en/modules/identity/two-factor-authentication.mdandconfirm the two new
##sections appear after2FA From the End Users' Perspective, the heading hierarchy is
correct, and the
csharp/textcode blocks render.modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs→
AddDefaultTokenProviders()registration.AbpAuthenticatorTokenProvider(in the commercial Identity Promodule) → the only provider ABP overrides out of the default three.
TokenOptions.ProviderMapreplacement semantics viaIdentityBuilder.AddTokenProvider(name, type)in the ASP.NET CoreIdentity source.
into any ABP Identity-enabled app, register them as shown, then log
in with a 2FA-enabled user — requesting a code twice should yield
two different values, and submitting an already-validated code
should fail on the second attempt.