diff --git a/internal/domain/human_email.go b/internal/domain/human_email.go index 489787c6c..25988a1d5 100644 --- a/internal/domain/human_email.go +++ b/internal/domain/human_email.go @@ -1,8 +1,9 @@ package domain import ( + "errors" "io" - "regexp" + "net/mail" "strings" "time" @@ -11,18 +12,20 @@ import ( "github.com/zitadel/zitadel/internal/zerrors" ) -var ( - emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") -) - type EmailAddress string +var errEmailNameProvided = errors.New("email with name provided") + func (e EmailAddress) Validate() error { if e == "" { return zerrors.ThrowInvalidArgument(nil, "EMAIL-spblu", "Errors.User.Email.Empty") } - if !emailRegex.MatchString(string(e)) { - return zerrors.ThrowInvalidArgument(nil, "EMAIL-599BI", "Errors.User.Email.Invalid") + p, err := mail.ParseAddress(string(e)) + if err != nil { + return zerrors.ThrowInvalidArgument(err, "EMAIL-599BI", "Errors.User.Email.Invalid") + } + if p.Name != "" { + return zerrors.ThrowInvalidArgument(errEmailNameProvided, "EMAIL-599GV", "Errors.User.Email.Invalid") } return nil } diff --git a/internal/domain/human_email_test.go b/internal/domain/human_email_test.go index 42aa77fd7..b21aa2c69 100644 --- a/internal/domain/human_email_test.go +++ b/internal/domain/human_email_test.go @@ -68,6 +68,20 @@ func TestEmailValid(t *testing.T) { }, result: true, }, + { + name: "email UTF-8, valid", // https://github.com/zitadel/zitadel/issues/9821 + args: args{ + email: &Email{EmailAddress: "abc@sünde.com"}, + }, + result: true, + }, + { + name: "email with name, invalid", + args: args{ + email: &Email{EmailAddress: "John Doe "}, + }, + result: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 8f9934f83..f533f7cde 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -8579,7 +8579,7 @@ message AddHumanUserRequest { zitadel.user.v1.Gender gender = 6; } message Email { - string email = 1 [(validate.rules).string.email = true]; + string email = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; bool is_email_verified = 2; } message Phone { @@ -8660,7 +8660,7 @@ message ImportHumanUserRequest { } }; string email = 1 [ - (validate.rules).string.email = true, + (validate.rules).string = {min_len: 1, max_len: 200}, (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"minnie@mouse.com\""; @@ -9139,7 +9139,7 @@ message UpdateHumanEmailRequest { string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string email = 2 [ - (validate.rules).string.email = true, + (validate.rules).string = {min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"gigi@zitadel.com\""; } @@ -9154,7 +9154,7 @@ message UpdateHumanEmailResponse { message ResendHumanInitializationRequest { string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string email = 2 [ - (validate.rules).string = {ignore_empty: true, email: true}, + (validate.rules).string = {ignore_empty: true, min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"updated-email@test.com\""; description: "Send a new email address if the one set on the user is wrong or has a typo."