mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-25 08:42:16 +00:00
<!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved - This adds rudimentary country code handling for LoginV1 SMS OTP - Previously defaulting to CH with no prompt to user causing e.g UK (+44) based numbers to be re-routed to CH. (+41) # Additional Context - Support for SMS OTP is not complete for LoginV2 (according to latest support email) Tested with Twilio on both UK + DE # . Appears to work. - Closes #8891 <img width="544" height="485" alt="image" src="https://github.com/user-attachments/assets/84d31bd8-8863-465f-8860-b51602439dfb" /> <img width="858" height="1298" alt="Screenshot 2026-01-16 at 11 23 22" src="https://github.com/user-attachments/assets/4bf16f0b-2fec-4d63-95b0-f0b73f3a131a" /> --------- Co-authored-by: Livio Spring <[email protected]> Co-authored-by: Silvan <[email protected]>
126 lines
2.8 KiB
Go
126 lines
2.8 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/ttacon/libphonenumber"
|
|
"golang.org/x/text/language"
|
|
"golang.org/x/text/language/display"
|
|
|
|
"github.com/zitadel/zitadel/internal/crypto"
|
|
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
const defaultRegion = "CH"
|
|
|
|
type PhoneNumber string
|
|
|
|
func (p PhoneNumber) Normalize() (PhoneNumber, error) {
|
|
return p.NormalizeWithRegion(defaultRegion)
|
|
}
|
|
|
|
func (p PhoneNumber) NormalizeWithRegion(region string) (PhoneNumber, error) {
|
|
if p == "" {
|
|
return p, zerrors.ThrowInvalidArgument(nil, "PHONE-Zt0NV", "Errors.User.Phone.Empty")
|
|
}
|
|
if region == "" {
|
|
region = defaultRegion
|
|
}
|
|
phoneNr, err := libphonenumber.Parse(string(p), region)
|
|
if err != nil {
|
|
return p, zerrors.ThrowInvalidArgument(err, "PHONE-so0wa", "Errors.User.Phone.Invalid")
|
|
}
|
|
return PhoneNumber(libphonenumber.Format(phoneNr, libphonenumber.E164)), nil
|
|
}
|
|
|
|
type Phone struct {
|
|
es_models.ObjectRoot
|
|
|
|
PhoneNumber PhoneNumber
|
|
IsPhoneVerified bool
|
|
// PlainCode is set by the command and can be used to return it to the caller (API)
|
|
PlainCode *string
|
|
}
|
|
|
|
type PhoneCode struct {
|
|
es_models.ObjectRoot
|
|
|
|
Code *crypto.CryptoValue
|
|
Expiry time.Duration
|
|
}
|
|
|
|
func (p *Phone) Normalize() error {
|
|
if p == nil {
|
|
return zerrors.ThrowInvalidArgument(nil, "PHONE-YlbwO", "Errors.User.Phone.Empty")
|
|
}
|
|
normalizedNumber, err := p.PhoneNumber.Normalize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Issue for avoiding mutating state: https://github.com/zitadel/zitadel/issues/5412
|
|
p.PhoneNumber = normalizedNumber
|
|
return nil
|
|
}
|
|
|
|
type PhoneState int32
|
|
|
|
const (
|
|
PhoneStateUnspecified PhoneState = iota
|
|
PhoneStateActive
|
|
PhoneStateRemoved
|
|
|
|
phoneStateCount
|
|
)
|
|
|
|
func (s PhoneState) Valid() bool {
|
|
return s >= 0 && s < phoneStateCount
|
|
}
|
|
|
|
func (s PhoneState) Exists() bool {
|
|
return s == PhoneStateActive
|
|
}
|
|
|
|
// CountryCode represents a country with its ISO code and calling code
|
|
type CountryCode struct {
|
|
ISOCode string
|
|
CallingCode string
|
|
Name string
|
|
}
|
|
|
|
// GetCountryCodes returns a list of country codes for phone number input using libphonenumber
|
|
func GetCountryCodes(lang language.Tag) []CountryCode {
|
|
regions := libphonenumber.GetSupportedRegions()
|
|
namer := display.Regions(lang)
|
|
|
|
codes := make([]CountryCode, 0, len(regions))
|
|
for region := range regions {
|
|
countryCode := libphonenumber.GetCountryCodeForRegion(region)
|
|
if countryCode == 0 {
|
|
continue
|
|
}
|
|
|
|
// Get the display name for the region
|
|
regionTag, err := language.ParseRegion(region)
|
|
name := region
|
|
if err == nil {
|
|
name = namer.Name(regionTag)
|
|
}
|
|
|
|
codes = append(codes, CountryCode{
|
|
ISOCode: region,
|
|
CallingCode: fmt.Sprintf("+%d", countryCode),
|
|
Name: name,
|
|
})
|
|
}
|
|
|
|
// Sort by country name
|
|
sort.Slice(codes, func(i, j int) bool {
|
|
return codes[i].Name < codes[j].Name
|
|
})
|
|
|
|
return codes
|
|
}
|