Skip to content

fix(signup): the verification link carries the address - #46

Merged
tranh0anghuan merged 1 commit into
testfrom
fix/verify-email-resend-dead-token
Sep 9, 2026
Merged

tranh0anghuan merged 1 commit into
testfrom
fix/verify-email-resend-dead-token

Conversation

@tranh0anghuan

Copy link
Copy Markdown
Collaborator

resend_verification can resolve an account from an email, a LIVE token, or a signup_data row keyed by a session that no longer exists (create_account stopped establishing one). But every failure that lands the user on the "Verification failed" screen is a failure of the token: consumed by an earlier click or by a mail scanner prefetching the URL, or superseded, since drumate_set_verification_token_v2 DELETEs the previous row whenever a newer link is minted. So the recovery path's only usable credential was guaranteed dead exactly when recovery was needed, and Resend answered no_pending_signup forever.

Measured on stage: of 44 outstanding tokens, 15 no longer joined to a drumate and 10 more pointed at accounts with no unverified_email staged, so 25 could not be resent at all. signup_data held one row in total.

Putting the address next to the token gives that screen something that survives the token. It lives in the fragment, so it never reaches a server, an access log or a Referer -- the same reach as the far more sensitive token beside it, in a mailbox that already knows its own address -- and it grants no new access: resend_verification already accepts an arbitrary address and still refuses any account whose unverified_email is not already staged.

resend_verification can resolve an account from an email, a LIVE token, or a
signup_data row keyed by a session that no longer exists (create_account
stopped establishing one). But every failure that lands the user on the
"Verification failed" screen is a failure of the token: consumed by an earlier
click or by a mail scanner prefetching the URL, or superseded, since
drumate_set_verification_token_v2 DELETEs the previous row whenever a newer
link is minted. So the recovery path's only usable credential was guaranteed
dead exactly when recovery was needed, and Resend answered no_pending_signup
forever.

Measured on stage: of 44 outstanding tokens, 15 no longer joined to a drumate
and 10 more pointed at accounts with no unverified_email staged, so 25 could
not be resent at all. signup_data held one row in total.

Putting the address next to the token gives that screen something that
survives the token. It lives in the fragment, so it never reaches a server, an
access log or a Referer -- the same reach as the far more sensitive token
beside it, in a mailbox that already knows its own address -- and it grants no
new access: resend_verification already accepts an arbitrary address and still
refuses any account whose unverified_email is not already staged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tranh0anghuan
tranh0anghuan merged commit a7fceed into test Sep 9, 2026
8 checks passed
@sonarqubecloud

sonarqubecloud Bot commented Sep 9, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

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