fixed verification code
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
"github.com/tech/sendico/pkg/db/verification"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
@@ -21,36 +20,45 @@ func (db *verificationDB) Consume(
|
||||
hash := tokenHash(rawToken)
|
||||
now := time.Now().UTC()
|
||||
|
||||
// 1) Пытаемся атомарно использовать токен
|
||||
// 1) Find token by hash (do NOT filter by usedAt/expiresAt here),
|
||||
// otherwise you can't distinguish "used/expired" from "not found".
|
||||
filter := repository.Query().And(
|
||||
repository.Filter("verifyTokenHash", hash),
|
||||
repository.Filter("usedAt", nil),
|
||||
repository.Query().Comparison(repository.Field("expiresAt"), builder.Gt, now),
|
||||
)
|
||||
|
||||
t, err := db.tf.CreateTransaction().Execute(
|
||||
t, e := db.tf.CreateTransaction().Execute(
|
||||
ct,
|
||||
func(ctx context.Context) (any, error) {
|
||||
var existing model.VerificationToken
|
||||
if err := db.DBImp.FindOne(ctx, filter, &existing); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
// normal behaviour
|
||||
db.Logger.Debug("Token hash not found", zap.Error(err), zap.String("hash", hash))
|
||||
return nil, wrap(verification.ErrTokenNotFound, err.Error())
|
||||
return nil, verification.ErorrTokenNotFound()
|
||||
}
|
||||
db.Logger.Warn("Failed to check token", zap.Error(err), zap.String("hash", hash))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2) Semantic checks
|
||||
if existing.UsedAt != nil {
|
||||
db.Logger.Debug("Token has already been used", zap.String("hash", hash), zap.Time("used_at", *existing.UsedAt))
|
||||
return nil, wrap(verification.ErrTokenAlreadyUsed, "db: token already used")
|
||||
db.Logger.Debug(
|
||||
"Token has already been used",
|
||||
zap.String("hash", hash),
|
||||
zap.Time("used_at", *existing.UsedAt),
|
||||
)
|
||||
return nil, verification.ErorrTokenAlreadyUsed()
|
||||
}
|
||||
|
||||
if existing.ExpiresAt.Before(now) {
|
||||
db.Logger.Debug("Token has already expired", zap.String("hash", hash), zap.Time("expired_at", existing.ExpiresAt))
|
||||
return nil, wrap(verification.ErrTokenExpired, "db: token expired")
|
||||
if !existing.ExpiresAt.After(now) { // includes equal time edge-case
|
||||
db.Logger.Debug(
|
||||
"Token has already expired",
|
||||
zap.String("hash", hash),
|
||||
zap.Time("expired_at", existing.ExpiresAt),
|
||||
)
|
||||
return nil, verification.ErorrTokenExpired()
|
||||
}
|
||||
|
||||
// 3) Mark as used
|
||||
existing.UsedAt = &now
|
||||
if err := db.DBImp.Update(ctx, &existing); err != nil {
|
||||
db.Logger.Warn("Failed to consume token", zap.Error(err), zap.String("hash", hash))
|
||||
@@ -60,12 +68,13 @@ func (db *verificationDB) Consume(
|
||||
return &existing, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
res, ok := t.(*model.VerificationToken)
|
||||
if !ok {
|
||||
return nil, merrors.Internal("unexpexted token type")
|
||||
return nil, merrors.Internal("unexpected token type")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
||||
Reference in New Issue
Block a user