From 4ac3be8bdf785e456f881d41ca5a4a141febd086 Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 10:09:42 +0530 Subject: [PATCH 1/6] Add support for any type in value field instead of only string --- recipe/emailpassword/api/implementation.go | 36 +++++++-- recipe/emailpassword/api/utils.go | 37 ++++++--- recipe/emailpassword/authFlow_test.go | 88 ++++++++++++++++++++++ recipe/emailpassword/epmodels/models.go | 4 +- recipe/emailpassword/utils.go | 10 +++ 5 files changed, 157 insertions(+), 18 deletions(-) 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 From fafe6929c3c9675820069793701f15c6f8c42ef7 Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 10:10:21 +0530 Subject: [PATCH 2/6] Update changelog with changes and version bump --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5803b6f7..986e2de9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.24.3] - 2024-09-24 + +- Adds support for form field related improvements by making fields accept any type of values +- Adds support for optional fields to properly optional + ## [0.24.2] - 2024-09-03 - Fixes memory leak with the JWKS cache. From caea0a888e4b2a52ed8a8a8614d7f4423b00aadc Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 12:58:25 +0530 Subject: [PATCH 3/6] Add more tests for the newly added validations --- recipe/emailpassword/api/implementation.go | 36 ++-- recipe/emailpassword/api/utils.go | 7 +- recipe/emailpassword/authFlow_test.go | 202 +++++++++++++++++++++ recipe/emailpassword/passwordReset_test.go | 92 ++++++++++ 4 files changed, 322 insertions(+), 15 deletions(-) diff --git a/recipe/emailpassword/api/implementation.go b/recipe/emailpassword/api/implementation.go index 9ab51cc6..470d3ad7 100644 --- a/recipe/emailpassword/api/implementation.go +++ b/recipe/emailpassword/api/implementation.go @@ -40,9 +40,11 @@ func MakeAPIImplementation() epmodels.APIInterface { var email string for _, formField := range formFields { if formField.ID == "email" { - valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + valueAsString, parseErr := withValueAsString(formField.Value, "email value needs to be a string") if parseErr != nil { - return epmodels.GeneratePasswordResetTokenPOSTResponse{}, parseErr + return epmodels.GeneratePasswordResetTokenPOSTResponse{ + GeneralError: &supertokens.GeneralErrorResponse{Message: parseErr.Error()}, + }, nil } email = valueAsString } @@ -106,9 +108,11 @@ func MakeAPIImplementation() epmodels.APIInterface { var newPassword string for _, formField := range formFields { if formField.ID == "password" { - valueAsString, parseErr := withValueAsString(formField.Value, "Password value needs to be a string") + valueAsString, parseErr := withValueAsString(formField.Value, "password value needs to be a string") if parseErr != nil { - return epmodels.ResetPasswordPOSTResponse{}, parseErr + return epmodels.ResetPasswordPOSTResponse{ + GeneralError: &supertokens.GeneralErrorResponse{Message: parseErr.Error()}, + }, nil } newPassword = valueAsString } @@ -135,15 +139,19 @@ func MakeAPIImplementation() epmodels.APIInterface { var password string for _, formField := range formFields { if formField.ID == "email" { - valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + valueAsString, parseErr := withValueAsString(formField.Value, "email value needs to be a string") if parseErr != nil { - return epmodels.SignInPOSTResponse{}, parseErr + return epmodels.SignInPOSTResponse{ + WrongCredentialsError: &struct{}{}, + }, nil } email = valueAsString } else if formField.ID == "password" { - valueAsString, parseErr := withValueAsString(formField.Value, "Password value needs to be a string") + valueAsString, parseErr := withValueAsString(formField.Value, "password value needs to be a string") if parseErr != nil { - return epmodels.SignInPOSTResponse{}, parseErr + return epmodels.SignInPOSTResponse{ + WrongCredentialsError: &struct{}{}, + }, nil } password = valueAsString } @@ -181,15 +189,19 @@ func MakeAPIImplementation() epmodels.APIInterface { var password string for _, formField := range formFields { if formField.ID == "email" { - valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + valueAsString, parseErr := withValueAsString(formField.Value, "email value needs to be a string") if parseErr != nil { - return epmodels.SignUpPOSTResponse{}, parseErr + return epmodels.SignUpPOSTResponse{ + GeneralError: &supertokens.GeneralErrorResponse{Message: parseErr.Error()}, + }, nil } email = valueAsString } else if formField.ID == "password" { - valueAsString, parseErr := withValueAsString(formField.Value, "Password value needs to be a string") + valueAsString, parseErr := withValueAsString(formField.Value, "password value needs to be a string") if parseErr != nil { - return epmodels.SignUpPOSTResponse{}, parseErr + return epmodels.SignUpPOSTResponse{ + GeneralError: &supertokens.GeneralErrorResponse{Message: parseErr.Error()}, + }, nil } password = valueAsString } diff --git a/recipe/emailpassword/api/utils.go b/recipe/emailpassword/api/utils.go index 577ef715..363f713f 100644 --- a/recipe/emailpassword/api/utils.go +++ b/recipe/emailpassword/api/utils.go @@ -76,13 +76,14 @@ func validateFormFieldsOrThrowError(configFormFields []epmodels.NormalisedFormFi return nil, err } - if formField.ID == "email" { - valueAsString, parseErr := withValueAsString(formField.Value, "Email value needs to be a string") + if formField.ID == "email" || formField.ID == "password" { + valueAsString, parseErr := withValueAsString(formField.Value, fmt.Sprintf("%s value must be a string", formField.ID)) if parseErr != nil { return nil, supertokens.BadInputError{ - Msg: "Email value must be a string", + Msg: parseErr.Error(), } } + formFields = append(formFields, epmodels.TypeFormField{ ID: formField.ID, Value: strings.TrimSpace(valueAsString), diff --git a/recipe/emailpassword/authFlow_test.go b/recipe/emailpassword/authFlow_test.go index 375beae0..dee3699e 100644 --- a/recipe/emailpassword/authFlow_test.go +++ b/recipe/emailpassword/authFlow_test.go @@ -38,6 +38,208 @@ import ( "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestInvalidTypeForPassword(t *testing.T) { + 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{}, + }, + }), + 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": 123, + }, + { + "id": "email", + "value": "random@gmail.com", + }, + }, + } + + 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, 400, 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.NotNil(t, data["message"].(string)) + assert.Equal(t, "password value must be a string", data["message"].(string)) + + // Test the signin flow + respSignIn, err := http.Post(testServer.URL+"/auth/signin", "application/json", bytes.NewBuffer(postBody)) + + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, 400, respSignIn.StatusCode) + + dataInBytesSignIn, err := io.ReadAll(respSignIn.Body) + if err != nil { + t.Error(err.Error()) + } + respSignIn.Body.Close() + + var dataSignIn map[string]interface{} + err = json.Unmarshal(dataInBytesSignIn, &dataSignIn) + if err != nil { + t.Error(err.Error()) + } + + assert.NotNil(t, dataSignIn["message"].(string)) + assert.Equal(t, "password value must be a string", dataSignIn["message"].(string)) +} + +func TestInvalidTypeForEmail(t *testing.T) { + 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{}, + }, + }), + 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": "testpw1234", + }, + { + "id": "email", + "value": 1234, + }, + }, + } + + 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, 400, 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.NotNil(t, data["message"].(string)) + assert.Equal(t, "email value must be a string", data["message"].(string)) + + // Test the signin flow + respSignIn, err := http.Post(testServer.URL+"/auth/signin", "application/json", bytes.NewBuffer(postBody)) + + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, 400, respSignIn.StatusCode) + + dataInBytesSignIn, err := io.ReadAll(respSignIn.Body) + if err != nil { + t.Error(err.Error()) + } + respSignIn.Body.Close() + + var dataSignIn map[string]interface{} + err = json.Unmarshal(dataInBytesSignIn, &dataSignIn) + if err != nil { + t.Error(err.Error()) + } + + assert.NotNil(t, dataSignIn["message"].(string)) + assert.Equal(t, "email value must be a string", dataSignIn["message"].(string)) +} + func TestGoodCaseInputWithOptionalAndBoolean(t *testing.T) { optionalVal := true configValue := supertokens.TypeInput{ diff --git a/recipe/emailpassword/passwordReset_test.go b/recipe/emailpassword/passwordReset_test.go index abec9ce8..d9f5334d 100644 --- a/recipe/emailpassword/passwordReset_test.go +++ b/recipe/emailpassword/passwordReset_test.go @@ -232,6 +232,98 @@ func TestPasswordValidation(t *testing.T) { assert.Equal(t, "RESET_PASSWORD_INVALID_TOKEN_ERROR", data3["status"]) } +func TestInvalidTypeForPasswordAndEmail(t *testing.T) { + 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(nil), + }, + } + + 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": 1234, + }, + }, + } + token := map[string]interface{}{ + "token": "RandomToken", + } + var data map[string]interface{} + + a, _ := json.Marshal(formFields) + json.Unmarshal(a, &data) + b, _ := json.Marshal(token) + json.Unmarshal(b, &data) + + jData, _ := json.Marshal(data) + + resp, err := http.Post(testServer.URL+"/auth/user/password/reset", "application/json", bytes.NewBuffer(jData)) + + if err != nil { + t.Error(err.Error()) + } + + assert.NoError(t, err) + + dataInBytes, _ := io.ReadAll(resp.Body) + resp.Body.Close() + + var data1 map[string]interface{} + json.Unmarshal(dataInBytes, &data1) + + assert.Equal(t, 400, resp.StatusCode) + assert.Equal(t, "password value must be a string", data1["message"]) + + tokenResetFormFields := map[string][]map[string]interface{}{ + "formFields": { + { + "id": "email", + "value": 123456, + }, + }, + } + + postBody, err := json.Marshal(tokenResetFormFields) + if err != nil { + t.Error(err.Error()) + } + + resetResponse, resetErr := http.Post(testServer.URL+"/auth/user/password/reset/token", "application/json", bytes.NewBuffer(postBody)) + respInBytes, _ := io.ReadAll(resetResponse.Body) + resetResponse.Body.Close() + var resetResponseData map[string]interface{} + json.Unmarshal(respInBytes, &resetResponseData) + + if resetErr != nil { + t.Error(resetErr.Error()) + } + + assert.NoError(t, resetErr) + assert.Equal(t, 400, resetResponse.StatusCode) + assert.Equal(t, "email value must be a string", resetResponseData["message"]) +} + func TestTokenMissingFromInput(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ From 57a306dc94ad1fd3046730de2fc66916878daf68 Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 13:04:24 +0530 Subject: [PATCH 4/6] Add new tests for default email and password validators --- recipe/emailpassword/formFieldValidator_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recipe/emailpassword/formFieldValidator_test.go b/recipe/emailpassword/formFieldValidator_test.go index 36257941..ca915b33 100644 --- a/recipe/emailpassword/formFieldValidator_test.go +++ b/recipe/emailpassword/formFieldValidator_test.go @@ -40,6 +40,7 @@ func TestDefaultEmailValidator(t *testing.T) { assert.Equal(t, "Email is invalid", *defaultEmailValidator("fasd", "public")) assert.Equal(t, "Email is invalid", *defaultEmailValidator("dfa@@@abc.com", "public")) assert.Equal(t, "Email is invalid", *defaultEmailValidator("", "public")) + assert.Equal(t, "Field is not optional", *defaultEmailValidator(nil, "public")) } func TestDefaultPasswordValidator(t *testing.T) { @@ -54,6 +55,7 @@ func TestDefaultPasswordValidator(t *testing.T) { assert.Nil(t, defaultPasswordValidator("abc!@#$%^&*()gftr8", "public")) assert.Nil(t, defaultPasswordValidator(" dskj3", "public")) assert.Nil(t, defaultPasswordValidator(" dsk 3", "public")) + assert.Equal(t, "Field is not optional", *defaultPasswordValidator(nil, "public")) assert.Equal(t, "Password must contain at least 8 characters, including a number", *defaultPasswordValidator("asd", "public")) From b14a6ff0435bd705358b5dcb0c5c50055fed9e99 Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 13:06:21 +0530 Subject: [PATCH 5/6] Bump version in constants --- supertokens/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supertokens/constants.go b/supertokens/constants.go index 3e95d5f9..1ac2c115 100644 --- a/supertokens/constants.go +++ b/supertokens/constants.go @@ -21,7 +21,7 @@ const ( ) // VERSION current version of the lib -const VERSION = "0.24.2" +const VERSION = "0.24.3" var ( cdiSupported = []string{"3.1"} From f863cbe238d54f7b44eb2eed9e9b1f6ceda56e31 Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 15:00:06 +0530 Subject: [PATCH 6/6] Cleanup and optimize the code a bit --- recipe/emailpassword/api/implementation.go | 48 +++++++++++++--------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/recipe/emailpassword/api/implementation.go b/recipe/emailpassword/api/implementation.go index 470d3ad7..5d0d514b 100644 --- a/recipe/emailpassword/api/implementation.go +++ b/recipe/emailpassword/api/implementation.go @@ -40,6 +40,10 @@ func MakeAPIImplementation() epmodels.APIInterface { var email string for _, formField := range formFields { if formField.ID == "email" { + // NOTE: This check will never actually fail because the parent + // function already checks for the type however in order to read the + // value as a string, it's safer to keep it here in case something changes + // in the future. valueAsString, parseErr := withValueAsString(formField.Value, "email value needs to be a string") if parseErr != nil { return epmodels.GeneratePasswordResetTokenPOSTResponse{ @@ -108,6 +112,10 @@ func MakeAPIImplementation() epmodels.APIInterface { var newPassword string for _, formField := range formFields { if formField.ID == "password" { + // NOTE: This check will never actually fail because the parent + // function already checks for the type however in order to read the + // value as a string, it's safer to keep it here in case something changes + // in the future. valueAsString, parseErr := withValueAsString(formField.Value, "password value needs to be a string") if parseErr != nil { return epmodels.ResetPasswordPOSTResponse{ @@ -138,22 +146,22 @@ func MakeAPIImplementation() epmodels.APIInterface { var email string var password string for _, formField := range formFields { - if formField.ID == "email" { - valueAsString, parseErr := withValueAsString(formField.Value, "email value needs to be a string") + // NOTE: The check for type of password/email will never actually + // fail because the parent function already checks for the type + // however in order to read the value as a string, it's safer to + // keep it here in case something changes in the future. + if formField.ID == "email" || formField.ID == "password" { + valueAsString, parseErr := withValueAsString(formField.Value, fmt.Sprintf("%s value needs to be a string", formField.ID)) if parseErr != nil { return epmodels.SignInPOSTResponse{ WrongCredentialsError: &struct{}{}, }, nil } - email = valueAsString - } else if formField.ID == "password" { - valueAsString, parseErr := withValueAsString(formField.Value, "password value needs to be a string") - if parseErr != nil { - return epmodels.SignInPOSTResponse{ - WrongCredentialsError: &struct{}{}, - }, nil + if formField.ID == "email" { + email = valueAsString + } else { + password = valueAsString } - password = valueAsString } } @@ -188,22 +196,22 @@ func MakeAPIImplementation() epmodels.APIInterface { var email string var password string for _, formField := range formFields { - if formField.ID == "email" { - valueAsString, parseErr := withValueAsString(formField.Value, "email value needs to be a string") + // NOTE: The check for type of password/email will never actually + // fail because the parent function already checks for the type + // however in order to read the value as a string, it's safer to + // keep it here in case something changes in the future. + if formField.ID == "email" || formField.ID == "password" { + valueAsString, parseErr := withValueAsString(formField.Value, fmt.Sprintf("%s value needs to be a string", formField.ID)) if parseErr != nil { return epmodels.SignUpPOSTResponse{ GeneralError: &supertokens.GeneralErrorResponse{Message: parseErr.Error()}, }, nil } - email = valueAsString - } else if formField.ID == "password" { - valueAsString, parseErr := withValueAsString(formField.Value, "password value needs to be a string") - if parseErr != nil { - return epmodels.SignUpPOSTResponse{ - GeneralError: &supertokens.GeneralErrorResponse{Message: parseErr.Error()}, - }, nil + if formField.ID == "email" { + email = valueAsString + } else { + password = valueAsString } - password = valueAsString } }