What
Send a verification email after registration. Gate reactions and comments (M5 features) behind email verification. Add POST /auth/verify-email and POST /auth/resend-verification endpoints.
Acceptance Criteria
Implementation Notes
-- migrations/000000000004_email_verification_tokens.sql
CREATE TABLE email_verification_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash TEXT UNIQUE NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
The verification gate in M5 handlers:
// In require_verified_email() helper:
if user.email_verified_at.is_none() {
return Err(AppError::Forbidden("Please verify your email first"));
}
Rate limit POST /auth/resend-verification at 3 req/hour/user (not IP — logged-in endpoint).
What
Send a verification email after registration. Gate reactions and comments (M5 features) behind email verification. Add
POST /auth/verify-emailandPOST /auth/resend-verificationendpoints.Acceptance Criteria
email_verification_tokenstable migration createdPOST /auth/register: sends verification email with 24-hour tokenPOST /api/v1/auth/verify-emailaccepts{"token": "..."}and setsemail_verified_atPOST /api/v1/auth/resend-verification(protected, rate-limited) — resends verification email; invalidates old token403 {"error": "Please verify your email first", "code": "EMAIL_NOT_VERIFIED"}email_verified_atcolumn on users table (added in M1-001, nullable, set here)///doc comments;cargo doc --no-depsproduces no warnings for this moduledocs/bruno/auth/email-verification.bruadded (or updated) with example request body and expected response shapes; uses{{base_url}}and{{access_token}}variablesImplementation Notes
The verification gate in M5 handlers:
Rate limit
POST /auth/resend-verificationat 3 req/hour/user (not IP — logged-in endpoint).