This commit is contained in:
2026-06-15 16:53:25 +03:30
commit 390ef96e7e
34 changed files with 4463 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
package auth
import (
"context"
"crypto/rand"
"database/sql"
"errors"
"math/big"
"time"
)
// OTPStore مدیریت کدهای OTP در جدول otp_tokens.
type OTPStore struct {
db *sql.DB
ttl time.Duration
}
func NewOTPStore(db *sql.DB, ttl time.Duration) *OTPStore {
return &OTPStore{db: db, ttl: ttl}
}
// Generate یک کد ۵ رقمی امن می‌سازد (مطابق rand(10000,99999) در Laravel).
func Generate() string {
n, err := rand.Int(rand.Reader, big.NewInt(90000))
if err != nil {
return "12345" // عملاً رخ نمی‌دهد
}
return big.NewInt(10000 + n.Int64()).String()
}
// Create کد را برای کاربر ذخیره می‌کند.
func (s *OTPStore) Create(ctx context.Context, userID int64, token string) error {
_, err := s.db.ExecContext(ctx,
`INSERT INTO otp_tokens (user_id, token) VALUES (?, ?)`, userID, token)
return err
}
var ErrInvalidOTP = errors.New("token not valid")
// Verify اعتبار کد را در بازه ttl بررسی و پس از مصرف حذف می‌کند.
func (s *OTPStore) Verify(ctx context.Context, userID int64, token string) error {
cutoff := time.Now().Add(-s.ttl).UTC().Format("2006-01-02 15:04:05")
var id int64
err := s.db.QueryRowContext(ctx,
`SELECT id FROM otp_tokens
WHERE user_id = ? AND token = ? AND created_at > ?
ORDER BY id DESC LIMIT 1`, userID, token, cutoff).Scan(&id)
if errors.Is(err, sql.ErrNoRows) {
return ErrInvalidOTP
}
if err != nil {
return err
}
// مصرف شد ⇒ حذف
_, err = s.db.ExecContext(ctx, `DELETE FROM otp_tokens WHERE id = ?`, id)
return err
}