Skip to content

Commit ef20adf

Browse files
committed
Merge branch '0.12' into feat/jwt-rework
2 parents 3450446 + 691aeed commit ef20adf

File tree

14 files changed

+150
-35
lines changed

14 files changed

+150
-35
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
## [0.12.0] - 2023-05-05
1111

12+
### Added
13+
14+
- added optional password policy check in `updateEmailOrPassword`
15+
1216
### Breaking Changes
1317

1418
- Changed the interface and configuration of the Session recipe, see below for details. If you do not use the Session recipe directly and do not provide custom configuration, then no migration is necessary.

recipe/dashboard/api/userdetails/userPut.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func updateEmailForRecipeId(recipeId string, userId string, email string) (updat
7474
}, nil
7575
}
7676

77-
updateResponse, err := emailpassword.UpdateEmailOrPassword(userId, &email, nil)
77+
updateResponse, err := emailpassword.UpdateEmailOrPassword(userId, &email, nil, nil)
7878

7979
if err != nil {
8080
return updateEmailResponse{}, err
@@ -113,7 +113,7 @@ func updateEmailForRecipeId(recipeId string, userId string, email string) (updat
113113
}, nil
114114
}
115115

116-
updateResponse, err := thirdpartyemailpassword.UpdateEmailOrPassword(userId, &email, nil)
116+
updateResponse, err := thirdpartyemailpassword.UpdateEmailOrPassword(userId, &email, nil, nil)
117117

118118
if err != nil {
119119
return updateEmailResponse{}, err

recipe/emailpassword/authFlow_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
"github.com/supertokens/supertokens-golang/test/unittesting"
3636
)
3737

