58 lines
1.6 KiB
Go
58 lines
1.6 KiB
Go
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
|
|
}
|