Skip to content

feat(identity): resend cooldown + verification throttling (closes #199)#202

Open
antosubash wants to merge 1 commit into
mainfrom
issue-199-identity-throttling
Open

feat(identity): resend cooldown + verification throttling (closes #199)#202
antosubash wants to merge 1 commit into
mainfrom
issue-199-identity-throttling

Conversation

@antosubash
Copy link
Copy Markdown
Owner

Summary

Closes #199.

Adds IVerificationThrottle (FusionCache-backed) and wires it into the four endpoints called out in the issue:

  • POST /Identity/Account/ResendEmailConfirmation
  • POST /Identity/Account/Manage/Email (re-sends the change-email link)
  • POST /Identity/Account/Manage/SendPhoneVerificationCode
  • POST /Identity/Account/Manage/ConfirmPhoneNumber

Resend cooldown

Per-user, per-channel — default 60s, configurable via Identity:VerificationThrottle:ResendCooldown. When a caller is inside the cooldown:

  • The response shape is identical to the success path (no information leak for the anonymous resend endpoint).
  • Retry-After header is set so well-behaved clients back off.
  • The status message on the signed-in manage pages tells the user the wait.

Attempt cap on ConfirmPhoneNumber

After MaxFailedAttempts (default 5) the (user, phone-channel) pair is locked out for LockoutDuration (default 15 minutes). While locked, UserManager.ChangePhoneNumberAsync is not called — the 6-digit search space cannot be drained against the lockout. A successful confirmation clears the counter and the lockout.

Auditing

Every resend POST and every confirmation POST already runs through AuditMiddleware, so the audit log captures method/path/user/IP/status/timing without changes. The throttle decisions show up via Retry-After and distinct status messages.

Test plan

  • dotnet test modules/Users/tests --filter VerificationThrottle — 8/8 (first allow, cooldown rejects with retry-after, post-cooldown allow, per-channel, per-user, lockout-after-max-failures, success-clears-lockout, not-locked-by-default)
  • dotnet test modules/Users/tests — 70/70 (no regressions)
  • dotnet build — green
  • Manual smoke: curl -X POST /Identity/Account/ResendEmailConfirmation twice within 60s; second response carries Retry-After. Five wrong codes against ConfirmPhoneNumber → sixth attempt short-circuits with "Too many failed attempts."

Configuration

{
  "Identity": {
    "VerificationThrottle": {
      "ResendCooldown": "00:01:00",
      "MaxFailedAttempts": 5,
      "LockoutDuration": "00:15:00"
    }
  }
}

…199)

Adds IVerificationThrottle backed by IFusionCache and wires it into
the four endpoints called out in #199:

- POST /Identity/Account/ResendEmailConfirmation
- POST /Identity/Account/Manage/Email (re-sends change-email link)
- POST /Identity/Account/Manage/SendPhoneVerificationCode
- POST /Identity/Account/Manage/ConfirmPhoneNumber

Resend cooldown is per-user, per-channel (default 60s). When the
caller is inside the cooldown, the response shape is identical to
the success path (no information leak) but a Retry-After header is
set so well-behaved clients back off.

Attempt cap on ConfirmPhoneNumber locks the (user, phone-channel)
pair after MaxFailedAttempts (default 5) for LockoutDuration
(default 15 minutes). While locked out, ChangePhoneNumberAsync is
not called at all — the 6-digit search space cannot be drained
against the lockout. A successful confirmation clears the counter.

Configurable via Identity:VerificationThrottle in appsettings.

Tests: 8 new unit tests cover first-allow, cooldown-rejects-with-
retry-after, post-cooldown-allow, per-channel isolation,
per-user isolation, lockout-after-max-failures, success-clears-
lockout, and not-locked-out-by-default. Full Users suite still
70/70.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying simplemodule-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7f82c93
Status: ✅  Deploy successful!
Preview URL: https://a4d6764a.simplemodule-website.pages.dev
Branch Preview URL: https://issue-199-identity-throttlin.simplemodule-website.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Identity: add resend cooldown + verification attempt throttling for email and phone

1 participant