feat: users.email_verified_at records a proved address (mail activation, reset code)

- additive migration 202610210001_add_users_email_verified_at (ADD COLUMN IF NOT EXISTS, no backfill, no-op rollback)
- User.EmailVerifiedAt (json:"-", not fillable)
- VerifyActivationCode and ResetPassword stamp it in the write that activates or stores the password
This commit is contained in:
Jakub Zych
2026-10-21 13:53:19 +02:00
parent c7635c56e3
commit add1f7a806
6 changed files with 59 additions and 3 deletions

View File

@@ -113,7 +113,9 @@ func VerifyResetCode(ctx context.Context, db *gorm.DB, userID uint, code string)
}
// VerifyActivationCode accepts the stored code inside the activation TTL.
// A soft-deleted row is visible and is restored on success.
// A soft-deleted row is visible and is restored on success. The code was
// mailed to the account's address, so success also records the proof of that
// address (email_verified_at, D-27) in the same write that activates it.
func VerifyActivationCode(ctx context.Context, db *gorm.DB, userID uint, code string) (*models.User, bool) {
user, err := takeUser(ctx, db, userID, true)
if err != nil || user == nil || user.IsActivated || user.ActivationCode == nil {
@@ -134,11 +136,13 @@ func VerifyActivationCode(ctx context.Context, db *gorm.DB, userID uint, code st
"activated_at": now,
"activation_code": nil,
"activation_code_issued_at": nil,
"email_verified_at": now,
}).Error; err != nil {
return nil, false
}
user.IsActivated = true
user.ActivatedAt = &now
user.EmailVerifiedAt = &now
user.ActivationCode = nil
user.ActivationCodeIssuedAt = nil
return user, true