38-
//SigninFeature Tests
38+
// SigninFeature Tests
3939
func TestDisablingAPIDefaultSigninDoesNotWork(t *testing.T) {
4040
configValue := supertokens.TypeInput{
4141
Supertokens: &supertokens.ConnectionInfo{
@@ -1234,7 +1234,7 @@ func TestHandlePostSignInFunction(t *testing.T) {
12341234

12351235
}
12361236

1237-
//Signout Feature tests
1237+
// Signout Feature tests
12381238
func TestDefaultSignoutRouteRevokesSession(t *testing.T) {
12391239
customAntiCsrfVal := "VIA_TOKEN"
12401240
configValue := supertokens.TypeInput{
@@ -1474,7 +1474,7 @@ func TestSignoutAPIreturnsTryRefreshTokenAndSignoutShouldReturnOK(t *testing.T)
14741474
assert.Equal(t, "", cookieData2["refreshTokenDomain"])
14751475
}
14761476

1477-
//Signup Feature tests
1477+
// Signup Feature tests
14781478
func TestDisablingAPIDefaultSignUpDoesNotWork(t *testing.T) {
14791479
configValue := supertokens.TypeInput{
14801480
Supertokens: &supertokens.ConnectionInfo{

recipe/emailpassword/ep_userIdMapping_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func TestCreateUserIdMappingUpdateEmailPassword(t *testing.T) {
188188

189189
newEmail := "email@example.com"
190190
newPass := "newpass123"
191-
updateResp, err := UpdateEmailOrPassword(externalUserId, &newEmail, &newPass)
191+
updateResp, err := UpdateEmailOrPassword(externalUserId, &newEmail, &newPass, nil)
192192
assert.NoError(t, err)
193193
assert.NotNil(t, updateResp.OK)
194194

recipe/emailpassword/epmodels/recipeInterface.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type RecipeInterface struct {
2424
GetUserByEmail *func(email string, userContext supertokens.UserContext) (*User, error)
2525
CreateResetPasswordToken *func(userID string, userContext supertokens.UserContext) (CreateResetPasswordTokenResponse, error)
2626
ResetPasswordUsingToken *func(token string, newPassword string, userContext supertokens.UserContext) (ResetPasswordUsingTokenResponse, error)
27-
UpdateEmailOrPassword *func(userId string, email *string, password *string, userContext supertokens.UserContext) (UpdateEmailOrPasswordResponse, error)
27+
UpdateEmailOrPassword *func(userId string, email *string, password *string, applyPasswordPolicy *bool, userContext supertokens.UserContext) (UpdateEmailOrPasswordResponse, error)
2828
}
2929

3030
type SignUpResponse struct {
@@ -56,7 +56,12 @@ type ResetPasswordUsingTokenResponse struct {
5656
}
5757

5858
type UpdateEmailOrPasswordResponse struct {
59-
OK *struct{}
60-
UnknownUserIdError *struct{}
61-
EmailAlreadyExistsError *struct{}
59+
OK *struct{}
60+
UnknownUserIdError *struct{}
61+
EmailAlreadyExistsError *struct{}
62+
PasswordPolicyViolatedError *PasswordPolicyViolatedError
63+
}
64+
65+
type PasswordPolicyViolatedError struct {
66+
FailureReason string
6267
}

recipe/emailpassword/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ func ResetPasswordUsingTokenWithContext(token string, newPassword string, userCo
7474
return (*instance.RecipeImpl.ResetPasswordUsingToken)(token, newPassword, userContext)
7575
}
7676

77-
func UpdateEmailOrPasswordWithContext(userId string, email *string, password *string, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) {
77+
func UpdateEmailOrPasswordWithContext(userId string, email *string, password *string, applyPasswordPolicy *bool, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) {
7878
instance, err := GetRecipeInstanceOrThrowError()
7979
if err != nil {
8080
return epmodels.UpdateEmailOrPasswordResponse{}, nil
8181
}
82-
return (*instance.RecipeImpl.UpdateEmailOrPassword)(userId, email, password, userContext)
82+
return (*instance.RecipeImpl.UpdateEmailOrPassword)(userId, email, password, applyPasswordPolicy, userContext)
8383
}
8484

8585
func SendEmailWithContext(input emaildelivery.EmailType, userContext supertokens.UserContext) error {
@@ -114,8 +114,8 @@ func ResetPasswordUsingToken(token string, newPassword string) (epmodels.ResetPa
114114
return ResetPasswordUsingTokenWithContext(token, newPassword, &map[string]interface{}{})
115115
}
116116

117-
func UpdateEmailOrPassword(userId string, email *string, password *string) (epmodels.UpdateEmailOrPasswordResponse, error) {
118-
return UpdateEmailOrPasswordWithContext(userId, email, password, &map[string]interface{}{})
117+
func UpdateEmailOrPassword(userId string, email *string, password *string, applyPasswordPolicy *bool) (epmodels.UpdateEmailOrPasswordResponse, error) {
118+
return UpdateEmailOrPasswordWithContext(userId, email, password, applyPasswordPolicy, &map[string]interface{}{})
119119
}
120120

121121
func SendEmail(input emaildelivery.EmailType) error {

recipe/emailpassword/recipe.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ func MakeRecipe(recipeId string, appInfo supertokens.NormalisedAppinfo, config *
5353
verifiedConfig := validateAndNormaliseUserInput(r, appInfo, config)
5454
r.Config = verifiedConfig
5555
r.APIImpl = verifiedConfig.Override.APIs(api.MakeAPIImplementation())
56-
r.RecipeImpl = verifiedConfig.Override.Functions(MakeRecipeImplementation(*querierInstance))
56+
var getEmailPasswordConfig = func() epmodels.TypeNormalisedInput {
57+
return verifiedConfig
58+
}
59+
r.RecipeImpl = verifiedConfig.Override.Functions(MakeRecipeImplementation(*querierInstance, getEmailPasswordConfig))
5760

5861
if emailDeliveryIngredient != nil {
5962
r.EmailDelivery = *emailDeliveryIngredient

recipe/emailpassword/recipeImplementation.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"github.com/supertokens/supertokens-golang/supertokens"
2121
)
2222

23-
func MakeRecipeImplementation(querier supertokens.Querier) epmodels.RecipeInterface {
23+
func MakeRecipeImplementation(querier supertokens.Querier, getEmailPasswordConfig func() epmodels.TypeNormalisedInput) epmodels.RecipeInterface {
2424
signUp := func(email, password string, userContext supertokens.UserContext) (epmodels.SignUpResponse, error) {
2525
response, err := querier.SendPostRequest("/recipe/signup", map[string]interface{}{
2626
"email": email,
@@ -158,14 +158,28 @@ func MakeRecipeImplementation(querier supertokens.Querier) epmodels.RecipeInterf
158158
}
159159
}
160160

161-
updateEmailOrPassword := func(userId string, email, password *string, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) {
161+
updateEmailOrPassword := func(userId string, email, password *string, applyPasswordPolicy *bool, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) {
162162
requestBody := map[string]interface{}{
163163
"userId": userId,
164164
}
165165
if email != nil {
166166
requestBody["email"] = email
167167
}
168168
if password != nil {
169+
if applyPasswordPolicy == nil || *applyPasswordPolicy {
170+
formFields := getEmailPasswordConfig().SignUpFeature.FormFields
171+
for i := range formFields {
172+
if formFields[i].ID == "password" {
173+
err := formFields[i].Validate(*password)
174+
if err != nil {
175+
errResponse := epmodels.PasswordPolicyViolatedError{
176+
FailureReason: *err,
177+
}
178+
return epmodels.UpdateEmailOrPasswordResponse{PasswordPolicyViolatedError: &errResponse}, nil
179+
}
180+
}
181+
}
182+
}
169183
requestBody["password"] = password
170184
}
171185
response, err := querier.SendPutRequest("/recipe/user", requestBody)

recipe/emailpassword/updateEmailPass_test.go

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,9 @@ func TestUpdateEmailPass(t *testing.T) {
9898
}
9999

100100
email := "test2@gmail.com"
101-
password := "testPass"
102-
103-
UpdateEmailOrPassword(data["user"].(map[string]interface{})["id"].(string), &email, &password)
101+
password := "testPass1"
104102

103+
UpdateEmailOrPassword(data["user"].(map[string]interface{})["id"].(string), &email, &password, nil)
105104
res1, err := unittesting.SignInRequest("testrandom@gmail.com", "validpass123", testServer.URL)
106105

107106
if err != nil {
@@ -140,6 +139,96 @@ func TestUpdateEmailPass(t *testing.T) {
140139

141140
assert.Equal(t, "OK", data2["status"])
142141
assert.Equal(t, email, data2["user"].(map[string]interface{})["email"])
142+
143+
password = "test"
144+
applyPasswordPolicy := true
145+
res3, err := UpdateEmailOrPassword(data["user"].(map[string]interface{})["id"].(string), &email, &password, &applyPasswordPolicy)
146+
assert.NotNil(t, res3.PasswordPolicyViolatedError)
147+
assert.Equal(t, "Password must contain at least 8 characters, including a number", res3.PasswordPolicyViolatedError.FailureReason)
148+
}
149+
150+
func TestUpdateEmailPassWithCustomValidator(t *testing.T) {
151+
configValue := supertokens.TypeInput{
152+
Supertokens: &supertokens.ConnectionInfo{
153+
ConnectionURI: "http://localhost:8080",
154+
},
155+
AppInfo: supertokens.AppInfo{
156+
APIDomain: "api.supertokens.io",
157+
AppName: "SuperTokens",
158+
WebsiteDomain: "supertokens.io",
159+
},
160+
RecipeList: []supertokens.Recipe{
161+
Init(&epmodels.TypeInput{SignUpFeature: &epmodels.TypeInputSignUp{FormFields: []epmodels.TypeInputFormField{
162+
{
163+
ID: "password",
164+
Validate: func(value interface{}) *string {
165+
if len(value.(string)) > 5 {
166+
return nil
167+
}
168+
err := "Password length must be more than 5 characters"
169+
return &err
170+
},
171+
},
172+
}}}),
173+
session.Init(&sessmodels.TypeInput{
174+
GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod {
175+
return sessmodels.CookieTransferMethod
176+
},
177+
}),
178+
},
179+
}
180+
181+
BeforeEach()
182+
unittesting.StartUpST("localhost", "8080")
183+
defer AfterEach()
184+
err := supertokens.Init(configValue)
185+
if err != nil {
186+
t.Error(err.Error())
187+
}
188+
querier, err := supertokens.GetNewQuerierInstanceOrThrowError("")
189+
if err != nil {
190+
t.Error(err.Error())
191+
}
192+
cdiVersion, err := querier.GetQuerierAPIVersion()
193+
if err != nil {
194+
t.Error(err.Error())
195+
}
196+
if unittesting.MaxVersion("2.7", cdiVersion) == "2.7" {
197+
return
198+
}
199+
mux := http.NewServeMux()
200+
testServer := httptest.NewServer(supertokens.Middleware(mux))
201+
defer testServer.Close()
202+
203+
_, err = unittesting.SignupRequest("testrandom@gmail.com", "validpass123", testServer.URL)
204+
if err != nil {
205+
t.Error(err.Error())
206+
}
207+
208+
res, err := unittesting.SignInRequest("testrandom@gmail.com", "validpass123", testServer.URL)
209+
210+
if err != nil {
211+
t.Error(err.Error())
212+
}
213+
dataInBytes, err := io.ReadAll(res.Body)
214+
if err != nil {
215+
t.Error(err.Error())
216+
}
217+
res.Body.Close()
218+
219+
var data map[string]interface{}
220+
err = json.Unmarshal(dataInBytes, &data)
221+
if err != nil {
222+
t.Error(err.Error())
223+
}
224+
225+
email := "testrandom@gmail.com"
226+
password := "test"
227+
228+
res1, err := UpdateEmailOrPassword(data["user"].(map[string]interface{})["id"].(string), &email, &password, nil)
229+
assert.NotNil(t, res1.PasswordPolicyViolatedError)
230+
assert.Equal(t, "Password length must be more than 5 characters", res1.PasswordPolicyViolatedError.FailureReason)
231+
143232
}
144233

145234
func TestAPICustomResponse(t *testing.T) {

recipe/thirdpartyemailpassword/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ func ResetPasswordUsingTokenWithContext(token, newPassword string, userContext s
9191
return (*instance.RecipeImpl.ResetPasswordUsingToken)(token, newPassword, userContext)
9292
}
9393

94-
func UpdateEmailOrPasswordWithContext(userId string, email *string, password *string, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) {
94+
func UpdateEmailOrPasswordWithContext(userId string, email *string, password *string, applyPasswordPolicy *bool, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) {
9595
instance, err := GetRecipeInstanceOrThrowError()
9696
if err != nil {
9797
return epmodels.UpdateEmailOrPasswordResponse{}, err
9898
}
99-
return (*instance.RecipeImpl.UpdateEmailOrPassword)(userId, email, password, userContext)
99+
return (*instance.RecipeImpl.UpdateEmailOrPassword)(userId, email, password, applyPasswordPolicy, userContext)
100100
}
101101

102102
func SendEmailWithContext(input emaildelivery.EmailType, userContext supertokens.UserContext) error {
@@ -139,8 +139,8 @@ func ResetPasswordUsingToken(token, newPassword string) (epmodels.ResetPasswordU
139139
return ResetPasswordUsingTokenWithContext(token, newPassword, &map[string]interface{}{})
140140
}
141141

142-
func UpdateEmailOrPassword(userId string, email *string, password *string) (epmodels.UpdateEmailOrPasswordResponse, error) {
143-
return UpdateEmailOrPasswordWithContext(userId, email, password, &map[string]interface{}{})
142+
func UpdateEmailOrPassword(userId string, email *string, password *string, applyPasswordPolicy *bool) (epmodels.UpdateEmailOrPasswordResponse, error) {
143+
return UpdateEmailOrPasswordWithContext(userId, email, password, applyPasswordPolicy, &map[string]interface{}{})
144144
}
145145

146146
func SendEmail(input emaildelivery.EmailType) error {

0 commit comments

Comments
 (0)