Skip to content

Deliver login codes only to a verified primary email - #12

Merged
andresbott merged 8 commits into
mainfrom
fix/verified-code-recipient
Aug 23, 2026
Merged

andresbott merged 8 commits into
mainfrom
fix/verified-code-recipient

Conversation

@andresbott

Copy link
Copy Markdown
Member

Summary

The primary email was a credential-delivery channel with no proof requirement behind it: login.CodeMethod resolved user.PrimaryEmail (falling back to LoginID) and never consulted PrimaryEmailVerified. Any path able to write an unverified address — a session-authenticated profile form, or registration without an email check — could redirect every future login and second-factor code for an account, so a stolen cookie was enough to turn a transient compromise into a durable credential.

What

  • CodeMethod now resolves only a verified primary email; the LoginID fallback is removed, and the Recipient hook is the explicit opt-out. Breaking: users with an unverified or absent address stop receiving codes, and if email is their only enrolled second factor they cannot complete login until it is verified. That fail-closed behaviour is deliberate — making availability depend on verification would be a second-factor bypass.
  • The code is generated after the recipient resolves, so an undeliverable attempt no longer burns a fresh code and resets the attempt count of a live one. The skip logs at Debug and returns nil, keeping Initiate enumeration-safe.
  • The demo tightens the same reasoning: the profile form requires the current password to change an email, and demo/setup seeds email logins with a verified primary address.
  • docs/agents/migration-verified-code-recipient.md documents the break — SQL to find affected users, and the three remediation options in order of preference.

Notes

  • This is Phase 0 of the email-change design (docs/superpowers/specs/2026-08-23-emailchange-engine-design.md). Until flow/emailchange ships, user.Service.SetPrimaryEmailVerified is the manual route to verify an address.

@andresbott
andresbott merged commit 1c1accf into main Aug 23, 2026
3 checks passed
@andresbott
andresbott deleted the fix/verified-code-recipient branch August 23, 2026 19:58
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