feat: add email verification flow#27
Conversation
Add complete email verification with token-based confirmation, auto-send on registration, and JWT-protected resend endpoint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis PR implements email verification for user accounts and organization-scoped user creation. It introduces email verification endpoints (request and confirm), adds the Changes
Sequence Diagram(s)sequenceDiagram
actor User as Authenticated User
participant Handler as RequestEmailVerification<br/>Handler
participant Service as AuthService
participant EmailSvc as EmailService
participant Store as PostgreSQL Store
participant SMTP as SMTP Server
User->>Handler: POST /api/v1/auth/email/verify-request<br/>(with JWT)
activate Handler
Handler->>Service: RequestEmailVerification(userID, orgID)
activate Service
Service->>Store: GetUser(orgID, userID)
Store-->>Service: user data
alt Email already verified
Service-->>Handler: ErrEmailAlreadyVerified
Handler-->>User: 409 Conflict
else User active & email not verified
Service->>Service: requestEmailVerificationInternal()
activate Service
Service->>Service: Generate raw token
Service->>Service: Hash token
Service->>Store: CreateEmailVerificationToken()
Store-->>Service: token record created
Service->>EmailSvc: SendEmailVerification(email, rawToken)
activate EmailSvc
EmailSvc->>SMTP: Send email with verification link
SMTP-->>EmailSvc: success
EmailSvc-->>Service: nil
deactivate EmailSvc
Service->>Store: AuditLog(email_verification_requested)
Service-->>Service: return nil
deactivate Service
Service-->>Handler: nil (success)
end
Handler-->>User: 200 OK<br/>"verification email sent"
deactivate Service
deactivate Handler
sequenceDiagram
actor User as User (Public)
participant Handler as ConfirmEmailVerification<br/>Handler
participant Service as AuthService
participant Store as PostgreSQL Store
participant Webhook as Webhook Dispatcher
User->>Handler: POST /api/v1/auth/email/verify-confirm<br/>{token: "xyz..."}
activate Handler
Handler->>Service: ConfirmEmailVerification(rawToken)
activate Service
Service->>Service: Hash token
Service->>Store: GetEmailVerificationToken(tokenHash)
Store-->>Service: token record or nil
alt No token found (invalid/expired)
Service-->>Handler: ErrTokenInvalid
Handler-->>User: 401 Unauthorized
else Token found & valid
Service->>Store: GetUserByIDGlobal(userID)
Store-->>Service: user data
Service->>Store: VerifyUserEmail(userID)
Store-->>Service: user.email_verified_at updated
Service->>Store: MarkEmailVerificationTokenUsed(tokenID)
Store-->>Service: success
Service->>Store: AuditLog(user.email_verified)
Service->>Webhook: Dispatch webhook
Service-->>Handler: nil (success)
Handler-->>User: 200 OK<br/>"email verified successfully"
end
deactivate Service
deactivate Handler
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Add a complete email verification flow: token generation, email delivery, confirmation endpoint, auto-send on registration, and a JWT-protected resend endpoint. Mirrors the existing password reset pattern for consistency.
Type of change
Related issues
Closes #15
Changes
000004creatingemail_verification_tokenstable (mirrorspassword_reset_tokens)ErrEmailAlreadyVerifiedand audit actionsuser.email_verification_requested,user.email_verifiedVERIFY_EMAIL_TOKEN_DURATION(default 24h)email_verifications.gowith token CRUD +VerifyUserEmailandGetUserByIDGlobalon users storeSendEmailVerificationmethod onEmailServiceAuthService:RequestEmailVerification,ConfirmEmailVerification, and privaterequestEmailVerificationInternalRegisternow auto-sends verification email (best-effort, never fails registration)POST /api/v1/auth/email/verify-request(JWT) andPOST /api/v1/auth/email/verify-confirm(public)ErrEmailAlreadyVerified→ 409 Conflict.env.example, andCLAUDE.mdTesting
Describe how you tested these changes:
internal/service/auth_verify_test.go— 5 test cases covering cleanup on delivery failure, already-verified guard, token persistence, confirm flow, and invalid token)go test ./...)docker compose up— verified migration applies, all endpoints return correct status codes (200/400/401/409),email_verified_atpopulates on confirm, single-use token enforcement, audit logs recordedChecklist
fmt.Errorf("context: %w", err)patterninternal/api/router.gointernal/config/config.go🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Documentation