feat: resend account activation email endpoint - #321
Conversation
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>
|
Ready to review this PR? Stage has broken it down into 4 individual chapters for you:
Chapters generated by Stage for commit 9e021d7 on Jun 30, 2026 11:12pm UTC. |
Brings in the durable email outbox (#327, squashed as 47029f0) and the v1 auth endpoint retirement. Adapts the resend-activation feature to the new model: - AccountService.ResendActivationEmailAsync now enqueues an EmailOutboxMessage (ForAccountActivation) and nudges the outbox instead of sending inline via IEmailService. It ensures the activation request exists (seeded hash) and lets the Cron delivery job mint the token lazily on send - so a resend supersedes the older pending activation (coalesce key) and invalidates the previous link. - The resend MailTests now assert outbox enqueue (row type / recipient / coalesce key) or that nothing is enqueued, matching the API test split; delivery + token rotation are the Cron host's job (Cron.IntegrationTests).
…ivation-email # Conflicts: # API/Services/Account/AccountService.cs # API/Services/Account/IAccountService.cs
|
Warning Review limit reached
Next review available in: 48 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Drop the LegacyEmptyResponse envelope in favour of a bare Ok(), matching how the newer endpoints (PasswordResetInitiateV2, SignUpV2) respond. The message it carried only restated the documented 200 response. The integration tests only assert the status code, so they are unaffected.
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
UserActivationRequesttable.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
200— the endpoint can't be used to probe which emails are registered or their activation state.authpolicy (mirrorsPOST /account/reset).UserActivationRequest.EmailSendAttemptsfor 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 returns400and the fresh token returns200.ResendActivation_AlreadyActivatedUser_Returns200_AndSendsNoEmailResendActivation_UnknownEmail_Returns200_AndSendsNoEmailAll 4 pass locally.