forked from baron/baron-sso
feat: add robust login ID collision prevention and UI validation (#440)
- Add `ValidateLoginID` to enforce ID collision and security rules (prevents phone number collision, email format usage, and reserved words). - Add `POST /api/v1/auth/signup/check-login-id` endpoint for real-time ID availability checks. - Add `checkLoginIDAvailability` API call to userfront's `AuthProxyService`. - Implement "Check Duplication" button and error/success messaging for the Login ID field in the signup screen. - Add "000000" magic code bypass for `VerifySignupCode` in non-production environments to streamline testing.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -61,3 +62,63 @@ func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateLoginID checks if the loginID violates any collision, length, or security rules.
|
||||
func ValidateLoginID(loginID, email, phone string) error {
|
||||
loginID = strings.TrimSpace(loginID)
|
||||
if loginID == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(loginID) < 4 || len(loginID) > 30 {
|
||||
return fmt.Errorf("ID must be between 4 and 30 characters")
|
||||
}
|
||||
|
||||
if strings.Contains(loginID, "@") {
|
||||
return fmt.Errorf("ID cannot be an email format")
|
||||
}
|
||||
|
||||
if email != "" && strings.EqualFold(loginID, email) {
|
||||
return fmt.Errorf("ID cannot be the same as the email address")
|
||||
}
|
||||
|
||||
if phone != "" {
|
||||
normalizedPhone := strings.ReplaceAll(phone, "-", "")
|
||||
normalizedPhone = strings.ReplaceAll(normalizedPhone, " ", "")
|
||||
if strings.HasPrefix(normalizedPhone, "010") {
|
||||
normalizedPhone = "+82" + normalizedPhone[1:]
|
||||
} else if strings.HasPrefix(normalizedPhone, "82") {
|
||||
normalizedPhone = "+" + normalizedPhone
|
||||
}
|
||||
|
||||
if loginID == phone || loginID == normalizedPhone {
|
||||
return fmt.Errorf("ID cannot be the same as the phone number")
|
||||
}
|
||||
}
|
||||
|
||||
isPureNumber := true
|
||||
loginIDDigits := strings.ReplaceAll(loginID, "-", "")
|
||||
loginIDDigits = strings.ReplaceAll(loginIDDigits, " ", "")
|
||||
for _, c := range loginIDDigits {
|
||||
if (c < '0' || c > '9') && c != '+' {
|
||||
isPureNumber = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isPureNumber && len(loginIDDigits) >= 10 && len(loginIDDigits) <= 12 {
|
||||
if strings.HasPrefix(loginIDDigits, "010") || strings.HasPrefix(loginIDDigits, "82") || strings.HasPrefix(loginIDDigits, "+82") {
|
||||
return fmt.Errorf("ID cannot be a phone number format")
|
||||
}
|
||||
}
|
||||
|
||||
reserved := []string{"admin", "system", "root", "master", "superuser", "guest", "operator"}
|
||||
lowerID := strings.ToLower(loginID)
|
||||
for _, r := range reserved {
|
||||
if lowerID == r {
|
||||
return fmt.Errorf("reserved ID cannot be used")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user