Skip to content

Commit d7b015a

Browse files
authored
notp update user (#561)
1 parent f56c592 commit d7b015a

File tree

8 files changed

+176
-0
lines changed

8 files changed

+176
-0
lines changed

descope/api/client.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ var (
4545
signUpNOTP: "auth/notp/whatsapp/signup",
4646
signInNOTP: "auth/notp/whatsapp/signin",
4747
signUpOrInNOTP: "auth/notp/whatsapp/signup-in",
48+
updateUserNOTP: "auth/notp/whatsapp/update",
4849
getNOTPSession: "auth/notp/pending-session",
4950
verifyCode: "auth/otp/verify",
5051
signUpPassword: "auth/password/signup",
@@ -276,6 +277,7 @@ type authEndpoints struct {
276277
signUpNOTP string
277278
signInNOTP string
278279
signUpOrInNOTP string
280+
updateUserNOTP string
279281
getNOTPSession string
280282
verifyCode string
281283
signUpPassword string
@@ -524,6 +526,10 @@ func (e *endpoints) SignUpOrInNOTP() string {
524526
return path.Join(e.version, e.auth.signUpOrInNOTP)
525527
}
526528

529+
func (e *endpoints) UpdateUserNOTP() string {
530+
return path.Join(e.version, e.auth.updateUserNOTP)
531+
}
532+
527533
func (e *endpoints) GetNOTPSession() string {
528534
return path.Join(e.version, e.auth.getNOTPSession)
529535
}

descope/internal/auth/auth.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,10 @@ func composeNOTPSignUpOrInURL() string {
962962
return api.Routes.SignUpOrInNOTP()
963963
}
964964

965+
func composeNOTPUpdateUserURL() string {
966+
return api.Routes.UpdateUserNOTP()
967+
}
968+
965969
func composeNOTPGetSession() string {
966970
return api.Routes.GetNOTPSession()
967971
}

descope/internal/auth/notp.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66

77
"github.com/descope/go-sdk/descope"
8+
"github.com/descope/go-sdk/descope/internal/utils"
89
)
910

1011
type notp struct {
@@ -64,3 +65,23 @@ func (auth *notp) GetSession(ctx context.Context, pendingRef string, w http.Resp
6465
}
6566
return auth.generateAuthenticationInfo(httpResponse, w)
6667
}
68+
69+
func (auth *notp) UpdateUser(ctx context.Context, loginID, phone string, updateOptions *descope.NOTPUpdateOptions, r *http.Request) (*descope.NOTPResponse, error) {
70+
if loginID == "" {
71+
return nil, utils.NewInvalidArgumentError("loginID")
72+
}
73+
74+
pswd, err := getValidRefreshToken(r)
75+
if err != nil {
76+
return nil, err
77+
}
78+
if updateOptions == nil {
79+
updateOptions = &descope.NOTPUpdateOptions{}
80+
}
81+
82+
httpResponse, err := auth.client.DoPostRequest(ctx, composeNOTPUpdateUserURL(), newNOTPUpdateUserRequestBody(loginID, phone, updateOptions), nil, pswd)
83+
if err != nil {
84+
return nil, err
85+
}
86+
return getNOTPResponse(httpResponse)
87+
}

descope/internal/auth/notp_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/descope/go-sdk/descope"
1414
"github.com/descope/go-sdk/descope/api"
15+
"github.com/descope/go-sdk/descope/internal/utils"
1516
"github.com/stretchr/testify/assert"
1617
"github.com/stretchr/testify/require"
1718
)
@@ -313,6 +314,7 @@ func TestSignUpNOTPNoUser(t *testing.T) {
313314
_, err = a.NOTP().SignUp(context.Background(), phone, nil, nil)
314315
require.NoError(t, err)
315316
}
317+
316318
func TestSignUpOrInNOTPNoLoginID(t *testing.T) {
317319
a, err := newTestAuth(nil, func(_ *http.Request) (*http.Response, error) {
318320
return &http.Response{
@@ -324,3 +326,93 @@ func TestSignUpOrInNOTPNoLoginID(t *testing.T) {
324326
_, err = a.NOTP().SignUpOrIn(context.Background(), "", nil)
325327
require.NoError(t, err)
326328
}
329+
330+
func TestUpdateUserNOTPEmptyLoginID(t *testing.T) {
331+
a, err := newTestAuth(nil, func(_ *http.Request) (*http.Response, error) {
332+
return nil, nil
333+
})
334+
require.NoError(t, err)
335+
_, err = a.NOTP().UpdateUser(context.Background(), "", "", nil, nil)
336+
require.Error(t, err)
337+
require.ErrorContains(t, err, utils.NewInvalidArgumentError("loginID").Message)
338+
}
339+
340+
func TestUpdateUserNOTPMissingRefreshToken(t *testing.T) {
341+
a, err := newTestAuth(nil, func(_ *http.Request) (*http.Response, error) {
342+
return nil, nil
343+
})
344+
require.NoError(t, err)
345+
_, err = a.NOTP().UpdateUser(context.Background(), "login-id", "", nil, nil)
346+
require.Error(t, err)
347+
assert.ErrorIs(t, err, descope.ErrRefreshToken)
348+
}
349+
350+
func TestUpdateUserNOTPWithoutOptions(t *testing.T) {
351+
loginID := "943248329844"
352+
phone := "+111111111111"
353+
a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) {
354+
assert.EqualValues(t, composeNOTPUpdateUserURL(), r.URL.RequestURI())
355+
356+
body, err := readBodyMap(r)
357+
require.NoError(t, err)
358+
assert.EqualValues(t, loginID, body["loginId"])
359+
assert.EqualValues(t, phone, body["phone"])
360+
assert.Nil(t, body["addToLoginIDs"])
361+
assert.Nil(t, body["onMergeUseExisting"])
362+
assert.Nil(t, body["templateOptions"])
363+
u, p := getProjectAndJwt(r)
364+
assert.NotEmpty(t, u)
365+
assert.NotEmpty(t, p)
366+
resp := descope.NOTPResponse{}
367+
respBytes, err := utils.Marshal(resp)
368+
require.NoError(t, err)
369+
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBuffer(respBytes))}, nil
370+
})
371+
require.NoError(t, err)
372+
r := &http.Request{Header: http.Header{}}
373+
r.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtTokenValid})
374+
_, err = a.NOTP().UpdateUser(context.Background(), loginID, phone, nil, r)
375+
require.NoError(t, err)
376+
}
377+
378+
func TestUpdateUserNOTPWithOptions(t *testing.T) {
379+
loginID := "943248329844"
380+
phone := "+111111111111"
381+
options := &descope.NOTPUpdateOptions{
382+
AddToLoginIDs: true,
383+
OnMergeUseExisting: true,
384+
ProviderID: "provider-id",
385+
TemplateOptions: map[string]string{
386+
"key1": "value1",
387+
},
388+
Templates: &descope.NOTPTemplates{
389+
VerifyTemplateID: "verify-template-id",
390+
SuccessTemplateID: "success-template-id",
391+
ErrorTemplateID: "error-template-id",
392+
},
393+
}
394+
a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) {
395+
assert.EqualValues(t, composeNOTPUpdateUserURL(), r.URL.RequestURI())
396+
397+
body, err := readBodyMap(r)
398+
require.NoError(t, err)
399+
assert.EqualValues(t, loginID, body["loginId"])
400+
assert.EqualValues(t, phone, body["phone"])
401+
assert.EqualValues(t, true, body["addToLoginIDs"])
402+
assert.EqualValues(t, true, body["onMergeUseExisting"])
403+
assert.NotEmpty(t, body["templates"])
404+
assert.NotEmpty(t, body["templateOptions"])
405+
u, p := getProjectAndJwt(r)
406+
assert.NotEmpty(t, u)
407+
assert.NotEmpty(t, p)
408+
resp := descope.NOTPResponse{}
409+
respBytes, err := utils.Marshal(resp)
410+
require.NoError(t, err)
411+
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBuffer(respBytes))}, nil
412+
})
413+
require.NoError(t, err)
414+
r := &http.Request{Header: http.Header{}}
415+
r.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtTokenValid})
416+
_, err = a.NOTP().UpdateUser(context.Background(), loginID, phone, options, r)
417+
require.NoError(t, err)
418+
}

