Skip to content

feat: resend account activation email endpoint#321

Open
hhvrc wants to merge 1 commit into
developfrom
feature/resend-activation-email
Open

feat: resend account activation email endpoint#321
hhvrc wants to merge 1 commit into
developfrom
feature/resend-activation-email

Conversation

@hhvrc

@hhvrc hhvrc commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a user-facing action to re-send the account activation email, so users who never received (or lost) their activation email can request a new one. No database migration required — it reuses the existing UserActivationRequest table.

This is the first, deliberately small slice of a larger email-features effort (admin send-any-email-type, list email types, and an upstream-failure retry queue will follow in separate PRs).

Endpoint

POST /{version}/account/activate/resend

{ "email": "user@example.com" }

Always returns a generic 200 OK (LegacyEmptyResponse).

Behavior

  • Rotates the activation token on every resend (invalidating any previously sent link) and persists it before sending, so the link in the new email always matches the stored hash.
  • Creates an activation request if an unactivated account doesn't have one yet (e.g. legacy data or a failed initial send), so the user can still complete activation.
  • Silently no-ops for unknown, already-activated, or deactivated accounts and always returns a generic 200 — the endpoint can't be used to probe which emails are registered or their activation state.
  • Rate limited under the existing auth policy (mirrors POST /account/reset).
  • Increments UserActivationRequest.EmailSendAttempts for observability (the column already existed and was unused).

Tests

Added integration tests (MailTests.cs, delivered via the Mailpit SMTP test server):

  • ResendActivation_UnactivatedUser_SendsWorkingActivationEmail — create-request path; the resent link actually activates the account.
  • ResendActivation_RotatesToken_PreviousLinkInvalidated — after a resend, the original token returns 400 and the fresh token returns 200.
  • ResendActivation_AlreadyActivatedUser_Returns200_AndSendsNoEmail
  • ResendActivation_UnknownEmail_Returns200_AndSendsNoEmail

All 4 pass locally.

🤖 Generated with Claude Code


Open in Stage

Adds POST /{version}/account/activate/resend so users can request a fresh
activation email. The endpoint:

- rotates the activation token (invalidating any previously sent link) and
  persists it before sending, so the emailed link matches the stored hash
- creates an activation request if the unactivated account lacks one (e.g.
  legacy data or a failed initial send)
- silently no-ops for unknown, already-activated, or deactivated accounts and
  always returns a generic 200, so it can't be used to probe account state
- is rate limited under the existing "auth" policy and tracks EmailSendAttempts

No database migration required (reuses the existing UserActivationRequest table).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stage-review

stage-review Bot commented Jun 24, 2026

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 4 individual chapters for you:

Title
1 Define account activation resend service interface
2 Implement activation token rotation and email logic
3 Expose resend activation endpoint
4 Verify activation resend and token rotation
Open in Stage

Chapters generated by Stage for commit 105d509 on Jun 24, 2026 7:58am UTC.

@hhvrc hhvrc closed this Jun 24, 2026
@hhvrc hhvrc reopened this Jun 24, 2026
@hhvrc hhvrc closed this Jun 24, 2026
@hhvrc hhvrc deleted the feature/resend-activation-email branch June 24, 2026 08:04
@hhvrc hhvrc restored the feature/resend-activation-email branch June 24, 2026 09:42
@hhvrc hhvrc reopened this Jun 24, 2026
@hhvrc hhvrc self-assigned this Jun 24, 2026
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.

1 participant