diff --git a/recipe/thirdparty/override_test.go b/recipe/thirdparty/override_test.go index 72b31451..ae992bcc 100644 --- a/recipe/thirdparty/override_test.go +++ b/recipe/thirdparty/override_test.go @@ -26,6 +26,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/supertokens/supertokens-golang/recipe/passwordless" + "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" @@ -34,6 +36,346 @@ import ( "gopkg.in/h2non/gock.v1" ) +func TestOverridingAPIs(t *testing.T) { + var userRef *tpmodels.User + var newUser bool + 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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + passwordless.Init(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{customProvider1}, + }, + Override: &tpmodels.OverrideStruct{ + APIs: func(originalImplementation tpmodels.APIInterface) tpmodels.APIInterface { + originalThirdPartySignInUpPost := *originalImplementation.SignInUpPOST + *originalImplementation.SignInUpPOST = func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.SignInUpPOSTResponse, error) { + resp, err := originalThirdPartySignInUpPost(provider, input, tenantId, options, userContext) + userRef = &resp.OK.User + newUser = resp.OK.CreatedNewUser + return resp, err + } + return originalImplementation + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + q, err := supertokens.GetNewQuerierInstanceOrThrowError("") + if err != nil { + t.Error(err.Error()) + } + apiV, err := q.GetQuerierAPIVersion() + if err != nil { + t.Error(err.Error()) + } + + if unittesting.MaxVersion(apiV, "2.11") == "2.11" { + return + } + + mux := http.NewServeMux() + + mux.HandleFunc("/user", func(rw http.ResponseWriter, r *http.Request) { + userId := r.URL.Query().Get("userId") + fetchedUser, err := GetUserByID(userId) + if err != nil { + t.Error(err.Error()) + } + jsonResp, err := json.Marshal(fetchedUser) + if err != nil { + t.Errorf("Error happened in JSON marshal. Err: %s", err) + } + rw.WriteHeader(200) + rw.Write(jsonResp) + }) + + defer gock.OffAll() + gock.New("https://test.com"). + Post("/oauth/token"). + Persist(). + Reply(200). + JSON(map[string]string{}) + + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + formFields := map[string]interface{}{ + "thirdPartyId": "custom", + "redirectURIInfo": map[string]interface{}{ + "redirectURIOnProviderDashboard": testServer.URL + "/callback", + "redirectURIQueryParams": map[string]interface{}{ + "code": "abcdefghj", + }, + }, + } + + postBody, err := json.Marshal(formFields) + if err != nil { + t.Error(err.Error()) + } + + gock.New(testServer.URL).EnableNetworking().Persist() + gock.New("http://localhost:8080/").EnableNetworking().Persist() + + resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + signUpResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) + fetchedUser := signUpResponse["user"].(map[string]interface{}) + + assert.NotNil(t, userRef) + assert.True(t, newUser) + assert.Equal(t, fetchedUser["email"], userRef.Email) + assert.Equal(t, fetchedUser["id"], userRef.ID) + assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) + assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) + + userRef = nil + assert.Nil(t, userRef) + + formFields = map[string]interface{}{ + "thirdPartyId": "custom", + "redirectURIInfo": map[string]interface{}{ + "redirectURIOnProviderDashboard": testServer.URL + "/callback", + "redirectURIQueryParams": map[string]interface{}{ + "code": "abcdefghj", + }, + }, + } + + postBody, err = json.Marshal(formFields) + if err != nil { + t.Error(err.Error()) + } + + resp, err = http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + signInResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) + fetchedUserFromSignIn := signInResponse["user"].(map[string]interface{}) + + assert.NotNil(t, userRef) + assert.False(t, newUser) + assert.Equal(t, fetchedUserFromSignIn["email"], userRef.Email) + assert.Equal(t, fetchedUserFromSignIn["id"], userRef.ID) + assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) + assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) +} + +func TestOverridingFunctions(t *testing.T) { + var userRef *tpmodels.User + var newUser bool + 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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + passwordless.Init(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{customProvider1}, + }, + Override: &tpmodels.OverrideStruct{ + Functions: func(originalImplementation tpmodels.RecipeInterface) tpmodels.RecipeInterface { + originalThirdPartySignInUp := *originalImplementation.SignInUp + *originalImplementation.SignInUp = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) { + resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) + userRef = &resp.OK.User + newUser = resp.OK.CreatedNewUser + return resp, err + } + originalGetUserById := *originalImplementation.GetUserByID + *originalImplementation.GetUserByID = func(userID string, userContext supertokens.UserContext) (*tpmodels.User, error) { + resp, err := originalGetUserById(userID, userContext) + userRef = resp + return resp, err + } + return originalImplementation + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + q, err := supertokens.GetNewQuerierInstanceOrThrowError("") + if err != nil { + t.Error(err.Error()) + } + apiV, err := q.GetQuerierAPIVersion() + if err != nil { + t.Error(err.Error()) + } + + if unittesting.MaxVersion(apiV, "2.11") == "2.11" { + return + } + + mux := http.NewServeMux() + + mux.HandleFunc("/user", func(rw http.ResponseWriter, r *http.Request) { + userId := r.URL.Query().Get("userId") + fetchedUser, err := GetUserByID(userId) + if err != nil { + t.Error(err.Error()) + } + jsonResp, err := json.Marshal(fetchedUser) + if err != nil { + t.Errorf("Error happened in JSON marshal. Err: %s", err) + } + rw.WriteHeader(200) + rw.Write(jsonResp) + }) + + defer gock.OffAll() + gock.New("https://test.com"). + Post("/oauth/token"). + Persist(). + Reply(200). + JSON(map[string]string{}) + + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + formFields := map[string]interface{}{ + "thirdPartyId": "custom", + "redirectURIInfo": map[string]interface{}{ + "redirectURIOnProviderDashboard": testServer.URL + "/callback", + "redirectURIQueryParams": map[string]interface{}{ + "code": "abcdefghj", + }, + }, + } + + postBody, err := json.Marshal(formFields) + if err != nil { + t.Error(err.Error()) + } + + gock.New(testServer.URL).EnableNetworking().Persist() + gock.New("http://localhost:8080/").EnableNetworking().Persist() + + resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + signUpResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) + fetchedUser := signUpResponse["user"].(map[string]interface{}) + + assert.NotNil(t, userRef) + assert.True(t, newUser) + assert.Equal(t, fetchedUser["email"], userRef.Email) + assert.Equal(t, fetchedUser["id"], userRef.ID) + assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) + assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) + + userRef = nil + assert.Nil(t, userRef) + + formFields = map[string]interface{}{ + "thirdPartyId": "custom", + "redirectURIInfo": map[string]interface{}{ + "redirectURIOnProviderDashboard": testServer.URL + "/callback", + "redirectURIQueryParams": map[string]interface{}{ + "code": "abcdefghj", + }, + }, + } + + postBody, err = json.Marshal(formFields) + if err != nil { + t.Error(err.Error()) + } + + resp, err = http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + signInResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) + fetchedUserFromSignIn := signInResponse["user"].(map[string]interface{}) + + assert.NotNil(t, userRef) + assert.False(t, newUser) + assert.Equal(t, fetchedUserFromSignIn["email"], userRef.Email) + assert.Equal(t, fetchedUserFromSignIn["id"], userRef.ID) + assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) + assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) + + userRef = nil + assert.Nil(t, userRef) + + req, err := http.NewRequest(http.MethodPost, testServer.URL+"/user", nil) + assert.NoError(t, err) + + query := req.URL.Query() + query.Add("userId", fetchedUserFromSignIn["id"].(string)) + req.URL.RawQuery = query.Encode() + + res, err := http.DefaultClient.Do(req) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + + userByIdResponse := *unittesting.HttpResponseToConsumableInformation(res.Body) + + assert.NotNil(t, userRef) + assert.Equal(t, userByIdResponse["email"], userRef.Email) + assert.Nil(t, userByIdResponse["phoneNumber"]) + assert.Equal(t, userByIdResponse["id"], userRef.ID) + assert.Equal(t, userByIdResponse["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) + assert.Equal(t, userByIdResponse["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) +} + func TestOverrideFunctions(t *testing.T) { var createdNewUser bool var user tpmodels.User diff --git a/recipe/thirdparty/provider_test.go b/recipe/thirdparty/provider_test.go index 73b1f82b..0549e927 100644 --- a/recipe/thirdparty/provider_test.go +++ b/recipe/thirdparty/provider_test.go @@ -19,7 +19,6 @@ package thirdparty import ( "encoding/json" "errors" - "github.com/supertokens/supertokens-golang/recipe/session" "io" "io/ioutil" "net" @@ -29,6 +28,9 @@ import ( "strings" "testing" + "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" + "github.com/stretchr/testify/assert" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" "github.com/supertokens/supertokens-golang/supertokens" @@ -36,6 +38,408 @@ import ( "gopkg.in/h2non/gock.v1" ) +const privateKey = "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----" + +func TestWithAdditionalParamsCheckTheyArePresentInAuthorizationURLForThirdPartyProviderApple(t *testing.T) { + clientId := "test" + + 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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: clientId, + AdditionalConfig: map[string]interface{}{ + "keyId": "test-key", + "privateKey": privateKey, + "teamId": "test-team-id", + }, + }, + }, + AuthorizationEndpointQueryParams: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + }, + }, + }, + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + q, err := supertokens.GetNewQuerierInstanceOrThrowError("") + if err != nil { + t.Error(err.Error()) + } + apiV, err := q.GetQuerierAPIVersion() + if err != nil { + t.Error(err.Error()) + } + + if unittesting.MaxVersion(apiV, "2.11") == "2.11" { + return + } + + providerRes, err := GetProvider("public", "apple", nil) + assert.NoError(t, err) + + providerInfo := providerRes + + assert.Equal(t, "apple", providerInfo.ID) + + assert.Equal(t, "https://appleid.apple.com/auth/authorize", providerInfo.Config.AuthorizationEndpoint) + assert.Equal(t, "https://appleid.apple.com/auth/token", providerInfo.Config.TokenEndpoint) + + authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) + assert.NoError(t, err) + + urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) + assert.NoError(t, err) + + authParams := urlObj.Query() + + assert.Equal(t, url.Values{ + "client_id": {"test"}, + "response_mode": {"form_post"}, + "response_type": {"code"}, + "redirect_uri": {"redirect"}, + "scope": {"openid email"}, + "key1": {"value1"}, + "key2": {"value2"}, + }, authParams) +} + +func TestWithPassingScopesInConfigForThirdPartyProviderApple(t *testing.T) { + clientId := "test" + + 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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: clientId, + Scope: []string{"test-scope-1", "test-scope-2"}, + AdditionalConfig: map[string]interface{}{ + "keyId": "test-key", + "privateKey": privateKey, + "teamId": "test-team-id", + }, + }, + }, + }, + }, + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + q, err := supertokens.GetNewQuerierInstanceOrThrowError("") + if err != nil { + t.Error(err.Error()) + } + apiV, err := q.GetQuerierAPIVersion() + if err != nil { + t.Error(err.Error()) + } + + if unittesting.MaxVersion(apiV, "2.11") == "2.11" { + return + } + + providerRes, err := GetProvider("public", "apple", nil) + assert.NoError(t, err) + + providerInfo := providerRes + + assert.Equal(t, "apple", providerInfo.ID) + + assert.Equal(t, "https://appleid.apple.com/auth/authorize", providerInfo.Config.AuthorizationEndpoint) + assert.Equal(t, "https://appleid.apple.com/auth/token", providerInfo.Config.TokenEndpoint) + + authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) + assert.NoError(t, err) + + urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) + assert.NoError(t, err) + + authParams := urlObj.Query() + + assert.Equal(t, url.Values{ + "client_id": {"test"}, + "response_mode": {"form_post"}, + "response_type": {"code"}, + "redirect_uri": {"redirect"}, + "scope": {"test-scope-1 test-scope-2"}, + }, authParams) +} + +func TestWithMinimumConfigForThirdPartyProviderApple(t *testing.T) { + clientId := "test" + + 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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: clientId, + AdditionalConfig: map[string]interface{}{ + "keyId": "test-key", + "privateKey": privateKey, + "teamId": "test-team-id", + }, + }, + }, + }, + }, + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + q, err := supertokens.GetNewQuerierInstanceOrThrowError("") + if err != nil { + t.Error(err.Error()) + } + apiV, err := q.GetQuerierAPIVersion() + if err != nil { + t.Error(err.Error()) + } + + if unittesting.MaxVersion(apiV, "2.11") == "2.11" { + return + } + + providerRes, err := GetProvider("public", "apple", nil) + assert.NoError(t, err) + + providerInfo := providerRes + + assert.Equal(t, "apple", providerInfo.ID) + + assert.Equal(t, "https://appleid.apple.com/auth/authorize", providerInfo.Config.AuthorizationEndpoint) + assert.Equal(t, "https://appleid.apple.com/auth/token", providerInfo.Config.TokenEndpoint) + + authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) + assert.NoError(t, err) + + urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) + assert.NoError(t, err) + + authParams := urlObj.Query() + + assert.Equal(t, url.Values{ + "client_id": {"test"}, + "response_mode": {"form_post"}, + "response_type": {"code"}, + "redirect_uri": {"redirect"}, + "scope": {"openid email"}, + }, authParams) + + tokenParams := url.Values{} + + defer gock.OffAll() + gock.New("https://appleid.apple.com"). + Post("/auth/token"). + Persist(). + Map(func(r *http.Request) *http.Request { + data, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + tokenParams, err = url.ParseQuery(string(data)) + assert.NoError(t, err) + return r + }). + Reply(200). + JSON(map[string]string{ + "id_token": "abcd", + }) + + _, err = providerInfo.ExchangeAuthCodeForOAuthTokens(tpmodels.TypeRedirectURIInfo{ + RedirectURIOnProviderDashboard: "redirect", + RedirectURIQueryParams: map[string]interface{}{ + "code": "abcd", + }, + }, &map[string]interface{}{}) + assert.NoError(t, err) + + assert.NotEmpty(t, tokenParams.Get("client_secret")) + tokenParams.Del("client_secret") + + assert.Equal(t, url.Values{ + "client_id": {"test"}, + "grant_type": {"authorization_code"}, + "code": {"abcd"}, + "redirect_uri": {"redirect"}, + }, tokenParams) +} + +func TestPassingAdditionalParamsCheckTheyArePresentInAuthorizationUrlForThirdPartyProviderGoogle(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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "test", + ClientSecret: "test-secret", + }, + }, + AuthorizationEndpointQueryParams: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + }, + }, + }, + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + q, err := supertokens.GetNewQuerierInstanceOrThrowError("") + if err != nil { + t.Error(err.Error()) + } + apiV, err := q.GetQuerierAPIVersion() + if err != nil { + t.Error(err.Error()) + } + + if unittesting.MaxVersion(apiV, "2.11") == "2.11" { + return + } + + providerRes, err := GetProvider("public", "google", nil) + assert.NoError(t, err) + + providerInfo := providerRes + assert.Equal(t, "google", providerInfo.ID) + + assert.Equal(t, "https://oauth2.googleapis.com/token", providerInfo.Config.TokenEndpoint) + assert.Equal(t, "https://accounts.google.com/o/oauth2/v2/auth", providerInfo.Config.AuthorizationEndpoint) + assert.Equal(t, "https://openidconnect.googleapis.com/v1/userinfo", providerInfo.Config.UserInfoEndpoint) + + authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) + assert.NoError(t, err) + + urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) + assert.NoError(t, err) + + authParams := urlObj.Query() + + assert.Equal(t, url.Values{ + "client_id": {"test"}, + "access_type": {"offline"}, + "include_granted_scopes": {"true"}, + "response_type": {"code"}, + "redirect_uri": {"redirect"}, + "scope": {"openid email"}, + "key1": {"value1"}, + "key2": {"value2"}, + }, authParams) +} + func TestMinimumConfigForGoogleAsThirdPartyProvider(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ @@ -901,3 +1305,59 @@ func TestThatSignInUpWorksIfValidateAccessTokenDoesNotReturnError(t *testing.T) assert.True(t, overrideValidateCalled) assert.Equal(t, response["status"], "OK") } + +func TestWithDuplicateProvider(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{ + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "test", + ClientSecret: "test-secret", + }, + }, + }, + }, + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "test", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + assert.Equal(t, "The providers array has multiple entries for the same third party provider.", err.Error()) + } +} diff --git a/recipe/thirdpartypasswordless/deleteUser_test.go b/recipe/thirdpartypasswordless/deleteUser_test.go deleted file mode 100644 index 77faed48..00000000 --- a/recipe/thirdpartypasswordless/deleteUser_test.go +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package thirdpartypasswordless - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestDeletePhoneNumber(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(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - 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()) - } - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - cdiVersion, err := querier.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - if unittesting.MaxVersion("2.11", cdiVersion) == cdiVersion { - res, err := PasswordlessSignInUpByEmail("public", "test@example.com") - if err != nil { - t.Error(err.Error()) - } - phoneNumber := "+1234567890" - _, err = UpdatePasswordlessUser(res.User.ID, nil, &phoneNumber) - if err != nil { - t.Error(err.Error()) - } - deleteResponse, err := DeletePhoneNumberForUser(res.User.ID) - if err != nil { - t.Error(err.Error()) - } - assert.NotNil(t, deleteResponse.OK) - - userInfo, err := GetUserByID(res.User.ID) - if err != nil { - t.Error(err.Error()) - } - assert.Nil(t, userInfo.PhoneNumber) - } -} - -func TestDeleteEmail(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(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - 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()) - } - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - cdiVersion, err := querier.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - if unittesting.MaxVersion("2.11", cdiVersion) == cdiVersion { - res, err := PasswordlessSignInUpByEmail("public", "test@example.com") - if err != nil { - t.Error(err.Error()) - } - phoneNumber := "+1234567890" - _, err = UpdatePasswordlessUser(res.User.ID, nil, &phoneNumber) - if err != nil { - t.Error(err.Error()) - } - deleteResponse, err := DeleteEmailForPasswordlessUser(res.User.ID) - if err != nil { - t.Error(err.Error()) - } - assert.NotNil(t, deleteResponse.OK) - - userInfo, err := GetUserByID(res.User.ID) - if err != nil { - t.Error(err.Error()) - } - assert.Nil(t, userInfo.Email) - } -} - -func TestDeleteEmailAndPhoneShouldThrowError(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(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - 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()) - } - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - cdiVersion, err := querier.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - if unittesting.MaxVersion("2.11", cdiVersion) == cdiVersion { - res, err := PasswordlessSignInUpByEmail("public", "test@example.com") - if err != nil { - t.Error(err.Error()) - } - phoneNumber := "+1234567890" - _, err = UpdatePasswordlessUser(res.User.ID, nil, &phoneNumber) - if err != nil { - t.Error(err.Error()) - } - deleteResponse, err := DeleteEmailForPasswordlessUser(res.User.ID) - if err != nil { - t.Error(err.Error()) - } - assert.NotNil(t, deleteResponse.OK) - - userInfo, err := GetUserByID(res.User.ID) - if err != nil { - t.Error(err.Error()) - } - assert.Nil(t, userInfo.Email) - - _, err = DeletePhoneNumberForUser(res.User.ID) - assert.NotNil(t, err) - assert.Equal(t, err.Error(), "SuperTokens core threw an error for a request to path: '/recipe/user' with status code: 400 and message: You cannot clear both email and phone number of a user\n") - } -} diff --git a/recipe/thirdpartypasswordless/getUserByEmailFeature_test.go b/recipe/thirdpartypasswordless/getUserByEmailFeature_test.go deleted file mode 100644 index 4e499721..00000000 --- a/recipe/thirdpartypasswordless/getUserByEmailFeature_test.go +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package thirdpartypasswordless - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestInvalidEmailYieldsEmptyUsersArray(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(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - mockThirdPartyProvider1, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - users, err := GetUsersByEmail("public", "john.doe@example.com") - assert.NoError(t, err) - assert.Equal(t, 0, len(users)) -} - -func TestValidEmailYieldsThirdPartyUsers(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(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - mockThirdPartyProvider1, - mockThirdPartyProvider2, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - ThirdPartyManuallyCreateOrUpdateUser("public", "mock1", "thirdPartyJohnDoe", "john.doe@example.com") - ThirdPartyManuallyCreateOrUpdateUser("public", "mock2", "thirdPartyJohnDoe", "john.doe@example.com") - - users, err := GetUsersByEmail("public", "john.doe@example.com") - assert.NoError(t, err) - assert.Equal(t, 2, len(users)) - - for _, u := range users { - assert.Equal(t, "john.doe@example.com", *u.Email) - assert.Equal(t, "thirdPartyJohnDoe", u.ThirdParty.UserID) - assert.NotNil(t, u.ID) - assert.NotNil(t, u.TimeJoined) - assert.NotNil(t, u.ThirdParty.ID) - assert.Nil(t, u.PhoneNumber) - } -} diff --git a/recipe/thirdpartypasswordless/override_test.go b/recipe/thirdpartypasswordless/override_test.go deleted file mode 100644 index 605eea36..00000000 --- a/recipe/thirdpartypasswordless/override_test.go +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package thirdpartypasswordless - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" - "gopkg.in/h2non/gock.v1" -) - -func TestOverridingFunctions(t *testing.T) { - var userRef *tplmodels.User - var newUser bool - 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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{customProvider1}, - Override: &tplmodels.OverrideStruct{ - Functions: func(originalImplementation tplmodels.RecipeInterface) tplmodels.RecipeInterface { - originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp - *originalImplementation.ThirdPartySignInUp = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tplmodels.ThirdPartySignInUp, error) { - resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - userRef = &resp.OK.User - newUser = resp.OK.CreatedNewUser - return resp, err - } - originalGetUserById := *originalImplementation.GetUserByID - *originalImplementation.GetUserByID = func(userID string, userContext supertokens.UserContext) (*tplmodels.User, error) { - resp, err := originalGetUserById(userID, userContext) - userRef = resp - return resp, err - } - return originalImplementation - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - mux := http.NewServeMux() - - mux.HandleFunc("/user", func(rw http.ResponseWriter, r *http.Request) { - userId := r.URL.Query().Get("userId") - fetchedUser, err := GetUserByID(userId) - if err != nil { - t.Error(err.Error()) - } - jsonResp, err := json.Marshal(fetchedUser) - if err != nil { - t.Errorf("Error happened in JSON marshal. Err: %s", err) - } - rw.WriteHeader(200) - rw.Write(jsonResp) - }) - - defer gock.OffAll() - gock.New("https://test.com"). - Post("/oauth/token"). - Persist(). - Reply(200). - JSON(map[string]string{}) - - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - formFields := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": testServer.URL + "/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(formFields) - if err != nil { - t.Error(err.Error()) - } - - gock.New(testServer.URL).EnableNetworking().Persist() - gock.New("http://localhost:8080/").EnableNetworking().Persist() - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - signUpResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) - fetchedUser := signUpResponse["user"].(map[string]interface{}) - - assert.NotNil(t, userRef) - assert.True(t, newUser) - assert.Equal(t, fetchedUser["email"], *userRef.Email) - assert.Equal(t, fetchedUser["id"], userRef.ID) - assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) - assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) - - userRef = nil - assert.Nil(t, userRef) - - formFields = map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": testServer.URL + "/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err = json.Marshal(formFields) - if err != nil { - t.Error(err.Error()) - } - - resp, err = http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - signInResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) - fetchedUserFromSignIn := signInResponse["user"].(map[string]interface{}) - - assert.NotNil(t, userRef) - assert.False(t, newUser) - assert.Equal(t, fetchedUserFromSignIn["email"], *userRef.Email) - assert.Equal(t, fetchedUserFromSignIn["id"], userRef.ID) - assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) - assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) - - userRef = nil - assert.Nil(t, userRef) - - req, err := http.NewRequest(http.MethodPost, testServer.URL+"/user", nil) - assert.NoError(t, err) - - query := req.URL.Query() - query.Add("userId", fetchedUserFromSignIn["id"].(string)) - req.URL.RawQuery = query.Encode() - - res, err := http.DefaultClient.Do(req) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, res.StatusCode) - - userByIdResponse := *unittesting.HttpResponseToConsumableInformation(res.Body) - - assert.NotNil(t, userRef) - assert.Equal(t, userByIdResponse["email"], *userRef.Email) - assert.Nil(t, userByIdResponse["phoneNumber"]) - assert.Equal(t, userByIdResponse["id"], userRef.ID) - assert.Equal(t, userByIdResponse["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) - assert.Equal(t, userByIdResponse["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) -} - -func TestOverridingAPIs(t *testing.T) { - var userRef *tplmodels.User - var newUser bool - 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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{customProvider1}, - Override: &tplmodels.OverrideStruct{ - APIs: func(originalImplementation tplmodels.APIInterface) tplmodels.APIInterface { - originalThirdPartySignInUpPost := *originalImplementation.ThirdPartySignInUpPOST - *originalImplementation.ThirdPartySignInUpPOST = func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tplmodels.ThirdPartySignInUpPOSTResponse, error) { - resp, err := originalThirdPartySignInUpPost(provider, input, tenantId, options, userContext) - userRef = &resp.OK.User - newUser = resp.OK.CreatedNewUser - return resp, err - } - return originalImplementation - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - mux := http.NewServeMux() - - mux.HandleFunc("/user", func(rw http.ResponseWriter, r *http.Request) { - userId := r.URL.Query().Get("userId") - fetchedUser, err := GetUserByID(userId) - if err != nil { - t.Error(err.Error()) - } - jsonResp, err := json.Marshal(fetchedUser) - if err != nil { - t.Errorf("Error happened in JSON marshal. Err: %s", err) - } - rw.WriteHeader(200) - rw.Write(jsonResp) - }) - - defer gock.OffAll() - gock.New("https://test.com"). - Post("/oauth/token"). - Persist(). - Reply(200). - JSON(map[string]string{}) - - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - formFields := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": testServer.URL + "/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(formFields) - if err != nil { - t.Error(err.Error()) - } - - gock.New(testServer.URL).EnableNetworking().Persist() - gock.New("http://localhost:8080/").EnableNetworking().Persist() - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - signUpResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) - fetchedUser := signUpResponse["user"].(map[string]interface{}) - - assert.NotNil(t, userRef) - assert.True(t, newUser) - assert.Equal(t, fetchedUser["email"], *userRef.Email) - assert.Equal(t, fetchedUser["id"], userRef.ID) - assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) - assert.Equal(t, fetchedUser["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) - - userRef = nil - assert.Nil(t, userRef) - - formFields = map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": testServer.URL + "/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err = json.Marshal(formFields) - if err != nil { - t.Error(err.Error()) - } - - resp, err = http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - signInResponse := *unittesting.HttpResponseToConsumableInformation(resp.Body) - fetchedUserFromSignIn := signInResponse["user"].(map[string]interface{}) - - assert.NotNil(t, userRef) - assert.False(t, newUser) - assert.Equal(t, fetchedUserFromSignIn["email"], *userRef.Email) - assert.Equal(t, fetchedUserFromSignIn["id"], userRef.ID) - assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["id"], userRef.ThirdParty.ID) - assert.Equal(t, fetchedUserFromSignIn["thirdParty"].(map[string]interface{})["userId"], userRef.ThirdParty.UserID) -} diff --git a/recipe/thirdpartypasswordless/provider_test.go b/recipe/thirdpartypasswordless/provider_test.go deleted file mode 100644 index 57ab59c0..00000000 --- a/recipe/thirdpartypasswordless/provider_test.go +++ /dev/null @@ -1,1207 +0,0 @@ -/* - * Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package thirdpartypasswordless - -import ( - "io/ioutil" - "net/http" - "net/url" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" - "gopkg.in/h2non/gock.v1" -) - -const privateKey = "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----" - -func TestForThirdPartyPasswordlessTheMinimumConfigForThirdPartyProviderGoogle(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "google", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "google", providerInfo.ID) - - assert.Equal(t, "https://accounts.google.com/o/oauth2/v2/auth", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://oauth2.googleapis.com/token", providerInfo.Config.TokenEndpoint) - assert.Equal(t, "https://openidconnect.googleapis.com/v1/userinfo", providerInfo.Config.UserInfoEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "access_type": {"offline"}, - "include_granted_scopes": {"true"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"openid email"}, - }, authParams) - - tokenParams := url.Values{} - - defer gock.OffAll() - gock.New("https://oauth2.googleapis.com"). - Post("/token"). - Persist(). - Map(func(r *http.Request) *http.Request { - data, err := ioutil.ReadAll(r.Body) - assert.NoError(t, err) - tokenParams, err = url.ParseQuery(string(data)) - assert.NoError(t, err) - return r - }). - Reply(200). - JSON(map[string]string{ - "access_token": "abcd", - }) - - _, err = providerInfo.ExchangeAuthCodeForOAuthTokens(tpmodels.TypeRedirectURIInfo{ - RedirectURIOnProviderDashboard: "redirect", - RedirectURIQueryParams: map[string]interface{}{ - "code": "abcd", - }, - }, &map[string]interface{}{}) - assert.NoError(t, err) - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "client_secret": {"test-secret"}, - "grant_type": {"authorization_code"}, - "code": {"abcd"}, - "redirect_uri": {"redirect"}, - }, tokenParams) -} - -func TestWithThirdPartyPasswordlessPassingAdditionalParamsCheckTheyArePresentInAuthorizationUrlForThirdPartyProviderGoogle(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - AuthorizationEndpointQueryParams: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "google", nil) - assert.NoError(t, err) - - providerInfo := providerRes - assert.Equal(t, "google", providerInfo.ID) - - assert.Equal(t, "https://oauth2.googleapis.com/token", providerInfo.Config.TokenEndpoint) - assert.Equal(t, "https://accounts.google.com/o/oauth2/v2/auth", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://openidconnect.googleapis.com/v1/userinfo", providerInfo.Config.UserInfoEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "access_type": {"offline"}, - "include_granted_scopes": {"true"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"openid email"}, - "key1": {"value1"}, - "key2": {"value2"}, - }, authParams) -} - -func TestForThirdpartyPasswordlessPassingScopesInConfigForThirdpartyProviderGoogle(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - Scope: []string{"test-scope-1", "test-scope-2"}, - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "google", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "google", providerInfo.ID) - - assert.Equal(t, "https://accounts.google.com/o/oauth2/v2/auth", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://oauth2.googleapis.com/token", providerInfo.Config.TokenEndpoint) - assert.Equal(t, "https://openidconnect.googleapis.com/v1/userinfo", providerInfo.Config.UserInfoEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "access_type": {"offline"}, - "include_granted_scopes": {"true"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"test-scope-1 test-scope-2"}, - }, authParams) -} - -func TestForThirdPartyPasswordlessMinimumConfigForThirdPartyProviderFacebook(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "facebook", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "facebook", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "facebook", providerInfo.ID) - - assert.Equal(t, "https://graph.facebook.com/v12.0/oauth/access_token", providerInfo.Config.TokenEndpoint) - assert.Equal(t, "https://www.facebook.com/v12.0/dialog/oauth", providerInfo.Config.AuthorizationEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"email"}, - }, authParams) - - tokenParams := url.Values{} - - defer gock.OffAll() - gock.New("https://graph.facebook.com"). - Post("/v12.0/oauth/access_token"). - Persist(). - Map(func(r *http.Request) *http.Request { - data, err := ioutil.ReadAll(r.Body) - assert.NoError(t, err) - tokenParams, err = url.ParseQuery(string(data)) - assert.NoError(t, err) - return r - }). - Reply(200). - JSON(map[string]string{ - "access_token": "abcd", - }) - - _, err = providerInfo.ExchangeAuthCodeForOAuthTokens(tpmodels.TypeRedirectURIInfo{ - RedirectURIOnProviderDashboard: "redirect", - RedirectURIQueryParams: map[string]interface{}{ - "code": "abcd", - }, - }, &map[string]interface{}{}) - assert.NoError(t, err) - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "client_secret": {"test-secret"}, - "grant_type": {"authorization_code"}, - "code": {"abcd"}, - "redirect_uri": {"redirect"}, - }, tokenParams) -} - -func TestWithThirdPartyPasswordlessPassingScopesInConfigForThirdPartyProviderFacebook(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "facebook", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - Scope: []string{"test-scope-1", "test-scope-2"}, - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "facebook", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "facebook", providerInfo.ID) - - assert.Equal(t, "https://graph.facebook.com/v12.0/oauth/access_token", providerInfo.Config.TokenEndpoint) - assert.Equal(t, "https://www.facebook.com/v12.0/dialog/oauth", providerInfo.Config.AuthorizationEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"test-scope-1 test-scope-2"}, - }, authParams) -} - -func TestWithThirdPartyPasswordlessMinimumConfigForThirdPartyProviderGithub(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "github", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "github", providerInfo.ID) - - assert.Equal(t, "https://github.com/login/oauth/authorize", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://github.com/login/oauth/access_token", providerInfo.Config.TokenEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"read:user user:email"}, - }, authParams) - - tokenParams := url.Values{} - - defer gock.OffAll() - gock.New("https://github.com"). - Post("/login/oauth/access_token"). - Persist(). - Map(func(r *http.Request) *http.Request { - data, err := ioutil.ReadAll(r.Body) - assert.NoError(t, err) - tokenParams, err = url.ParseQuery(string(data)) - assert.NoError(t, err) - return r - }). - Reply(200). - JSON(map[string]string{ - "access_token": "abcd", - }) - - _, err = providerInfo.ExchangeAuthCodeForOAuthTokens(tpmodels.TypeRedirectURIInfo{ - RedirectURIOnProviderDashboard: "redirect", - RedirectURIQueryParams: map[string]interface{}{ - "code": "abcd", - }, - }, &map[string]interface{}{}) - assert.NoError(t, err) - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "client_secret": {"test-secret"}, - "grant_type": {"authorization_code"}, - "code": {"abcd"}, - "redirect_uri": {"redirect"}, - }, tokenParams) -} - -func TestWithThirdPartyPasswordlessParamCheckTheyArePresentInAuthorizationURLForThirdPartyProviderGithub(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - AuthorizationEndpointQueryParams: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "github", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "github", providerInfo.ID) - - assert.Equal(t, "https://github.com/login/oauth/authorize", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://github.com/login/oauth/access_token", providerInfo.Config.TokenEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"read:user user:email"}, - "key1": {"value1"}, - "key2": {"value2"}, - }, authParams) -} - -func TestWithThirdPartyPasswordlessPassingScopesInConfigForThirdPartyProviderGithub(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - Scope: []string{"test-scope-1", "test-scope-2"}, - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "github", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "github", providerInfo.ID) - - assert.Equal(t, "https://github.com/login/oauth/authorize", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://github.com/login/oauth/access_token", providerInfo.Config.TokenEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"test-scope-1 test-scope-2"}, - }, authParams) -} - -func TestWithThirdPartyPasswordlessMinimumConfigForThirdPartyProviderApple(t *testing.T) { - clientId := "test" - - 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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: clientId, - AdditionalConfig: map[string]interface{}{ - "keyId": "test-key", - "privateKey": privateKey, - "teamId": "test-team-id", - }, - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "apple", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "apple", providerInfo.ID) - - assert.Equal(t, "https://appleid.apple.com/auth/authorize", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://appleid.apple.com/auth/token", providerInfo.Config.TokenEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_mode": {"form_post"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"openid email"}, - }, authParams) - - tokenParams := url.Values{} - - defer gock.OffAll() - gock.New("https://appleid.apple.com"). - Post("/auth/token"). - Persist(). - Map(func(r *http.Request) *http.Request { - data, err := ioutil.ReadAll(r.Body) - assert.NoError(t, err) - tokenParams, err = url.ParseQuery(string(data)) - assert.NoError(t, err) - return r - }). - Reply(200). - JSON(map[string]string{ - "id_token": "abcd", - }) - - _, err = providerInfo.ExchangeAuthCodeForOAuthTokens(tpmodels.TypeRedirectURIInfo{ - RedirectURIOnProviderDashboard: "redirect", - RedirectURIQueryParams: map[string]interface{}{ - "code": "abcd", - }, - }, &map[string]interface{}{}) - assert.NoError(t, err) - - assert.NotEmpty(t, tokenParams.Get("client_secret")) - tokenParams.Del("client_secret") - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "grant_type": {"authorization_code"}, - "code": {"abcd"}, - "redirect_uri": {"redirect"}, - }, tokenParams) -} - -func TestWithThirdPartyPasswordlessPassingAdditionalParamsCheckTheyArePresentInAuthorizationURLForThirdPartyProviderApple(t *testing.T) { - clientId := "test" - - 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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: clientId, - AdditionalConfig: map[string]interface{}{ - "keyId": "test-key", - "privateKey": privateKey, - "teamId": "test-team-id", - }, - }, - }, - AuthorizationEndpointQueryParams: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "apple", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "apple", providerInfo.ID) - - assert.Equal(t, "https://appleid.apple.com/auth/authorize", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://appleid.apple.com/auth/token", providerInfo.Config.TokenEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_mode": {"form_post"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"openid email"}, - "key1": {"value1"}, - "key2": {"value2"}, - }, authParams) -} - -func TestWithThirdPartyProviderPasswordlessPassingScopesInConfigForThirdPartyProviderApple(t *testing.T) { - clientId := "test" - - 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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: clientId, - Scope: []string{"test-scope-1", "test-scope-2"}, - AdditionalConfig: map[string]interface{}{ - "keyId": "test-key", - "privateKey": privateKey, - "teamId": "test-team-id", - }, - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - q, err := supertokens.GetNewQuerierInstanceOrThrowError("") - if err != nil { - t.Error(err.Error()) - } - apiV, err := q.GetQuerierAPIVersion() - if err != nil { - t.Error(err.Error()) - } - - if unittesting.MaxVersion(apiV, "2.11") == "2.11" { - return - } - - providerRes, err := ThirdPartyGetProvider("public", "apple", nil) - assert.NoError(t, err) - - providerInfo := providerRes - - assert.Equal(t, "apple", providerInfo.ID) - - assert.Equal(t, "https://appleid.apple.com/auth/authorize", providerInfo.Config.AuthorizationEndpoint) - assert.Equal(t, "https://appleid.apple.com/auth/token", providerInfo.Config.TokenEndpoint) - - authUrlRes, err := providerInfo.GetAuthorisationRedirectURL("redirect", &map[string]interface{}{}) - assert.NoError(t, err) - - urlObj, err := url.Parse(authUrlRes.URLWithQueryParams) - assert.NoError(t, err) - - authParams := urlObj.Query() - - assert.Equal(t, url.Values{ - "client_id": {"test"}, - "response_mode": {"form_post"}, - "response_type": {"code"}, - "redirect_uri": {"redirect"}, - "scope": {"test-scope-1 test-scope-2"}, - }, authParams) -} - -func TestWithThirdPartyPasswordlessDuplicateProvider(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{ - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - assert.Equal(t, "The providers array has multiple entries for the same third party provider.", err.Error()) - } -}