Filed by the domain:identity seat (#6022) out of PR #8017's review, on that dev's reported option B. Observation-class — this is the shipped better-auth default and the maintainer ruling selected it, so nothing here is a defect against the ruling. Recording the exposure so it does not evaporate into a closed thread. Unassigned; triage grades it.
What ships now
PR #8017 enabled user.changeEmail per the 2026-08-12 ruling, which said 「策略按 better-auth 常规」. The dev established what 常规 means in the installed version (1.7.0-rc.2) rather than in general — a correction worth keeping, since the option name I had suggested does not exist there.
The resulting flow is single-step: the verification token rides emailVerification.sendVerificationEmail to the new address, and /verify-email applies the change. sendChangeEmailConfirmation and updateEmailWithoutVerification stay at their defaults, each documented at the config site as the policy it is.
The exposure
The old mailbox is never contacted. So an attacker holding a live session — stolen cookie, unattended device, session not yet revoked — can:
POST /change-email to an address they control;- verify it from that address;
- own the account's identity, with the original owner's mailbox receiving nothing.
The account-recovery path then belongs to the attacker. Password knowledge is not required at any step, because the session already authenticates the request.
Why this is a finding and not a queue card:
The mitigation, if it is ever graded up
sendChangeEmailConfirmation gives the two-step shape: the old address approves first, then the new one verifies. better-auth treats the confirmation callback as a pure addition, so adopting it later undoes nothing that shipped here — the option stays reachable at any time.
⚠️ Do not read this as a defect in #8017
The PR implemented the ruling correctly and documented both withheld options at the config site with their reasons, rather than leaving them as unexplained defaults. That documentation is what makes this finding cheap to act on later. If triage grades this up, the work is a follow-up card against the policy, not rework against that PR.
Related
#7735 / PR #8017 (the enablement and the documented withheld options) · #7732 (session tombstoning — makes session validity auditable) · #7724 / PR #7879 (the admin deletion path, whose brokenness was the other half of #7735's ruling)
Filed by the
domain:identityseat (#6022) out of PR #8017's review, on that dev's reported option B. Observation-class — this is the shipped better-auth default and the maintainer ruling selected it, so nothing here is a defect against the ruling. Recording the exposure so it does not evaporate into a closed thread. Unassigned; triage grades it.What ships now
PR #8017 enabled
user.changeEmailper the 2026-08-12 ruling, which said 「策略按 better-auth 常规」. The dev established what 常规 means in the installed version (1.7.0-rc.2) rather than in general — a correction worth keeping, since the option name I had suggested does not exist there.The resulting flow is single-step: the verification token rides
emailVerification.sendVerificationEmailto the new address, and/verify-emailapplies the change.sendChangeEmailConfirmationandupdateEmailWithoutVerificationstay at their defaults, each documented at the config site as the policy it is.The exposure
The old mailbox is never contacted. So an attacker holding a live session — stolen cookie, unattended device, session not yet revoked — can:
POST /change-emailto an address they control;The account-recovery path then belongs to the attacker. Password knowledge is not required at any step, because the session already authenticates the request.
Why this is a
findingand not a queue card:sendChangeEmailConfirmationmeans a new email template plus its i18n bundle keys in all four locales, i.e. a new authoring surface, not a config flip;sys_session.revoked_at/revoke_reasonare declared readonly "System-managed" but the interactive-revoke path deletes rather than tombstones — ADR-0069 D4 revoke-audit trail is effectively inert #7732), so "was the session still valid" became an answerable question that it was not before.The mitigation, if it is ever graded up
sendChangeEmailConfirmationgives the two-step shape: the old address approves first, then the new one verifies. better-auth treats the confirmation callback as a pure addition, so adopting it later undoes nothing that shipped here — the option stays reachable at any time.The PR implemented the ruling correctly and documented both withheld options at the config site with their reasons, rather than leaving them as unexplained defaults. That documentation is what makes this finding cheap to act on later. If triage grades this up, the work is a follow-up card against the policy, not rework against that PR.
Related
#7735 / PR #8017 (the enablement and the documented withheld options) · #7732 (session tombstoning — makes session validity auditable) · #7724 / PR #7879 (the admin deletion path, whose brokenness was the other half of #7735's ruling)