You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/api/sms/validate.go

73 lines
1.8 KiB
Go

2 months ago
//
// validate.go
// Copyright (C) 2025 veypi <i@veypi.com>
//
// Distributed under terms of the MIT license.
//
package sms
import (
"fmt"
"time"
"github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/libs/utils"
"github.com/veypi/OneAuth/models"
"gorm.io/gorm"
)
// VerifyCode 验证验证码
func VerifyCode(Phone, Code, Region, Purpose string) error {
normalizedPhone := utils.NormalizePhoneNumber(Phone)
// 1. 查找最新的待验证码
var smsCode models.SMSCode
err := cfg.DB().Where("phone = ? AND region = ? AND purpose = ? AND status = ?",
normalizedPhone, Region, Purpose, models.CodeStatusPending).
Order("created_at DESC").
First(&smsCode).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return fmt.Errorf("verification code not found or already used")
}
return fmt.Errorf("failed to query sms code: %w", err)
}
// 2. 检查验证码是否过期
if smsCode.IsExpired() {
cfg.DB().Model(&smsCode).Updates(map[string]any{
"status": models.CodeStatusExpired,
})
return fmt.Errorf("verification code has expired")
}
// 3. 检查是否已达到最大尝试次数
if !smsCode.CanRetry(cfg.Config.SMS.Global.MaxAttempts) {
cfg.DB().Model(&smsCode).Updates(map[string]any{
"status": models.CodeStatusFailed,
})
return fmt.Errorf("verification failed too many times")
}
// 4. 验证码不匹配
if smsCode.Code != Code {
cfg.DB().Model(&smsCode).Updates(map[string]any{
"attempts": smsCode.Attempts + 1,
})
remaining := cfg.Config.SMS.Global.MaxAttempts - smsCode.Attempts - 1
return fmt.Errorf("verification code incorrect, %d attempts remaining", remaining)
}
// 5. 验证成功
now := time.Now()
cfg.DB().Model(&smsCode).Updates(map[string]any{
"status": models.CodeStatusUsed,
"used_at": &now,
})
return nil
}