66 lines
1.7 KiB
Go
66 lines
1.7 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"crypto/subtle"
|
|
|
|
smsentity "spark-server/internal/module/sms/entity"
|
|
userentity "spark-server/internal/module/user/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
// CheckAndConsume validates an OTP and deletes it on success.
|
|
func (s *Service) CheckAndConsume(ctx context.Context, ch smsentity.Channel, purpose smsentity.Purpose, target, code string) error {
|
|
if s == nil || s.store == nil {
|
|
return errs.ErrAvailable
|
|
}
|
|
if !ch.Valid() || !purpose.Valid() {
|
|
return errs.ErrInvalidArgument
|
|
}
|
|
target = normalizeTarget(ch, target)
|
|
if target == "" || code == "" {
|
|
return errs.ErrVerifyCodeInvalid
|
|
}
|
|
if ch == smsentity.ChannelEmail && userentity.IsTestEmail(target) {
|
|
return nil
|
|
}
|
|
if ch == smsentity.ChannelPhone && skipSendPhone(target) {
|
|
return nil
|
|
}
|
|
key := storeKey(ch, purpose, target)
|
|
want, err := s.store.Get(ctx, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if want == "" || subtle.ConstantTimeCompare([]byte(want), []byte(code)) != 1 {
|
|
return errs.ErrVerifyCodeInvalid
|
|
}
|
|
_ = s.store.Del(ctx, key)
|
|
return nil
|
|
}
|
|
|
|
// ClearTarget removes stored OTP and cooldown keys for one target (all purposes on that channel).
|
|
func (s *Service) ClearTarget(ctx context.Context, ch smsentity.Channel, target string) {
|
|
if s == nil || s.store == nil {
|
|
return
|
|
}
|
|
target = normalizeTarget(ch, target)
|
|
if target == "" {
|
|
return
|
|
}
|
|
for _, p := range []smsentity.Purpose{smsentity.PurposeLogin, smsentity.PurposeAccountDelete} {
|
|
_ = s.store.Del(ctx, storeKey(ch, p, target))
|
|
}
|
|
}
|
|
|
|
func normalizeTarget(ch smsentity.Channel, target string) string {
|
|
switch ch {
|
|
case smsentity.ChannelEmail:
|
|
return userentity.NormalizeEmail(target)
|
|
case smsentity.ChannelPhone:
|
|
return smsentity.NormalizePhone(target)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|