From 14a9e7b5d69beb1e62528298799c652428b6be47 Mon Sep 17 00:00:00 2001 From: dorsha Date: Wed, 3 Apr 2024 00:40:58 +0300 Subject: [PATCH 1/3] OTP voice support --- README.md | 4 +- descope/internal/auth/auth.go | 11 +++ descope/internal/auth/auth_test.go | 7 ++ descope/internal/auth/otp.go | 2 +- descope/internal/auth/otp_test.go | 112 +++++++++++++++++++++++++++++ descope/internal/auth/utils.go | 2 + descope/internal/mgmt/user_test.go | 33 +++++++++ descope/sdk/auth.go | 4 +- descope/types.go | 1 + examples/ginwebapp/go.mod | 12 ++-- examples/ginwebapp/go.sum | 22 +++--- examples/ginwebapp/main.go | 4 ++ examples/webapp/go.mod | 8 +-- examples/webapp/go.sum | 18 ++--- examples/webapp/main.go | 7 ++ 15 files changed, 212 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 5a15e376..524bef10 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ For rate limiting information, please confer to the [API Rate Limits](#api-rate- ### OTP Authentication -Send a user a one-time password (OTP) using your preferred delivery method (_email / SMS_). An email address or phone number must be provided accordingly. +Send a user a one-time password (OTP) using your preferred delivery method (_email / SMS / Voice call_). An email address or phone number must be provided accordingly. The user can either `sign up`, `sign in` or `sign up or in` @@ -1594,7 +1594,7 @@ assert.EqualValues(t, updateJWTWithCustomClaimsResponse, res) ### Utils for your end to end (e2e) tests and integration tests To ease your e2e tests, we exposed dedicated management methods, -that way, you don't need to use 3rd party messaging services in order to receive sign-in/up Emails or SMS, and avoid the need of parsing the code and token from them. +that way, you don't need to use 3rd party messaging services in order to receive sign-in/up Emails, SMS or Voice call, and avoid the need of parsing the code and token from them. ```go // User for test can be created, this user will be able to generate code/link without diff --git a/descope/internal/auth/auth.go b/descope/internal/auth/auth.go index 801c2617..2fa1114f 100644 --- a/descope/internal/auth/auth.go +++ b/descope/internal/auth/auth.go @@ -607,6 +607,15 @@ func (*authenticationsBase) verifyDeliveryMethod(method descope.DeliveryMethod, if !phoneRegex.MatchString(user.Phone) { return utils.NewInvalidArgumentError(varName) } + case descope.MethodVoice: + if len(user.Phone) == 0 { + user.Phone = loginID + } else { + varName = "user.Phone" + } + if !phoneRegex.MatchString(user.Phone) { + return utils.NewInvalidArgumentError(varName) + } case descope.MethodWhatsApp: if len(user.Phone) == 0 { user.Phone = loginID @@ -986,6 +995,8 @@ func getMaskedValue(method descope.DeliveryMethod) Masked { switch method { case descope.MethodSMS: fallthrough + case descope.MethodVoice: + fallthrough case descope.MethodWhatsApp: m = &MaskedPhoneRes{} case descope.MethodEmail: diff --git a/descope/internal/auth/auth_test.go b/descope/internal/auth/auth_test.go index ef959911..97af9aef 100644 --- a/descope/internal/auth/auth_test.go +++ b/descope/internal/auth/auth_test.go @@ -155,6 +155,8 @@ func TestVerifyDeliveryMethod(t *testing.T) { assert.ErrorIs(t, err, descope.ErrInvalidArguments) err = a.verifyDeliveryMethod(descope.MethodSMS, "abc@notaphone.com", &descope.User{}) assert.ErrorIs(t, err, descope.ErrInvalidArguments) + err = a.verifyDeliveryMethod(descope.MethodVoice, "abc@notaphone.com", &descope.User{}) + assert.ErrorIs(t, err, descope.ErrInvalidArguments) err = a.verifyDeliveryMethod(descope.MethodWhatsApp, "abc@notaphone.com", &descope.User{}) assert.ErrorIs(t, err, descope.ErrInvalidArguments) @@ -171,6 +173,9 @@ func TestVerifyDeliveryMethod(t *testing.T) { err = a.verifyDeliveryMethod(descope.MethodSMS, "+19999999999", u) assert.Nil(t, err) assert.NotEmpty(t, u.Phone) + err = a.verifyDeliveryMethod(descope.MethodVoice, "+19999999999", u) + assert.Nil(t, err) + assert.NotEmpty(t, u.Phone) err = a.verifyDeliveryMethod(descope.MethodWhatsApp, "+19999999999", u) assert.Nil(t, err) assert.NotEmpty(t, u.Phone) @@ -178,6 +183,8 @@ func TestVerifyDeliveryMethod(t *testing.T) { u = &descope.User{Phone: "+19999999999"} err = a.verifyDeliveryMethod(descope.MethodSMS, "my username", u) assert.Nil(t, err) + err = a.verifyDeliveryMethod(descope.MethodVoice, "my username", u) + assert.Nil(t, err) err = a.verifyDeliveryMethod(descope.MethodWhatsApp, "my username", u) assert.Nil(t, err) } diff --git a/descope/internal/auth/otp.go b/descope/internal/auth/otp.go index 384b350c..b1022c31 100644 --- a/descope/internal/auth/otp.go +++ b/descope/internal/auth/otp.go @@ -119,7 +119,7 @@ func (auth *otp) UpdateUserPhone(ctx context.Context, method descope.DeliveryMet if !phoneRegex.MatchString(phone) { return "", utils.NewInvalidArgumentError("phone") } - if method != descope.MethodSMS && method != descope.MethodWhatsApp { + if method != descope.MethodSMS && method != descope.MethodVoice && method != descope.MethodWhatsApp { return "", utils.NewInvalidArgumentError("method") } pswd, err := getValidRefreshToken(r) diff --git a/descope/internal/auth/otp_test.go b/descope/internal/auth/otp_test.go index cdfaa643..41e929cb 100644 --- a/descope/internal/auth/otp_test.go +++ b/descope/internal/auth/otp_test.go @@ -120,6 +120,28 @@ func TestSignUpSMS(t *testing.T) { require.EqualValues(t, maskedPhone, mp) } +func TestSignUpVoice(t *testing.T) { + phone := "943248329844" + maskedPhone := "*********44" + a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) { + assert.EqualValues(t, composeSignUpURL(descope.MethodVoice), r.URL.RequestURI()) + + body, err := readBodyMap(r) + require.NoError(t, err) + assert.EqualValues(t, phone, body["phone"]) + assert.EqualValues(t, phone, body["loginId"]) + assert.EqualValues(t, "test", body["user"].(map[string]interface{})["name"]) + resp := MaskedPhoneRes{MaskedPhone: maskedPhone} + respBytes, err := utils.Marshal(resp) + require.NoError(t, err) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBuffer(respBytes))}, nil + }) + require.NoError(t, err) + mp, err := a.OTP().SignUp(context.Background(), descope.MethodVoice, phone, &descope.User{Name: "test"}, nil) + require.NoError(t, err) + require.EqualValues(t, maskedPhone, mp) +} + func TestSignUpWhatsApp(t *testing.T) { phone := "943248329844" maskedPhone := "*********44" @@ -172,6 +194,21 @@ func TestSignUpOrInSMS(t *testing.T) { require.NoError(t, err) } +func TestSignUpOrInVoice(t *testing.T) { + loginID := "943248329844" + a, err := newTestAuth(nil, DoOk(func(r *http.Request) { + assert.EqualValues(t, composeSignUpOrInURL(descope.MethodVoice), r.URL.RequestURI()) + + body, err := readBodyMap(r) + require.NoError(t, err) + assert.EqualValues(t, loginID, body["loginId"]) + assert.Nil(t, body["user"]) + })) + require.NoError(t, err) + _, err = a.OTP().SignUpOrIn(context.Background(), descope.MethodVoice, loginID, nil) + require.NoError(t, err) +} + func TestSignUpOrInSMSWithSignUpOptions(t *testing.T) { loginID := "943248329844" a, err := newTestAuth(nil, DoOk(func(r *http.Request) { @@ -191,6 +228,25 @@ func TestSignUpOrInSMSWithSignUpOptions(t *testing.T) { require.NoError(t, err) } +func TestSignUpOrInVoiceWithSignUpOptions(t *testing.T) { + loginID := "943248329844" + a, err := newTestAuth(nil, DoOk(func(r *http.Request) { + assert.EqualValues(t, composeSignUpOrInURL(descope.MethodVoice), r.URL.RequestURI()) + + body, err := readBodyMap(r) + require.NoError(t, err) + assert.EqualValues(t, loginID, body["loginId"]) + assert.Nil(t, body["user"]) + assert.EqualValues(t, map[string]interface{}{"customClaims": map[string]interface{}{"aa": "bb"}, "templateOptions": map[string]interface{}{"cc": "dd"}}, body["loginOptions"]) + })) + require.NoError(t, err) + _, err = a.OTP().SignUpOrIn(context.Background(), descope.MethodVoice, loginID, &descope.SignUpOptions{ + CustomClaims: map[string]interface{}{"aa": "bb"}, + TemplateOptions: map[string]string{"cc": "dd"}, + }) + require.NoError(t, err) +} + func TestSignUpOrInEmail(t *testing.T) { loginID := "943248329844" a, err := newTestAuth(nil, DoOk(func(r *http.Request) { @@ -351,6 +407,22 @@ func TestVerifyCodeWhatsApp(t *testing.T) { require.NoError(t, err) } +func TestVerifyCodeVoice(t *testing.T) { + phone := "943248329844" + code := "4914" + a, err := newTestAuth(nil, DoOk(func(r *http.Request) { + assert.EqualValues(t, composeVerifyCodeURL(descope.MethodVoice), r.URL.RequestURI()) + + body, err := readBodyMap(r) + require.NoError(t, err) + assert.EqualValues(t, phone, body["loginId"]) + assert.EqualValues(t, code, body["code"]) + })) + require.NoError(t, err) + _, err = a.OTP().VerifyCode(context.Background(), descope.MethodVoice, phone, code, nil) + require.NoError(t, err) +} + func TestVerifyCodeEmailResponseOption(t *testing.T) { email := "test@email.com" code := "4914" @@ -558,6 +630,46 @@ func TestUpdatePhoneOTP(t *testing.T) { require.EqualValues(t, maskedPhone, mp) } +func TestUpdatePhoneOTPVoice(t *testing.T) { + loginID := "943248329844" + phone := "+111111111111" + maskedPhone := "+*******111" + checkOptions := true + a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) { + assert.EqualValues(t, composeUpdateUserPhoneOTP(descope.MethodVoice), r.URL.RequestURI()) + + body, err := readBodyMap(r) + require.NoError(t, err) + assert.EqualValues(t, loginID, body["loginId"]) + assert.EqualValues(t, phone, body["phone"]) + if checkOptions { + assert.EqualValues(t, true, body["addToLoginIDs"]) + assert.EqualValues(t, true, body["onMergeUseExisting"]) + } else { + assert.EqualValues(t, nil, body["addToLoginIDs"]) + assert.EqualValues(t, nil, body["onMergeUseExisting"]) + } + + u, p := getProjectAndJwt(r) + assert.NotEmpty(t, u) + assert.NotEmpty(t, p) + resp := MaskedPhoneRes{MaskedPhone: maskedPhone} + respBytes, err := utils.Marshal(resp) + require.NoError(t, err) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBuffer(respBytes))}, nil + }) + require.NoError(t, err) + r := &http.Request{Header: http.Header{}} + r.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtTokenValid}) + mp, err := a.OTP().UpdateUserPhone(context.Background(), descope.MethodSMS, loginID, phone, &descope.UpdateOptions{AddToLoginIDs: true, OnMergeUseExisting: true}, r) + require.NoError(t, err) + require.EqualValues(t, maskedPhone, mp) + checkOptions = false + mp, err = a.OTP().UpdateUserPhone(context.Background(), descope.MethodSMS, loginID, phone, nil, r) + require.NoError(t, err) + require.EqualValues(t, maskedPhone, mp) +} + func TestUpdatePhoneOTPWithTemplateOptions(t *testing.T) { loginID := "943248329844" phone := "+111111111111" diff --git a/descope/internal/auth/utils.go b/descope/internal/auth/utils.go index f517f045..7a71e9df 100644 --- a/descope/internal/auth/utils.go +++ b/descope/internal/auth/utils.go @@ -141,6 +141,8 @@ func newSignUpRequestBody(method descope.DeliveryMethod, user *descope.User) *au switch method { case descope.MethodSMS: return &authenticationSignUpRequestBody{Phone: user.Phone} + case descope.MethodVoice: + return &authenticationSignUpRequestBody{Phone: user.Phone} case descope.MethodWhatsApp: return &authenticationSignUpRequestBody{WhatsApp: user.Phone} } diff --git a/descope/internal/mgmt/user_test.go b/descope/internal/mgmt/user_test.go index f57e9a65..825e05be 100644 --- a/descope/internal/mgmt/user_test.go +++ b/descope/internal/mgmt/user_test.go @@ -1392,6 +1392,39 @@ func TestGenerateOTPForTestUserSuccess(t *testing.T) { assert.EqualValues(t, code, resCode) } +func TestGenerateOTPForTestUserSuccessMethodVoice(t *testing.T) { + loginID := "some-id" + code := "123456" + response := map[string]any{ + "loginId": loginID, + "code": code, + } + loginOptions := descope.LoginOptions{ + MFA: true, + } + visited := false + m := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { + visited = true + require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + req := map[string]any{} + require.NoError(t, helpers.ReadBody(r, &req)) + require.Equal(t, loginID, req["loginId"]) + require.Equal(t, string(descope.MethodVoice), req["deliveryMethod"]) + b, err := utils.Marshal(req["loginOptions"]) + require.NoError(t, err) + var loginOptionsReq descope.LoginOptions + err = utils.Unmarshal(b, &loginOptionsReq) + require.NoError(t, err) + require.Equal(t, loginOptions, loginOptionsReq) + }, response)) + + resCode, err := m.User().GenerateOTPForTestUser(context.Background(), descope.MethodVoice, loginID, &loginOptions) + require.NoError(t, err) + require.NotEmpty(t, resCode) + require.True(t, visited) + assert.EqualValues(t, code, resCode) +} + func TestGenerateOTPForTestUserNoLoginID(t *testing.T) { loginID := "some-id" code := "123456" diff --git a/descope/sdk/auth.go b/descope/sdk/auth.go index 9f6f0a80..3d364bf1 100644 --- a/descope/sdk/auth.go +++ b/descope/sdk/auth.go @@ -109,7 +109,7 @@ type OTP interface { UpdateUserEmail(ctx context.Context, loginID, email string, updateOptions *descope.UpdateOptions, request *http.Request) (maskedAddress string, err error) // UpdateUserPhone - Use to update phone and validate via OTP - // allowed methods are phone based methods - whatsapp and SMS + // allowed methods are phone based methods - whatsapp and SMS or Voice call // LoginID of user whom we want to update // UpdateOptions to determine whether to add email as a login id and if to merge with existing user in that case // Request is needed to obtain JWT and send it to Descope, for verification @@ -152,7 +152,7 @@ type Password interface { // redirectURL is an optional parameter that is used by Magic Link or Enchanted Link // if those are the chosen reset methods. See the Magic Link and Enchanted Link sections // for more details. - // templateOptions is used to pass dynamic options for the messaging (email / text message) template + // templateOptions is used to pass dynamic options for the messaging (email / text message, voice call) template SendPasswordReset(ctx context.Context, loginID, redirectURL string, templateOptions map[string]string) error // UpdateUserPassword - updates a user's password according to the given loginID. diff --git a/descope/types.go b/descope/types.go index 2025f485..7df0be65 100644 --- a/descope/types.go +++ b/descope/types.go @@ -875,6 +875,7 @@ type ProjectTag string const ( MethodWhatsApp DeliveryMethod = "whatsapp" MethodSMS DeliveryMethod = "sms" + MethodVoice DeliveryMethod = "voice" MethodEmail DeliveryMethod = "email" MethodEmbedded DeliveryMethod = "Embedded" diff --git a/examples/ginwebapp/go.mod b/examples/ginwebapp/go.mod index f2e3c0aa..7a878741 100644 --- a/examples/ginwebapp/go.mod +++ b/examples/ginwebapp/go.mod @@ -3,7 +3,7 @@ module github.com/descope/go-sdk/examples/ginwebapp go 1.18 require ( - github.com/descope/go-sdk v1.5.7 + github.com/descope/go-sdk v1.6.2 github.com/descope/go-sdk/descope/gin v0.0.0-00010101000000-000000000000 github.com/gin-gonic/gin v1.9.1 ) @@ -23,9 +23,9 @@ require ( github.com/leodido/go-urn v1.2.4 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx/v2 v2.0.19 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -35,10 +35,10 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/examples/ginwebapp/go.sum b/examples/ginwebapp/go.sum index c6f06355..71c9165a 100644 --- a/examples/ginwebapp/go.sum +++ b/examples/ginwebapp/go.sum @@ -39,12 +39,12 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY= -github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= @@ -71,7 +71,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -79,16 +79,16 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e h1:Ctm9yurWsg7aWwIpH9Bnap/IdSVxixymIb3MhiMEQQA= golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/examples/ginwebapp/main.go b/examples/ginwebapp/main.go index 4b2ca69e..77138c68 100644 --- a/examples/ginwebapp/main.go +++ b/examples/ginwebapp/main.go @@ -61,6 +61,7 @@ func help(c *gin.Context) { helpTxt := "Sign up or in with otp email go to /otp/signupOrIn?email=\n\n" helpTxt += "Sign up or in with otp sms go to /otp/signupOrIn?sms=\n\n" helpTxt += "Sign up or in with otp whatsapp go to /otp/signupOrIn?whatsapp=\n\n" + helpTxt += "Sign up or in with otp voice go to /otp/signupOrIn?voice=\n\n" helpTxt += "-------------------------------------\n\n" helpTxt += "See a private page /private\n\n" helpTxt += "To see more examples see out webapp example (../webapp)\n\n" @@ -111,6 +112,9 @@ func getMethodAndLoginID(c *gin.Context) (descope.DeliveryMethod, string) { } else if whatsapp := c.Query("whatsapp"); whatsapp != "" { method = descope.MethodWhatsApp loginID = whatsapp + } else if voice := c.Query("voice"); voice != "" { + method = descope.MethodVoice + loginID = voice } return method, loginID } diff --git a/examples/webapp/go.mod b/examples/webapp/go.mod index 0c925ab8..60a1f0d8 100644 --- a/examples/webapp/go.mod +++ b/examples/webapp/go.mod @@ -12,14 +12,14 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx/v2 v2.0.19 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.18.0 // indirect ) replace github.com/descope/go-sdk => ../../ diff --git a/examples/webapp/go.sum b/examples/webapp/go.sum index 2e9bc1d1..323f5918 100644 --- a/examples/webapp/go.sum +++ b/examples/webapp/go.sum @@ -10,12 +10,12 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY= -github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -25,13 +25,13 @@ github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e h1:Ctm9yurWsg7aWwIpH9Bnap/IdSVxixymIb3MhiMEQQA= golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/examples/webapp/main.go b/examples/webapp/main.go index 1b522137..d6c50d90 100644 --- a/examples/webapp/main.go +++ b/examples/webapp/main.go @@ -129,9 +129,11 @@ func help(w http.ResponseWriter, r *http.Request) { helpTxt := "Sign up with otp email go to: /otp/signup?email=\n\n" helpTxt += "Sign up with otp sms go to: /otp/signup?sms=\n\n" helpTxt += "Sign up with otp whatsapp go to: /otp/signup?whatsapp=\n\n" + helpTxt += "Sign up with otp voice go to: /otp/signup?voice=\n\n" helpTxt += "Sign in of existing user with otp email go to: /otp/signin?email=\n\n" helpTxt += "Sign in of existing user with otp sms go to: /otp/signin?sms=\n\n" helpTxt += "Sign in of existing user with otp whatsapp go to: /otp/signin?whatsapp=\n\n" + helpTxt += "Sign in of existing user with otp voice go to: /otp/signin?voice=\n\n" helpTxt += "---------------------------------------------------------\n\n" helpTxt += "Sign up/in with OAuth go to: /oauth?provider=[google|github|facebook]\n\n" helpTxt += "---------------------------------------------------------\n\n" @@ -140,9 +142,11 @@ func help(w http.ResponseWriter, r *http.Request) { helpTxt += "Sign up with magiclink and email go to: /magiclink/signup?email=\n\n" helpTxt += "Sign up with magiclink and sms go to: /magiclink/signup?sms=\n\n" helpTxt += "Sign up with magiclink and whatsapp go to: /magiclink/signup?whatsapp=\n\n" + helpTxt += "Sign up with magiclink and voice go to: /magiclink/signup?voice=\n\n" helpTxt += "Sign in of existing user with magiclink email go to: /magiclink/signin?email=\n\n" helpTxt += "Sign in of existing user with magiclink sms go to: /magiclink/signin?sms=\n\n" helpTxt += "Sign in of existing user with magiclink whatsapp go to: /magiclink/signin?whatsapp=\n\n" + helpTxt += "Sign in of existing user with magiclink voice go to: /magiclink/signin?voice=\n\n" helpTxt += "---------------------------------------------------------\n\n" helpTxt += "Sign up with enchanted link go to: /enchantedlink/signup?email=\n\n" helpTxt += "Sign in of existing user with enchanted link email go to: /enchantedlink/signin?email=\n\n" @@ -615,6 +619,9 @@ func getMethodAndLoginID(r *http.Request) (descope.DeliveryMethod, string) { } else if whatsapp, ok := r.URL.Query()["whatsapp"]; ok { method = descope.MethodWhatsApp loginID = whatsapp[0] + } else if voice, ok := r.URL.Query()["voice"]; ok { + method = descope.MethodVoice + loginID = voice[0] } return method, loginID } From 0aad873479a652ed48010fde19540f47d1176ce4 Mon Sep 17 00:00:00 2001 From: dorsha Date: Wed, 3 Apr 2024 00:44:32 +0300 Subject: [PATCH 2/3] Fix test --- descope/internal/auth/otp_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/descope/internal/auth/otp_test.go b/descope/internal/auth/otp_test.go index 41e929cb..175c6344 100644 --- a/descope/internal/auth/otp_test.go +++ b/descope/internal/auth/otp_test.go @@ -661,11 +661,11 @@ func TestUpdatePhoneOTPVoice(t *testing.T) { require.NoError(t, err) r := &http.Request{Header: http.Header{}} r.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtTokenValid}) - mp, err := a.OTP().UpdateUserPhone(context.Background(), descope.MethodSMS, loginID, phone, &descope.UpdateOptions{AddToLoginIDs: true, OnMergeUseExisting: true}, r) + mp, err := a.OTP().UpdateUserPhone(context.Background(), descope.MethodVoice, loginID, phone, &descope.UpdateOptions{AddToLoginIDs: true, OnMergeUseExisting: true}, r) require.NoError(t, err) require.EqualValues(t, maskedPhone, mp) checkOptions = false - mp, err = a.OTP().UpdateUserPhone(context.Background(), descope.MethodSMS, loginID, phone, nil, r) + mp, err = a.OTP().UpdateUserPhone(context.Background(), descope.MethodVoice, loginID, phone, nil, r) require.NoError(t, err) require.EqualValues(t, maskedPhone, mp) } From 5906aed67f1d40a49b3772e2065b051f722c7a7f Mon Sep 17 00:00:00 2001 From: dorsha Date: Wed, 3 Apr 2024 01:12:16 +0300 Subject: [PATCH 3/3] Fix docs --- README.md | 6 +++--- descope/sdk/auth.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c07ad709..b0e9a3b7 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ For rate limiting information, please confer to the [API Rate Limits](#api-rate- ### OTP Authentication -Send a user a one-time password (OTP) using your preferred delivery method (_email / SMS / Voice call_). An email address or phone number must be provided accordingly. +Send a user a one-time password (OTP) using your preferred delivery method (_Email / SMS / Voice call / WhatsApp_). An email address or phone number must be provided accordingly. The user can either `sign up`, `sign in` or `sign up or in` @@ -125,7 +125,7 @@ The session and refresh JWTs should be returned to the caller, and passed with e ### Magic Link -Send a user a Magic Link using your preferred delivery method (_email / SMS_). +Send a user a Magic Link using your preferred delivery method (_Email / SMS / WhatsApp_). The Magic Link will redirect the user to page where the its token needs to be verified. This redirection can be configured in code, or globally in the [Descope Console](https://app.descope.com/settings/authentication/magiclink) @@ -1594,7 +1594,7 @@ assert.EqualValues(t, updateJWTWithCustomClaimsResponse, res) ### Utils for your end to end (e2e) tests and integration tests To ease your e2e tests, we exposed dedicated management methods, -that way, you don't need to use 3rd party messaging services in order to receive sign-in/up Emails, SMS or Voice call, and avoid the need of parsing the code and token from them. +that way, you don't need to use 3rd party messaging services in order to receive sign-in/up Email, SMS, Voice call or WhatsApp, and avoid the need of parsing the code and token from them. ```go // User for test can be created, this user will be able to generate code/link without diff --git a/descope/sdk/auth.go b/descope/sdk/auth.go index 3d364bf1..93853759 100644 --- a/descope/sdk/auth.go +++ b/descope/sdk/auth.go @@ -152,7 +152,7 @@ type Password interface { // redirectURL is an optional parameter that is used by Magic Link or Enchanted Link // if those are the chosen reset methods. See the Magic Link and Enchanted Link sections // for more details. - // templateOptions is used to pass dynamic options for the messaging (email / text message, voice call) template + // templateOptions is used to pass dynamic options for the messaging (Email / SMS / Voice call / WhatsApp) template SendPasswordReset(ctx context.Context, loginID, redirectURL string, templateOptions map[string]string) error // UpdateUserPassword - updates a user's password according to the given loginID.