descope/internal/auth/utils.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ type notpAuthenticationRequestBody struct {
8585
LoginOptions *descope.LoginOptions `json:"loginOptions,omitempty"`
8686
}
8787

88+
type notpUpdateUserRequestBody struct {
89+
LoginID string `json:"loginId,omitempty"`
90+
Phone string `json:"phone,omitempty"`
91+
AddToLoginIDs bool `json:"addToLoginIDs,omitempty"`
92+
OnMergeUseExisting bool `json:"onMergeUseExisting,omitempty"`
93+
ProviderID string `json:"providerId,omitempty"`
94+
Templates *descope.NOTPTemplates `json:"templates,omitempty"`
95+
TemplateOptions map[string]string `json:"templateOptions,omitempty"`
96+
Locale string `json:"locale,omitempty"`
97+
}
98+
8899
type otpUpdateEmailRequestBody struct {
89100
*descope.UpdateOptions `json:",inline"`
90101
LoginID string `json:"loginId,omitempty"`
@@ -181,6 +192,19 @@ func newNOTPAuthenticationSignUpRequestBody(loginID string, user *descope.User,
181192
return res
182193
}
183194

195+
func newNOTPUpdateUserRequestBody(loginID string, phone string, options *descope.NOTPUpdateOptions) *notpUpdateUserRequestBody {
196+
return &notpUpdateUserRequestBody{
197+
LoginID: loginID,
198+
Phone: phone,
199+
AddToLoginIDs: options.AddToLoginIDs,
200+
OnMergeUseExisting: options.OnMergeUseExisting,
201+
ProviderID: options.ProviderID,
202+
Templates: options.Templates,
203+
TemplateOptions: options.TemplateOptions,
204+
Locale: options.Locale,
205+
}
206+
}
207+
184208
func newOTPUpdatePhoneRequestBody(loginID, phone string, updateOptions *descope.UpdateOptions) *otpUpdatePhoneRequestBody {
185209
return &otpUpdatePhoneRequestBody{LoginID: loginID, Phone: phone, UpdateOptions: updateOptions}
186210
}

descope/sdk/auth.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ type NOTP interface {
159159
// GetSession - Use to get a session that was generated by SignIn/SignUp/SignUpOrIn request.
160160
// This function will return a proper JWT only after Verify succeed for this sign-in/sign-up/sign-up-or-in.
161161
GetSession(ctx context.Context, pendingRef string, w http.ResponseWriter) (*descope.AuthenticationInfo, error)
162+
163+
// UpdateUser - Use to update user details with NOTP (WhatsApp) authentication.
164+
UpdateUser(ctx context.Context, loginID, phone string, updateOptions *descope.NOTPUpdateOptions, request *http.Request) (*descope.NOTPResponse, error)
162165
}
163166

164167
type Password interface {

descope/tests/mocks/auth/authenticationmock.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ type MockNOTP struct {
316316
GetSessionAssert func(pendingRef string, w http.ResponseWriter)
317317
GetSessionResponse *descope.AuthenticationInfo
318318
GetSessionError error
319+
320+
UpdateUserAssert func(loginID, phone string, updateOptions *descope.NOTPUpdateOptions, r *http.Request)
321+
UpdateUserError error
322+
UpdateUserResponse *descope.NOTPResponse
319323
}
320324

321325
func (m *MockNOTP) SignIn(_ context.Context, loginID string, r *http.Request, loginOptions *descope.LoginOptions) (*descope.NOTPResponse, error) {
@@ -346,6 +350,13 @@ func (m *MockNOTP) GetSession(_ context.Context, pendingRef string, w http.Respo
346350
return m.GetSessionResponse, m.GetSessionError
347351
}
348352

353+
func (m *MockNOTP) UpdateUser(_ context.Context, loginID, phone string, updateOptions *descope.NOTPUpdateOptions, r *http.Request) (*descope.NOTPResponse, error) {
354+
if m.UpdateUserAssert != nil {
355+
m.UpdateUserAssert(loginID, phone, updateOptions, r)
356+
}
357+
return m.UpdateUserResponse, m.UpdateUserError
358+
}
359+
349360
// Mock Password
350361

351362
type MockPassword struct {

descope/types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,21 @@ type UpdateOptions struct {
608608
ProviderID string `json:"providerId,omitempty"`
609609
}
610610

611+
type NOTPTemplates struct {
612+
VerifyTemplateID string `json:"verifyTemplateId,omitempty"`
613+
SuccessTemplateID string `json:"successTemplateId,omitempty"`
614+
ErrorTemplateID string `json:"errorTemplateId,omitempty"`
615+
}
616+
617+
type NOTPUpdateOptions struct {
618+
AddToLoginIDs bool `json:"addToLoginIDs,omitempty"`
619+
OnMergeUseExisting bool `json:"onMergeUseExisting,omitempty"`
620+
TemplateOptions map[string]string `json:"templateOptions,omitempty"` // for providing messaging template options (templates that are being sent via email / text message)
621+
ProviderID string `json:"providerId,omitempty"`
622+
Templates *NOTPTemplates `json:"templates,omitempty"`
623+
Locale string `json:"locale,omitempty"` // locale for the message
624+
}
625+
611626
type AccessKeyResponse struct {
612627
ID string `json:"id,omitempty"`
613628
Name string `json:"name,omitempty"`

0 commit comments

Comments
 (0)