64 lines
1.8 KiB
Go
64 lines
1.8 KiB
Go
package auth
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"galo/internal/httpx"
|
||
)
|
||
|
||
// Identifier kinds for login and registration.
|
||
const (
|
||
IdentifierEmail = "email"
|
||
IdentifierPhone = "phone"
|
||
)
|
||
|
||
// NormalizePhone validates and canonicalises a phone number to E.164: a leading
|
||
// plus followed by 8–15 digits. It is the single normalisation point used by
|
||
// registration, identifier addition and login, so lookups compare canonical
|
||
// values only.
|
||
func NormalizePhone(raw string) (string, error) {
|
||
s := strings.TrimSpace(raw)
|
||
if s == "" {
|
||
return "", httpx.BadRequest("invalid_phone", "phone is required")
|
||
}
|
||
if !strings.HasPrefix(s, "+") {
|
||
return "", httpx.BadRequest("invalid_phone", "phone must be in E.164 format with a leading +")
|
||
}
|
||
digits := s[1:]
|
||
if len(digits) < 8 || len(digits) > 15 {
|
||
return "", httpx.BadRequest("invalid_phone", "phone must contain between 8 and 15 digits")
|
||
}
|
||
for _, r := range digits {
|
||
if r < '0' || r > '9' {
|
||
return "", httpx.BadRequest("invalid_phone", "phone may contain digits only after the leading +")
|
||
}
|
||
}
|
||
return s, nil
|
||
}
|
||
|
||
func validEmail(s string) bool {
|
||
i := strings.Index(s, "@")
|
||
return i > 0 && i < len(s)-1 && !strings.ContainsAny(s, " \t\r\n")
|
||
}
|
||
|
||
// NormalizeIdentifier classifies a login identifier as email (contains `@`) or
|
||
// phone, returning the canonical value for lookups.
|
||
func NormalizeIdentifier(raw string) (kind, value string, err error) {
|
||
s := strings.TrimSpace(raw)
|
||
if s == "" {
|
||
return "", "", httpx.BadRequest("invalid_identifier", "identifier is required")
|
||
}
|
||
if strings.Contains(s, "@") {
|
||
v := strings.ToLower(s)
|
||
if !validEmail(v) {
|
||
return "", "", httpx.BadRequest("invalid_identifier", "valid email is required")
|
||
}
|
||
return IdentifierEmail, v, nil
|
||
}
|
||
v, err := NormalizePhone(s)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
return IdentifierPhone, v, nil
|
||
}
|