diff --git a/recipe/emailpassword/api/implementation.go b/recipe/emailpassword/api/implementation.go index 09186496..9ab51cc6 100644 --- a/recipe/emailpassword/api/implementation.go +++ b/recipe/emailpassword/api/implementation.go @@ -40,7 +40,11 @@ func MakeAPIImplementation() epmodels.APIInterface { var email string for _, formField := range formFields { if formField.ID == "email" { - email = formField.Value + valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + if parseErr != nil { + return epmodels.GeneratePasswordResetTokenPOSTResponse{}, parseErr + } + email = valueAsString } } @@ -102,7 +106,11 @@ func MakeAPIImplementation() epmodels.APIInterface { var newPassword string for _, formField := range formFields { if formField.ID == "password" { - newPassword = formField.Value + valueAsString, parseErr := withValueAsString(formField.Value, "Password value needs to be a string") + if parseErr != nil { + return epmodels.ResetPasswordPOSTResponse{}, parseErr + } + newPassword = valueAsString } } @@ -127,9 +135,17 @@ func MakeAPIImplementation() epmodels.APIInterface { var password string for _, formField := range formFields { if formField.ID == "email" { - email = formField.Value + valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + if parseErr != nil { + return epmodels.SignInPOSTResponse{}, parseErr + } + email = valueAsString } else if formField.ID == "password" { - password = formField.Value + valueAsString, parseErr := withValueAsString(formField.Value, "Password value needs to be a string") + if parseErr != nil { + return epmodels.SignInPOSTResponse{}, parseErr + } + password = valueAsString } } @@ -165,9 +181,17 @@ func MakeAPIImplementation() epmodels.APIInterface { var password string for _, formField := range formFields { if formField.ID == "email" { - email = formField.Value + valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + if parseErr != nil { + return epmodels.SignUpPOSTResponse{}, parseErr + } + email = valueAsString } else if formField.ID == "password" { - password = formField.Value + valueAsString, parseErr := withValueAsString(formField.Value, "Password value needs to be a string") + if parseErr != nil { + return epmodels.SignUpPOSTResponse{}, parseErr + } + password = valueAsString } } diff --git a/recipe/emailpassword/api/utils.go b/recipe/emailpassword/api/utils.go index 6b7a5e0c..577ef715 100644 --- a/recipe/emailpassword/api/utils.go +++ b/recipe/emailpassword/api/utils.go @@ -26,6 +26,16 @@ import ( "github.com/supertokens/supertokens-golang/supertokens" ) +func withValueAsString(emailValue interface{}, errorStr string) (string, error) { + // Throw error if the value is not a string + valueAsString, asStrOk := emailValue.(string) + if !asStrOk { + return "", fmt.Errorf(errorStr) + } + + return valueAsString, nil +} + func validateFormFieldsOrThrowError(configFormFields []epmodels.NormalisedFormField, formFieldsRaw interface{}, tenantId string) ([]epmodels.TypeFormField, error) { if formFieldsRaw == nil { return nil, supertokens.BadInputError{ @@ -56,14 +66,6 @@ func validateFormFieldsOrThrowError(configFormFields []epmodels.NormalisedFormFi } } - if rawFormField.(map[string]interface{})["value"] != nil { - if _, ok := rawFormField.(map[string]interface{})["value"].(string); !ok { - return nil, supertokens.BadInputError{ - Msg: "formFields must be an array of objects containing id and value of type string", - } - } - } - jsonformField, err := json.Marshal(rawFormField) if err != nil { return nil, err @@ -75,9 +77,15 @@ func validateFormFieldsOrThrowError(configFormFields []epmodels.NormalisedFormFi } if formField.ID == "email" { + valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + if parseErr != nil { + return nil, supertokens.BadInputError{ + Msg: "Email value must be a string", + } + } formFields = append(formFields, epmodels.TypeFormField{ ID: formField.ID, - Value: strings.TrimSpace(formField.Value), + Value: strings.TrimSpace(valueAsString), }) } else { formFields = append(formFields, epmodels.TypeFormField{ @@ -106,7 +114,16 @@ func validateFormOrThrowError(configFormFields []epmodels.NormalisedFormField, i } } - isValidInput := input.Value != "" + isValidInput := true + if input.Value == nil { + isValidInput = false + } else { + // If it is a string, it shouldn't be empty. + valueAsStr, err := withValueAsString(input.Value, "") + if err == nil && strings.TrimSpace(valueAsStr) == "" { + isValidInput = false + } + } // If the field is not option and input is invalid, we should // throw a validation error. diff --git a/recipe/emailpassword/authFlow_test.go b/recipe/emailpassword/authFlow_test.go index 237a21b7..375beae0 100644 --- a/recipe/emailpassword/authFlow_test.go +++ b/recipe/emailpassword/authFlow_test.go @@ -38,6 +38,94 @@ import ( "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestGoodCaseInputWithOptionalAndBoolean(t *testing.T) { + optionalVal := true + configValue := supertokens.TypeInput{ + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: "http://localhost:8080", + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + Init(&epmodels.TypeInput{ + SignUpFeature: &epmodels.TypeInputSignUp{ + FormFields: []epmodels.TypeInputFormField{ + { + ID: "autoVerify", + Optional: &optionalVal, + }, + }, + }, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + formFields := map[string][]map[string]interface{}{ + "formFields": { + { + "id": "password", + "value": "validpass123", + }, + { + "id": "email", + "value": "random@gmail.com", + }, + { + "id": "autoVerify", + "value": false, + }, + }, + } + + postBody, err := json.Marshal(formFields) + if err != nil { + t.Error(err.Error()) + } + + resp, err := http.Post(testServer.URL+"/auth/signup", "application/json", bytes.NewBuffer(postBody)) + + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, 200, resp.StatusCode) + + dataInBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Error(err.Error()) + } + resp.Body.Close() + + var data map[string]interface{} + err = json.Unmarshal(dataInBytes, &data) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", data["status"]) + assert.NotNil(t, data["user"].(map[string]interface{})["id"]) + assert.Equal(t, "random@gmail.com", data["user"].(map[string]interface{})["email"]) +} + func TestRightRidButRecipeMissingReturns404(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ diff --git a/recipe/emailpassword/epmodels/models.go b/recipe/emailpassword/epmodels/models.go index 082be12b..8dae31d1 100644 --- a/recipe/emailpassword/epmodels/models.go +++ b/recipe/emailpassword/epmodels/models.go @@ -75,8 +75,8 @@ type TypeInput struct { } type TypeFormField struct { - ID string `json:"id"` - Value string `json:"value"` + ID string `json:"id"` + Value interface{} `json:"value"` } type CreateResetPasswordLinkResponse struct { diff --git a/recipe/emailpassword/utils.go b/recipe/emailpassword/utils.go index d6ea3d7f..2336fbed 100644 --- a/recipe/emailpassword/utils.go +++ b/recipe/emailpassword/utils.go @@ -217,6 +217,11 @@ func defaultPasswordValidator(value interface{}, tenantId string) *string { // length >= 8 && < 100 // must have a number and a character + if (value) == nil { + msg := "Field is not optional" + return &msg + } + if reflect.TypeOf(value).Kind() != reflect.String { msg := "Development bug: Please make sure the password field yields a string" return &msg @@ -243,6 +248,11 @@ func defaultPasswordValidator(value interface{}, tenantId string) *string { } func defaultEmailValidator(value interface{}, tenantId string) *string { + if (value) == nil { + msg := "Field is not optional" + return &msg + } + if reflect.TypeOf(value).Kind() != reflect.String { msg := "Development bug: Please make sure the email field yields a string" return &msg