diff --git a/cmd/clidoc/main.go b/cmd/clidoc/main.go index a2b1850a87fa..b89092bfa606 100644 --- a/cmd/clidoc/main.go +++ b/cmd/clidoc/main.go @@ -181,6 +181,8 @@ func init() { "NewInfoSelfServiceLoginAAL2CodeAddress": text.NewInfoSelfServiceLoginAAL2CodeAddress("{channel}", "{address}"), "NewErrorCaptchaFailed": text.NewErrorCaptchaFailed(), "NewCaptchaContainerMessage": text.NewCaptchaContainerMessage(), + "NewErrorValidationEmail": text.NewErrorValidationEmail("{value}"), + "NewErrorValidationPhone": text.NewErrorValidationPhone("{value}"), } } diff --git a/selfservice/strategy/link/strategy_verification_test.go b/selfservice/strategy/link/strategy_verification_test.go index ae0e20021338..ac48fc949a75 100644 --- a/selfservice/strategy/link/strategy_verification_test.go +++ b/selfservice/strategy/link/strategy_verification_test.go @@ -11,6 +11,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "sync" "testing" "time" @@ -143,7 +144,7 @@ func TestVerification(t *testing.T) { t.Run("description=should require a valid email to be sent", func(t *testing.T) { check := func(t *testing.T, actual string, value string) { assert.EqualValues(t, string(node.LinkGroup), gjson.Get(actual, "active").String(), "%s", actual) - assert.EqualValues(t, fmt.Sprintf("%q is not valid \"email\"", value), + assert.EqualValues(t, strings.ReplaceAll(fmt.Sprintf("%q is not a valid email address", value), "\"", ""), gjson.Get(actual, "ui.nodes.#(attributes.name==email).messages.0.text").String(), "%s", actual) } diff --git a/text/id.go b/text/id.go index c6d26323f5e2..edd2a47b9263 100644 --- a/text/id.go +++ b/text/id.go @@ -153,6 +153,8 @@ const ( ErrorValidationTraitsMismatch ErrorValidationAccountNotFound ErrorValidationCaptchaError + ErrorValidationEmail + ErrorValidationPhone ) const ( diff --git a/text/id_test.go b/text/id_test.go index 2efe742c667e..9a8c908e4c10 100644 --- a/text/id_test.go +++ b/text/id_test.go @@ -74,4 +74,7 @@ func TestIDs(t *testing.T) { assert.Equal(t, 1070015, int(InfoNodeLabelCaptcha)) assert.Equal(t, 4000038, int(ErrorValidationCaptchaError)) + + assert.Equal(t, 4000039, int(ErrorValidationEmail)) + assert.Equal(t, 4000040, int(ErrorValidationPhone)) } diff --git a/text/message_validation.go b/text/message_validation.go index b8e689deee03..89c401265ab8 100644 --- a/text/message_validation.go +++ b/text/message_validation.go @@ -195,6 +195,28 @@ func NewErrorValidationConstGeneric() *Message { } } +func NewErrorValidationEmail(value string) *Message { + return &Message{ + ID: ErrorValidationEmail, + Text: fmt.Sprintf("%s is not a valid email address", value), + Type: Error, + Context: context(map[string]any{ + "value": value, + }), + } +} + +func NewErrorValidationPhone(value string) *Message { + return &Message{ + ID: ErrorValidationEmail, + Text: fmt.Sprintf("%s is not a valid phone number", value), + Type: Error, + Context: context(map[string]any{ + "value": value, + }), + } +} + func NewErrorValidationPasswordPolicyViolationGeneric(reason string) *Message { return &Message{ ID: ErrorValidationPasswordPolicyViolationGeneric, diff --git a/ui/container/container.go b/ui/container/container.go index d80683cd2800..70ede0e406b6 100644 --- a/ui/container/container.go +++ b/ui/container/container.go @@ -271,6 +271,30 @@ func translateValidationError(err *jsonschema.ValidationError) *text.Message { return text.NewErrorValidationConst(expectedValue) } return text.NewErrorValidationConstGeneric() + case "format": + value, format := "", "" + _, _ = fmt.Sscanf(err.Message, "%s is not valid %s", &value, &format) + if len(value) < 3 { + // This should not happen, because the library returns the message: "value" is not a valid "format" + // But if it does, we can return a generic error instead of panicking later due to index out of range + return text.NewValidationErrorGeneric(err.Message) + } + + // Format is extracted with quotes around the actual string, so we remove all quotes from the string to make it easier to match against + format = strings.ReplaceAll(format, "\"", "") + // The value is user supplied, so it could contain quotes. Replacing all quotes would also remove the user supplied ones. + // While strings with quotes in them are most likely never valid values here, we should still echo the full user entered string to the user to make it easier to understand the error. + // E.g. if we replaced all quotes, the user could see the error valid-email@ory.sh is not a valid email address, if they entered it as valid"-email@ory.sh (note the quote) + value = value[1 : len(value)-1] + + switch format { + case "email": + return text.NewErrorValidationEmail(value) + case "tel": + return text.NewErrorValidationPhone(value) + default: + return text.NewValidationErrorGeneric(err.Message) + } default: return text.NewValidationErrorGeneric(err.Message) }