Security: gate sessions on verified email (opt-in) #53
Reference in New Issue
Block a user
Delete Branch "security-require-email-verification"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Backlog §2.10: registration issued a live session and
email_verified_atwas written but never read — unverified users had full access and there was no switch to require verification.Adds
REQUIRE_EMAIL_VERIFICATION(default false). When true:resolve_session_userreturns None for an unverified user — the single read-side gate over every authed request, incl. the session minted at registration.loginraises 403 instead of issuing a useless token.Default false on purpose: self-hosts without SMTP and pre-existing accounts (
email_verified_atnull) must not be locked out; operators flip it on once mail works and accounts are verified. Documented in.env.example.Tests: default-off unchanged; on → register session 401, login 403, then verify → both work. 75 passed.
🤖 Generated with Claude Code
Backlog §2.10: registration issued a live session and email_verified_at was written but never read, so an unverified user had full access and there was no switch to require verification. Add REQUIRE_EMAIL_VERIFICATION (default false). When true: - resolve_session_user returns None for a user whose email_verified_at is null — the single read-side gate covering every authenticated request, incl. the session minted at registration. - login raises 403 ("email not verified") instead of issuing a useless token. Default false on purpose: self-hosts without SMTP, and accounts created before this gate existed (email_verified_at null), must not be locked out. Operators enable it once mail works and accounts are verified. Documented in .env.example. Tests: default-off keeps unverified accounts working; on → register's session won't resolve (401), login is 403, and after verify-email both work. 75 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Paul <justin@jpaul.me>