package auth import ( "crypto/rand" "encoding/base64" "errors" "time" "golang.org/x/crypto/bcrypt" ) const ( // BCrypt cost - 12 is a good balance between security and performance bcryptCost = 12 // Session token length in bytes (32 bytes = 256 bits) sessionTokenLength = 32 // Session expiry duration (7 days) sessionExpiry = 7 * 24 * time.Hour ) var ( ErrInvalidCredentials = errors.New("invalid email or password") ErrUserExists = errors.New("user already exists") ErrInvalidSession = errors.New("invalid or expired session") ErrWeakPassword = errors.New("password must be at least 8 characters") ErrInvalidEmail = errors.New("invalid email format") ) // HashPassword securely hashes a password using bcrypt func HashPassword(password string) (string, error) { if len(password) < 8 { return "", ErrWeakPassword } hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost) if err != nil { return "", err } return string(hash), nil } // CheckPassword verifies a password against a hash func CheckPassword(password, hash string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } // GenerateSessionToken creates a cryptographically secure random token func GenerateSessionToken() (string, error) { bytes := make([]byte, sessionTokenLength) _, err := rand.Read(bytes) if err != nil { return "", err } // Use URL-safe base64 encoding token := base64.URLEncoding.EncodeToString(bytes) return token, nil } // GetSessionExpiry returns the expiry time for a new session func GetSessionExpiry() time.Time { return time.Now().Add(sessionExpiry) } // ValidateEmail performs basic email validation func ValidateEmail(email string) bool { if len(email) < 3 || len(email) > 254 { return false } // Basic check for @ symbol and domain atCount := 0 dotAfterAt := false atPos := -1 for i, c := range email { if c == '@' { atCount++ atPos = i } if atPos > 0 && i > atPos && c == '.' { dotAfterAt = true } } return atCount == 1 && dotAfterAt && atPos > 0 && atPos < len(email)-2 } // SanitizeInput removes dangerous characters for basic XSS prevention // Note: Go templates auto-escape, but this adds an extra layer func SanitizeInput(input string) string { // Templates handle escaping, but we can enforce length limits if len(input) > 1000 { return input[:1000] } return input }