Skip to content

Web registration: a failed verification delivery leaves an account permanently stuck #43

Description

@tgiachi

Raised in a comment on #40.

IAccountService.RegisterPending commits the inactive account and its activation token, then publishes AccountRegistrationRequestedEvent and answers 202. Delivery of the verification is best-effort by design: NotificationService schedules it on the job system, retries MaxAttempts times and logs Gave up delivering if every attempt fails.

That combination has no recovery path. When delivery fails — the SMTP host is down, the channel is misconfigured, the process restarts between the account commit and the send, the mail is silently dropped by the recipient side — the outcome is:

  • an account that exists but cannot log in, because it is inactive;
  • its username permanently taken, so the same person cannot simply register again;
  • an activation token that is never shown to anyone.

The account is unreachable through any existing endpoint. Only an operator with database access can resolve it.

Proposed fix

A rate-limited, idempotent resend endpoint — POST /api/v1/register/resend taking the email address:

  • Answers the same way whether or not a pending account exists, so it cannot be used to enumerate registered addresses.
  • Re-sends the notification for the existing account rather than creating anything, so calling it twice is harmless.
  • Reuses the existing activation token, or mints a fresh one and invalidates the previous, but does not touch IsActive.
  • Uses the same per-IP fixed-window limiter as POST /api/v1/register, since it triggers outbound mail.
  • Does nothing for an account that is already active.

A persisted outbox was considered and rejected in the notifications spec, and this issue does not reopen that: an outbox makes delivery more reliable, while the real problem here is that there is no way to ask again. The resend path fixes the stuck account at a fraction of the cost and is useful even with a perfectly reliable transport, because mail is lost outside our process too.

Acceptance

  • A pending account whose verification was never delivered can be recovered without operator intervention.
  • The endpoint is anonymous, rate-limited, idempotent, and does not reveal whether an address is registered.
  • Tests cover: resend for a pending account, resend for an unknown address, resend for an already-active account, and the rate limit.
  • The registration guide documents the endpoint and says plainly that a lost verification is recoverable this way.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions