diff --git a/pkg/admin/graphql/user_mutation.go b/pkg/admin/graphql/user_mutation.go index c813e3a208..d37ed02a5b 100644 --- a/pkg/admin/graphql/user_mutation.go +++ b/pkg/admin/graphql/user_mutation.go @@ -261,7 +261,7 @@ var _ = registerMutationField( gqlCtx := GQLContext(p.Context) var channel apimodel.AuthenticatorOOBChannel - if err := phone.EnsureE164(target); err == nil { + if err := phone.LegalAndValidParser.CheckE164(target); err == nil { channel = apimodel.AuthenticatorOOBChannelSMS } else { channel = apimodel.AuthenticatorOOBChannelEmail diff --git a/pkg/auth/webapp/authflow.go b/pkg/auth/webapp/authflow.go index 1dd72414a4..cde0654047 100644 --- a/pkg/auth/webapp/authflow.go +++ b/pkg/auth/webapp/authflow.go @@ -37,7 +37,7 @@ func GetMostAppropriateIdentification(f *authflow.FlowResponse, loginID string, // Else, guess the type lookLikeAPhoneNumber := func(loginID string) bool { - err := phone.EnsureE164(loginID) + err := phone.LegalAndValidParser.CheckE164(loginID) if err == nil { return true } diff --git a/pkg/lib/authn/identity/loginid/normalizer.go b/pkg/lib/authn/identity/loginid/normalizer.go index 91d2f37fef..34d154afde 100644 --- a/pkg/lib/authn/identity/loginid/normalizer.go +++ b/pkg/lib/authn/identity/loginid/normalizer.go @@ -10,6 +10,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/util/phone" ) type Normalizer interface { @@ -31,6 +32,8 @@ func (f *NormalizerFactory) NormalizerWithLoginIDType(loginIDKeyType model.Login return &UsernameNormalizer{ Config: f.Config.Types.Username, } + case model.LoginIDKeyTypePhone: + return &PhoneNumberNormalizer{} } return &NullNormalizer{} @@ -111,6 +114,22 @@ func (n *UsernameNormalizer) ComputeUniqueKey(normalizeLoginID string) (string, return normalizeLoginID, nil } +type PhoneNumberNormalizer struct { +} + +func (n *PhoneNumberNormalizer) Normalize(loginID string) (string, error) { + e164, err := phone.LegalParser.ParseInputPhoneNumber(loginID) + if err != nil { + return "", err + } + + return e164, nil +} + +func (n *PhoneNumberNormalizer) ComputeUniqueKey(normalizeLoginID string) (string, error) { + return normalizeLoginID, nil +} + type NullNormalizer struct{} func (n *NullNormalizer) Normalize(loginID string) (string, error) { diff --git a/pkg/lib/authn/identity/loginid/normalizer_test.go b/pkg/lib/authn/identity/loginid/normalizer_test.go index 7855b08965..8210645fa5 100644 --- a/pkg/lib/authn/identity/loginid/normalizer_test.go +++ b/pkg/lib/authn/identity/loginid/normalizer_test.go @@ -164,4 +164,26 @@ func TestNormalizers(t *testing.T) { } }) }) + + Convey("PhoneNumberNormalizer", t, func() { + Convey("normalize to e164", func() { + cases := []Case{ + {"+85298887766", "+85298887766"}, + { + "+852-98887766", + "+85298887766", + }, + { + "+852-98-88-77-66", + "+85298887766", + }, + } + + n := &PhoneNumberNormalizer{} + + for _, c := range cases { + f(c, n) + } + }) + }) } diff --git a/pkg/lib/authn/stdattrs/normalizer.go b/pkg/lib/authn/stdattrs/normalizer.go index 65c9e0e9ff..b093799130 100644 --- a/pkg/lib/authn/stdattrs/normalizer.go +++ b/pkg/lib/authn/stdattrs/normalizer.go @@ -51,12 +51,14 @@ func (n *Normalizer) normalizeEmail(t T) error { return nil } -func normalizePhoneNumber(t T) error { +func (n *Normalizer) normalizePhoneNumber(t T) error { if phoneNumber, ok := t[PhoneNumber].(string); ok && phoneNumber != "" { - err := phone.EnsureE164(phoneNumber) + phoneNumber, err := phone.LegalParser.ParseInputPhoneNumber(phoneNumber) if err != nil { return err } + + t[PhoneNumber] = phoneNumber } else { delete(t, PhoneNumber) } @@ -133,7 +135,7 @@ func (n *Normalizer) Normalize(t T) error { return err } - err = normalizePhoneNumber(t) + err = n.normalizePhoneNumber(t) if err != nil { return err } diff --git a/pkg/lib/elasticsearch/model.go b/pkg/lib/elasticsearch/model.go index 20c00aa875..1c300da271 100644 --- a/pkg/lib/elasticsearch/model.go +++ b/pkg/lib/elasticsearch/model.go @@ -78,7 +78,7 @@ func RawToSource(raw *model.ElasticsearchUserRaw) *model.ElasticsearchUserSource var phoneNumberCountryCode []string var phoneNumberNationalNumber []string for _, phoneNumber := range raw.PhoneNumber { - nationalNumber, callingCode, err := phone.ParseE164ToCallingCodeAndNumber(phoneNumber) + nationalNumber, callingCode, err := phone.LegalAndValidParser.SplitE164(phoneNumber) if err == nil { phoneNumberCountryCode = append(phoneNumberCountryCode, callingCode) phoneNumberNationalNumber = append(phoneNumberNationalNumber, nationalNumber) diff --git a/pkg/lib/usage/count_collector.go b/pkg/lib/usage/count_collector.go index 0dfa4c7b15..f4d6f071ae 100644 --- a/pkg/lib/usage/count_collector.go +++ b/pkg/lib/usage/count_collector.go @@ -300,7 +300,8 @@ func (c *CountCollector) querySMSCount(appID string, rangeFrom *time.Time, range } e164 := payload.Recipient - isNorthAmericaNumber, err := phoneutil.IsNorthAmericaNumber(e164) + + isNorthAmericaNumber, err := phoneutil.LegalAndValidParser.IsNorthAmericaNumber(e164) if err != nil { return nil, fmt.Errorf("usage: failed to parse sms recipient %w", err) } @@ -345,7 +346,7 @@ func (c *CountCollector) queryWhatsappCount(appID string, rangeFrom *time.Time, e164 := payload.Recipient - isNorthAmericaNumber, err := phoneutil.IsNorthAmericaNumber(e164) + isNorthAmericaNumber, err := phoneutil.LegalAndValidParser.IsNorthAmericaNumber(e164) if err != nil { return nil, fmt.Errorf("usage: failed to parse whatsapp recipient %w", err) } diff --git a/pkg/util/phone/legal_and_valid_parser.go b/pkg/util/phone/legal_and_valid_parser.go new file mode 100644 index 0000000000..754d2a11eb --- /dev/null +++ b/pkg/util/phone/legal_and_valid_parser.go @@ -0,0 +1,72 @@ +package phone + +import ( + "regexp" + "strconv" + + "github.com/nyaruka/phonenumbers" +) + +// LegalAndValidParser parses a legal and valid phone number. A legal and valid phone number is a phone number that passes phonenumbers.Parse() and phonenumbers.IsValidNumber(). +type legalAndValidParser struct{} + +func (p *legalAndValidParser) ParseInputPhoneNumber(phone string) (e164 string, err error) { + isNumericString, _ := regexp.Match(`^\+[0-9\ \-]*$`, []byte(phone)) + if !isNumericString { + err = ErrNotInE164Format + return + } + num, err := phonenumbers.Parse(phone, "") + if err != nil { + return + } + isPhoneValid := phonenumbers.IsValidNumber(num) + if !isPhoneValid { + err = ErrPhoneNumberInvalid + return + } + e164 = phonenumbers.Format(num, phonenumbers.E164) + return +} + +func (p *legalAndValidParser) ParseCountryCallingCodeAndNationalNumber(nationalNumber string, countryCallingCode string) (e164 string, err error) { + rawInput := combineCallingCodeWithNumber(nationalNumber, countryCallingCode) + e164, err = p.ParseInputPhoneNumber(rawInput) + return + +} + +func (p *legalAndValidParser) SplitE164(e164 string) (nationalNumber string, countryCallingCode string, err error) { + err = p.CheckE164(e164) + if err != nil { + return + } + + num, err := phonenumbers.Parse(e164, "") + if err != nil { + return + } + countryCallingCode = strconv.Itoa(int(num.GetCountryCode())) + nationalNumber = phonenumbers.GetNationalSignificantNumber(num) + return +} + +func (p *legalAndValidParser) CheckE164(phone string) error { + formatted, err := p.ParseInputPhoneNumber(phone) + if err != nil { + return err + } + if formatted != phone { + return ErrNotInE164Format + } + return nil +} + +func (p *legalAndValidParser) IsNorthAmericaNumber(e164 string) (bool, error) { + _, callingCode, err := p.SplitE164(e164) + if err != nil { + return false, err + } + + return callingCode == "1", nil +} diff --git a/pkg/util/phone/legal_and_valid_parser_test.go b/pkg/util/phone/legal_and_valid_parser_test.go new file mode 100644 index 0000000000..3014ec285a --- /dev/null +++ b/pkg/util/phone/legal_and_valid_parser_test.go @@ -0,0 +1,137 @@ +package phone + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestLegalAndValidParser(t *testing.T) { + parser := &legalAndValidParser{} + Convey("Phone", t, func() { + Convey("checkE164", func() { + good := "+85223456789" + So(parser.CheckE164(good), ShouldBeNil) + + bad := " +85223456789 " + So(parser.CheckE164(bad), ShouldBeError, "not in E.164 format") + + withLetter := "+85222a" + So(parser.CheckE164(withLetter), ShouldBeError, "not in E.164 format") + + invalid := "+85212345678" + So(parser.CheckE164(invalid), ShouldBeError, "invalid phone number") + + tooShort := "+85222" + So(parser.CheckE164(tooShort), ShouldBeError, "invalid phone number") + + nonsense := "a" + So(parser.CheckE164(nonsense), ShouldNotBeNil) + }) + + Convey("ParseCountryCallingCodeAndNationalNumber", func() { + Convey("valid cases", func() { + check := func(nationalNumber, callingCode, e164 string) { + actual, err := parser.ParseCountryCallingCodeAndNationalNumber(nationalNumber, callingCode) + if e164 == "" { + So(err, ShouldNotBeNil) + } else { + So(actual, ShouldEqual, e164) + } + } + // calling code can have optional + sign + check("98887766", "+852", "+85298887766") + check("98887766", "852", "+85298887766") + + // national number can have spaces in between + check("9888 7766", "852", "+85298887766") + check(" 9888 7766 ", "852", "+85298887766") + + // national number can have hyphens in between + check("9888-7766", "852", "+85298887766") + check(" 9888-7766 ", "852", "+85298887766") + check("98-88-77-66", "852", "+85298887766") + check("9-8-8-8-7-7-6-6", "852", "+85298887766") + check("9 - 8 - 8 - 8 - 7 - 7 - 6 - 6 ", "852", "+85298887766") + + // calling code can have leading or trailing spaces + check("98887766", " +852 ", "+85298887766") + check("98887766", " 852 ", "+85298887766") + + // calling code can have spaces or hyphens in between + check("98887766", "8 52", "+85298887766") + check("98887766", "8-52", "+85298887766") + check("98887766", " 8-5-2- ", "+85298887766") + }) + + Convey("should not accept non-numeric character(s)", func() { + _, err := parser.ParseCountryCallingCodeAndNationalNumber("asdf", "+852") + So(err, ShouldBeError, "not in E.164 format") + }) + + Convey("invalid, (+852) phone number does not begin with 1", func() { + _, err := parser.ParseCountryCallingCodeAndNationalNumber("12345678", "+852") + So(err, ShouldBeError, "invalid phone number") + }) + + Convey("invalid, (+852) phone number is 8 digit long", func() { + _, err := parser.ParseCountryCallingCodeAndNationalNumber("6234567", "+852") + So(err, ShouldBeError, "invalid phone number") + }) + }) + + Convey("SplitE164", func() { + Convey("valid cases", func() { + check := func(e164, nationalNumber, callingCode string) { + actualNationalNumber, actualCallingCode, err := parser.SplitE164(e164) + So(actualNationalNumber, ShouldEqual, nationalNumber) + So(actualCallingCode, ShouldEqual, callingCode) + So(err, ShouldBeNil) + } + + check("+61401123456", "401123456", "61") + check("+85298887766", "98887766", "852") + }) + + Convey("invalid, not in E.164 format", func() { + check := func(input string) { + _, _, err := parser.SplitE164(input) + So(err, ShouldBeError, "not in E.164 format") + } + + check("unknown") + check("85298887766") + }) + + Convey("invalid, invalid phone number", func() { + check := func(input string) { + _, _, err := parser.SplitE164(input) + So(err, ShouldBeError, "invalid phone number") + } + + check("+85212345678") + check("+852123456") + }) + }) + + Convey("IsNorthAmericaNumber", func() { + check := func(e164 string, expected bool, errStr string) { + actual, err := parser.IsNorthAmericaNumber(e164) + if errStr == "" { + So(expected, ShouldEqual, actual) + So(err, ShouldBeNil) + } else { + So(err, ShouldBeError, errStr) + } + } + + check("+12015550123", true, "") + check("+18195555555", true, "") + check("+61401123456", false, "") + check("+85298887766", false, "") + check("+85212345678", false, "invalid phone number") + check("+85223456789 ", false, "not in E.164 format") + check("", false, "not in E.164 format") + }) + }) +} diff --git a/pkg/util/phone/legal_parser.go b/pkg/util/phone/legal_parser.go new file mode 100644 index 0000000000..b9078623c3 --- /dev/null +++ b/pkg/util/phone/legal_parser.go @@ -0,0 +1,67 @@ +package phone + +import ( + "regexp" + "strconv" + + "github.com/nyaruka/phonenumbers" +) + +// LegalParser parses a legal phone number. A legal phone number is a phone number that passes phonenumbers.Parse(). +type legalParser struct{} + +func (p *legalParser) ParseInputPhoneNumber(phone string) (e164 string, err error) { + isNumericString, _ := regexp.Match(`^\+[0-9\ \-]*$`, []byte(phone)) + if !isNumericString { + err = ErrNotInE164Format + return + } + num, err := phonenumbers.Parse(phone, "") + if err != nil { + return + } + e164 = phonenumbers.Format(num, phonenumbers.E164) + return +} + +func (p *legalParser) ParseCountryCallingCodeAndNationalNumber(nationalNumber string, countryCallingCode string) (e164 string, err error) { + rawInput := combineCallingCodeWithNumber(nationalNumber, countryCallingCode) + e164, err = p.ParseInputPhoneNumber(rawInput) + return + +} + +func (p *legalParser) SplitE164(e164 string) (nationalNumber string, countryCallingCode string, err error) { + err = p.CheckE164(e164) + if err != nil { + return + } + + num, err := phonenumbers.Parse(e164, "") + if err != nil { + return + } + countryCallingCode = strconv.Itoa(int(num.GetCountryCode())) + nationalNumber = phonenumbers.GetNationalSignificantNumber(num) + return +} + +func (p *legalParser) CheckE164(phone string) error { + formatted, err := p.ParseInputPhoneNumber(phone) + if err != nil { + return err + } + if formatted != phone { + return ErrNotInE164Format + } + return nil +} + +func (p *legalParser) IsNorthAmericaNumber(e164 string) (bool, error) { + _, callingCode, err := p.SplitE164(e164) + if err != nil { + return false, err + } + + return callingCode == "1", nil +} diff --git a/pkg/util/phone/legal_parser_test.go b/pkg/util/phone/legal_parser_test.go new file mode 100644 index 0000000000..0cb196930e --- /dev/null +++ b/pkg/util/phone/legal_parser_test.go @@ -0,0 +1,32 @@ +package phone + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestLegalParser(t *testing.T) { + parser := &legalParser{} + Convey("Phone", t, func() { + Convey("checkE164", func() { + good := "+85223456789" + So(parser.CheckE164(good), ShouldBeNil) + + bad := " +85223456789 " + So(parser.CheckE164(bad), ShouldBeError, "not in E.164 format") + + withLetter := "+85222a" + So(parser.CheckE164(withLetter), ShouldBeError, "not in E.164 format") + + invalid := "+85212345678" + So(parser.CheckE164(invalid), ShouldBeNil) + + tooShort := "+85222" + So(parser.CheckE164(tooShort), ShouldBeNil) + + nonsense := "a" + So(parser.CheckE164(nonsense), ShouldNotBeNil) + }) + }) +} diff --git a/pkg/util/phone/parser.go b/pkg/util/phone/parser.go new file mode 100644 index 0000000000..614990723c --- /dev/null +++ b/pkg/util/phone/parser.go @@ -0,0 +1,12 @@ +package phone + +type Parser interface { + ParseInputPhoneNumber(phone string) (e164 string, err error) + ParseCountryCallingCodeAndNationalNumber(nationalNumber string, countryCallingCode string) (e164 string, err error) + SplitE164(e164 string) (nationalNumber string, countryCallingCode string, err error) + CheckE164(phone string) error + IsNorthAmericaNumber(e164 string) (bool, error) +} + +var LegalParser Parser = &legalParser{} +var LegalAndValidParser Parser = &legalAndValidParser{} diff --git a/pkg/util/phone/phone.go b/pkg/util/phone/phone.go index e8d20d0d2d..6d6db5b6d8 100644 --- a/pkg/util/phone/phone.go +++ b/pkg/util/phone/phone.go @@ -3,7 +3,6 @@ package phone import ( "errors" "fmt" - "regexp" "strconv" "strings" @@ -25,70 +24,6 @@ func combineCallingCodeWithNumber(nationalNumber string, callingCode string) str return fmt.Sprintf("%s%s", callingCode, nationalNumber) } -// According to https://godoc.org/github.com/nyaruka/phonenumbers#Parse, -// no validation is performed during parsing, so we need to call IsValidNumber to check -func ParseCombinedPhoneNumber(phone string) (e164 string, err error) { - // check if input contains non-numeric character(s) - // The nationalNumber part of phone is parsed to uint64, - // letters in input phone number will be parsed successfully. - isNumericString, _ := regexp.Match(`^\+[0-9\ \-]*$`, []byte(phone)) - if !isNumericString { - err = ErrNotInE164Format - return - } - num, err := phonenumbers.Parse(phone, "") - if err != nil { - return - } - isPhoneValid := phonenumbers.IsValidNumber(num) - if !isPhoneValid { - err = ErrPhoneNumberInvalid - return - } - e164 = phonenumbers.Format(num, phonenumbers.E164) - return -} - -// ParseE164ToCallingCodeAndNumber parse E.164 format phone number to national number and calling code. -func ParseE164ToCallingCodeAndNumber(e164 string) (nationalNumber string, callingCode string, err error) { - err = EnsureE164(e164) - if err != nil { - return - } - - num, err := phonenumbers.Parse(e164, "") - if err != nil { - return - } - isPhoneValid := phonenumbers.IsValidNumber(num) - if !isPhoneValid { - err = ErrPhoneNumberInvalid - return - } - callingCode = strconv.Itoa(int(num.GetCountryCode())) - nationalNumber = phonenumbers.GetNationalSignificantNumber(num) - return -} - -// Parse to E164 format -func Parse(nationalNumber string, callingCode string) (e164 string, err error) { - rawInput := combineCallingCodeWithNumber(nationalNumber, callingCode) - e164, err = ParseCombinedPhoneNumber(rawInput) - return -} - -// EnsureE164 ensures the given phone is in E.164 format. -func EnsureE164(phone string) error { - formatted, err := ParseCombinedPhoneNumber(phone) - if err != nil { - return err - } - if formatted != phone { - return ErrNotInE164Format - } - return nil -} - // Mask masks the give phone number. func Mask(phone string) string { return MaskWithCustomRune(phone, '*') @@ -120,12 +55,3 @@ func MaskWithCustomRune(phone string, r rune) string { return buf.String() } - -func IsNorthAmericaNumber(e164 string) (bool, error) { - _, callingCode, err := ParseE164ToCallingCodeAndNumber(e164) - if err != nil { - return false, err - } - - return callingCode == "1", nil -} diff --git a/pkg/util/phone/phone_test.go b/pkg/util/phone/phone_test.go index 68b0307a49..f2fe47fdbd 100644 --- a/pkg/util/phone/phone_test.go +++ b/pkg/util/phone/phone_test.go @@ -8,109 +8,6 @@ import ( func TestPhone(t *testing.T) { Convey("Phone", t, func() { - // EnsureE164 uses same validation logic as Parse - Convey("EnsureE164", func() { - good := "+85223456789" - So(EnsureE164(good), ShouldBeNil) - - bad := " +85223456789 " - So(EnsureE164(bad), ShouldBeError, "not in E.164 format") - - withLetter := "+85222a" - So(EnsureE164(withLetter), ShouldBeError, "not in E.164 format") - - tooShort := "+85222" - So(EnsureE164(tooShort), ShouldBeError, "invalid phone number") - - nonsense := "a" - So(EnsureE164(nonsense), ShouldNotBeNil) - }) - - Convey("Parse", func() { - Convey("valid cases", func() { - check := func(nationalNumber, callingCode, e164 string) { - actual, err := Parse(nationalNumber, callingCode) - if e164 == "" { - So(err, ShouldNotBeNil) - } else { - So(actual, ShouldEqual, e164) - } - } - // calling code can have optional + sign - check("98887766", "+852", "+85298887766") - check("98887766", "852", "+85298887766") - - // national number can have spaces in between - check("9888 7766", "852", "+85298887766") - check(" 9888 7766 ", "852", "+85298887766") - - // national number can have hyphens in between - check("9888-7766", "852", "+85298887766") - check(" 9888-7766 ", "852", "+85298887766") - check("98-88-77-66", "852", "+85298887766") - check("9-8-8-8-7-7-6-6", "852", "+85298887766") - check("9 - 8 - 8 - 8 - 7 - 7 - 6 - 6 ", "852", "+85298887766") - - // calling code can have leading or trailing spaces - check("98887766", " +852 ", "+85298887766") - check("98887766", " 852 ", "+85298887766") - - // calling code can have spaces or hyphens in between - check("98887766", "8 52", "+85298887766") - check("98887766", "8-52", "+85298887766") - check("98887766", " 8-5-2- ", "+85298887766") - }) - - Convey("should not accept non-numeric character(s)", func() { - _, err := Parse("asdf", "+852") - So(err, ShouldBeError, "not in E.164 format") - }) - - Convey("invalid, (+852) phone number does not begin with 1", func() { - _, err := Parse("12345678", "+852") - So(err, ShouldBeError, "invalid phone number") - }) - - Convey("invalid, (+852) phone number is 8 digit long", func() { - _, err := Parse("6234567", "+852") - So(err, ShouldBeError, "invalid phone number") - }) - }) - - Convey("ParseE164ToCallingCodeAndNumber", func() { - Convey("valid cases", func() { - check := func(e164, nationalNumber, callingCode string) { - actualNationalNumber, actualCallingCode, err := ParseE164ToCallingCodeAndNumber(e164) - So(actualNationalNumber, ShouldEqual, nationalNumber) - So(actualCallingCode, ShouldEqual, callingCode) - So(err, ShouldBeNil) - } - - check("+61401123456", "401123456", "61") - check("+85298887766", "98887766", "852") - }) - - Convey("invalid, not in E.164 format", func() { - check := func(input string) { - _, _, err := ParseE164ToCallingCodeAndNumber(input) - So(err, ShouldBeError, "not in E.164 format") - } - - check("unknown") - check("85298887766") - }) - - Convey("invalid, invalid phone number", func() { - check := func(input string) { - _, _, err := ParseE164ToCallingCodeAndNumber(input) - So(err, ShouldBeError, "invalid phone number") - } - - check("+85212345678") - check("+852123456") - }) - }) - Convey("Mask", func() { phone := "+85223456789" So(Mask(phone), ShouldEqual, "+8522345****") @@ -120,25 +17,5 @@ func TestPhone(t *testing.T) { phone := "+85223456789" So(MaskWithCustomRune(phone, 'x'), ShouldEqual, "+8522345xxxx") }) - - Convey("IsNorthAmericaNumber", func() { - check := func(e164 string, expected bool, errStr string) { - actual, err := IsNorthAmericaNumber(e164) - if errStr == "" { - So(expected, ShouldEqual, actual) - So(err, ShouldBeNil) - } else { - So(err, ShouldBeError, errStr) - } - } - - check("+12015550123", true, "") - check("+18195555555", true, "") - check("+61401123456", false, "") - check("+85298887766", false, "") - check("+85212345678", false, "invalid phone number") - check("+85223456789 ", false, "not in E.164 format") - check("", false, "not in E.164 format") - }) }) } diff --git a/pkg/util/validation/formats.go b/pkg/util/validation/formats.go index cfb83730b7..a8a218a155 100644 --- a/pkg/util/validation/formats.go +++ b/pkg/util/validation/formats.go @@ -63,7 +63,7 @@ func (f FormatPhone) CheckFormat(value interface{}) error { if !ok { return nil } - return phone.EnsureE164(str) + return phone.LegalAndValidParser.CheckE164(str) } // FormatEmail checks if input is an email address.