Files
rmm-openwrt/server/internal/store/verification.go
T

60 lines
2.1 KiB
Go

package store
import (
"context"
"database/sql"
"errors"
"strings"
"time"
"rmm-openwrt/server/internal/authn"
)
func VerificationCodeHash(code string) (string, error) {
return authn.HashPassword("verification:" + strings.TrimSpace(code))
}
func (s *Store) BeginContactVerification(ctx context.Context, userID, channel, destination, codeHash string, expiresAt time.Time) error {
_, err := s.db.ExecContext(ctx, `
INSERT INTO contact_verifications (user_id, channel, destination, code_hash, expires_at, verified_at, created_at)
VALUES (?, ?, ?, ?, ?, NULL, ?)
ON CONFLICT(user_id, channel) DO UPDATE SET
destination = excluded.destination, code_hash = excluded.code_hash,
expires_at = excluded.expires_at, verified_at = NULL, created_at = excluded.created_at
`, userID, channel, destination, codeHash, notificationTimeText(expiresAt), nowText())
return err
}
func (s *Store) ConfirmContactVerification(ctx context.Context, userID, channel, code string) (string, bool, error) {
var destination, expected, expires string
err := s.db.QueryRowContext(ctx, `
SELECT destination, code_hash, expires_at FROM contact_verifications
WHERE user_id = ? AND channel = ? AND verified_at IS NULL
`, userID, channel).Scan(&destination, &expected, &expires)
if errors.Is(err, sql.ErrNoRows) {
return "", false, nil
}
if err != nil {
return "", false, err
}
if time.Now().UTC().After(parseTime(expires)) ||
!authn.VerifyPassword(expected, "verification:"+strings.TrimSpace(code)) {
return "", false, nil
}
_, err = s.db.ExecContext(ctx, `
UPDATE contact_verifications SET verified_at = ? WHERE user_id = ? AND channel = ?
`, nowText(), userID, channel)
return destination, err == nil, err
}
func (s *Store) ContactVerified(ctx context.Context, userID, channel, destination string) (bool, error) {
var verified bool
err := s.db.QueryRowContext(ctx, `
SELECT EXISTS(
SELECT 1 FROM contact_verifications
WHERE user_id = ? AND channel = ? AND destination = ? AND verified_at IS NOT NULL
)
`, userID, channel, strings.TrimSpace(destination)).Scan(&verified)
return verified, err
}