diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c14a44a..975801cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,133 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.20.0] - 2024-05-23 + +### Breaking change + +- Removed ThirdPartyEmailPassword and ThirdPartyPasswordless recipes. Instead, you should use ThirdParty + EmailPassword or ThirdParty + Passwordless recipes separately in your recipe list. +- Removed `rid` query param from: + - email verification links + - passwordless magic links + - password reset links + +### Changes + +- If `rid` header is present in an API call, the routing no only only depends on that. If the SDK cannot resolve a request handler based on the `rid`, request path and method, it will try to resolve a request handler only based on the request path and method (therefore ignoring the `rid` header). +- New API handlers are: + - `GET /emailpassword/email/exists` => email password, does email exist API (used to be `GET /signup/email/exists` with `rid` of `emailpassword` or `thirdpartyemailpassword` which is now deprecated) + - `GET /passwordless/email/exists` => email password, does email exist API (used to be `GET /signup/email/exists` with `rid` of `passwordless` or `thirdpartypasswordless` which is now deprecated) + - `GET /passwordless/phonenumber/exists` => email password, does email exist API (used to be `GET /signup/phonenumber/exists` which is now deprecated) +- Support for FDI 2.0 + +### Migration guide + +- If you were using `ThirdPartyEmailPassword`, you should now init `ThirdParty` and `EmailPassword` recipes separately. The config for the individual recipes are mostly the same, except the syntax may be different. Check our recipe guides for [ThirdParty](https://supertokens.com/docs/thirdparty/introduction) and [EmailPassword](https://supertokens.com/docs/emailpassword/introduction) for more information. + +- If you were using `ThirdPartyPasswordless`, you should now init `ThirdParty` and `Passwordless` recipes separately. The config for the individual recipes are mostly the same, except the syntax may be different. Check our recipe guides for [ThirdParty](https://supertokens.com/docs/thirdparty/introduction) and [Passwordless](https://supertokens.com/docs/passwordless/introduction) for more information. + +- The way to get user information has changed: + - If you are using `thirdpartyemailpassword.GetUsersByEmail`: + + Before: + ```go + userInfo, err := thirdpartyemailpassword.GetUsersByEmail("public", "test@example.com") + ``` + + After: + ```go + thirdPartyUserInfo, err := thirdparty.GetUsersByEmail("public", "test@example.com") + if err != nil { + // TODO: Handle error + } + + emailPasswordUserInfo, err := emailpassword.GetUserByEmail("public", "test@example.com") + if err != nil { + // TODO: Handle error + } + + if emailPasswordUserInfo != nil { + fmt.Println(emailPasswordUserInfo) + } + if len(thirdPartyUserInfo) > 0 { + fmt.Println(thirdPartyUserInfo) + } + ``` + + - If you are using `thirdpartyemailpassword.GetUserById`: + + Before: + ```go + userInfo, err := thirdpartyemailpassword.GetUserById(userID) + ``` + + After: + ```go + userInfo, err := thirdparty.GetUserByID(userID) + if err != nil { + // TODO: Handle error + } + if userInfo == nil { + emailPasswordUserInfo, err := emailpassword.GetUserByID(userID) + if err != nil { + // TODO: Handle error + } + fmt.Println(emailPasswordUserInfo) + } else { + fmt.Println(userInfo) + } + ``` + - If you are using `thirdpartypasswordless.GetUsersByEmail`: + + Before: + ```go + userInfo, err := thirdpartypasswordless.GetUsersByEmail("public", "test@example.com") + ``` + + After: + ```go + thirdPartyUserInfo, err := thirdparty.GetUsersByEmail("public", "test@example.com") + if err != nil { + return + } + + passwordlessUserInfo, err := passwordless.GetUserByEmail("public", "test@example.com") + if err != nil { + return + } + + if passwordlessUserInfo != nil { + fmt.Println(passwordlessUserInfo) + } + if len(thirdPartyUserInfo) > 0 { + fmt.Println(thirdPartyUserInfo) + } + ``` + + - If you are using `thirdpartypasswordless.GetUserById`: + + Before: + ```go + userInfo, err := thirdpartypasswordless.GetUserById(userID) + ``` + + After: + ```go + userInfo, err := thirdparty.GetUserByID(userID) + if err != nil { + // TODO: Handle error + } + if userInfo == nil { + passwordlessUserInfo, err := passwordless.GetUserByID(userID) + if err != nil { + // TODO: Handle error + } + fmt.Println(passwordlessUserInfo) + } else { + fmt.Println(userInfo) + } + ``` + ## [0.19.0] - 2024-05-01 - Added `OlderCookieDomain` config option in the session recipe. This will allow users to clear cookies from the older domain when the `CookieDomain` is changed. diff --git a/examples/with-chi-oso/main.go b/examples/with-chi-oso/main.go index 36509060..a3b06cf3 100644 --- a/examples/with-chi-oso/main.go +++ b/examples/with-chi-oso/main.go @@ -12,9 +12,8 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -37,111 +36,113 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/examples/with-chi-oso/service/service.go b/examples/with-chi-oso/service/service.go index a8c2e8ec..3f752e8f 100644 --- a/examples/with-chi-oso/service/service.go +++ b/examples/with-chi-oso/service/service.go @@ -9,7 +9,7 @@ import ( "github.com/osohq/go-oso" "github.com/supertokens/supertokens-golang/examples/with-chi-oso/models" "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -92,7 +92,7 @@ func (s *service) Repo(w http.ResponseWriter, r *http.Request) { w.Write([]byte(err.Error())) return } - userByID, err := thirdpartyemailpassword.GetUserById(sessionContainer.GetUserID()) + userByID, err := thirdparty.GetUserByID(sessionContainer.GetUserID()) if err != nil { w.WriteHeader(500) w.Write([]byte(err.Error())) diff --git a/examples/with-chi/main.go b/examples/with-chi/main.go index fd2f1c4b..8065f747 100644 --- a/examples/with-chi/main.go +++ b/examples/with-chi/main.go @@ -7,12 +7,12 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/cors" "github.com/supertokens/supertokens-golang/recipe/dashboard" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -30,7 +30,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -39,116 +39,119 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, }, }, }), + emailpassword.Init(nil), session.Init(nil), dashboard.Init(nil), }, diff --git a/examples/with-fiber/main.go b/examples/with-fiber/main.go index 2a2b1f9a..72f6f437 100644 --- a/examples/with-fiber/main.go +++ b/examples/with-fiber/main.go @@ -13,9 +13,8 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -33,7 +32,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -41,110 +40,112 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/examples/with-gin/config/config.go b/examples/with-gin/config/config.go index 8722e0c9..f8dfcd4d 100644 --- a/examples/with-gin/config/config.go +++ b/examples/with-gin/config/config.go @@ -1,16 +1,17 @@ package config import ( - "github.com/supertokens/supertokens-golang/recipe/dashboard" "log" + "github.com/supertokens/supertokens-golang/recipe/dashboard" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" + "github.com/spf13/viper" "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -49,9 +50,12 @@ func Init() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ - Providers: providers, + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: providers, + }, }), + emailpassword.Init(nil), session.Init(nil), dashboard.Init(nil), // thirdparty.Init(thirdpartyConfig), diff --git a/examples/with-go-zero/main.go b/examples/with-go-zero/main.go index 0d0b4113..8f8c0aee 100644 --- a/examples/with-go-zero/main.go +++ b/examples/with-go-zero/main.go @@ -2,17 +2,17 @@ package main import ( "encoding/json" - "github.com/supertokens/supertokens-golang/recipe/dashboard" "log" "net/http" "strings" + "github.com/supertokens/supertokens-golang/recipe/dashboard" + "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/zeromicro/go-zero/rest" ) @@ -31,7 +31,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -39,110 +39,112 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/examples/with-http/main.go b/examples/with-http/main.go index 6a5e40d1..7b513221 100644 --- a/examples/with-http/main.go +++ b/examples/with-http/main.go @@ -2,16 +2,16 @@ package main import ( "encoding/json" - "github.com/supertokens/supertokens-golang/recipe/dashboard" "net/http" "strings" + "github.com/supertokens/supertokens-golang/recipe/dashboard" + "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -29,7 +29,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -38,110 +38,112 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/examples/with-labstack-echo/main.go b/examples/with-labstack-echo/main.go index ed9c5c42..50554636 100644 --- a/examples/with-labstack-echo/main.go +++ b/examples/with-labstack-echo/main.go @@ -13,9 +13,8 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -33,7 +32,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -42,110 +41,112 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/examples/with-mux/main.go b/examples/with-mux/main.go index c388e022..b79fee89 100644 --- a/examples/with-mux/main.go +++ b/examples/with-mux/main.go @@ -2,17 +2,17 @@ package main import ( "encoding/json" - "github.com/supertokens/supertokens-golang/recipe/dashboard" "net/http" + "github.com/supertokens/supertokens-golang/recipe/dashboard" + "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -30,7 +30,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -39,110 +39,112 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/examples/with-twirp/cmd/server/main.go b/examples/with-twirp/cmd/server/main.go index 0c27b65a..8f59037a 100644 --- a/examples/with-twirp/cmd/server/main.go +++ b/examples/with-twirp/cmd/server/main.go @@ -14,11 +14,12 @@ package main import ( - "github.com/supertokens/supertokens-golang/recipe/dashboard" "log" "net/http" "os" + "github.com/supertokens/supertokens-golang/recipe/dashboard" + "github.com/gorilla/handlers" "github.com/supertokens/supertokens-golang/examples/with-twirp/haberdasher" "github.com/supertokens/supertokens-golang/examples/with-twirp/internal/haberdasherserver" @@ -28,9 +29,8 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/twitchtv/twirp" ) @@ -49,7 +49,7 @@ func main() { emailverification.Init(evmodels.TypeInput{ Mode: evmodels.ModeRequired, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ /* We use different credentials for different platforms when required. For example the redirect URI for Github is different for Web and mobile. In such a case we can provide multiple providers with different client Ids. @@ -58,110 +58,112 @@ func main() { request. In the absence of a clientId in the request the SDK uses the default provider, indicated by `isDefault: true`. When adding multiple providers for the same type (Google, Github etc), make sure to set `isDefault: true`. */ - Providers: []tpmodels.ProviderInput{ - // We have provided you with development keys which you can use for testsing. - // IMPORTANT: Please replace them with your own OAuth keys for production use. - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // we use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", - ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + // We have provided you with development keys which you can use for testsing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // we use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-c7mgk8p0h27c4428prfuo3lg7ould5o7.apps.googleusercontent.com", + ClientSecret: "", // this is empty because we follow Authorization code grant flow via PKCE for mobile apps (Google doesn't issue a client secret for mobile apps). + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "467101b197249757c71f", - ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "8a9152860ce869b64c44", - ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "github", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "467101b197249757c71f", + ClientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "8a9152860ce869b64c44", + ClientSecret: "00e841f10f288363cd3786b1b1f538f05cfdbda2", + }, }, }, }, - }, - /* - For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms - need to configure a Service ID on the Apple developer dashboard and use that as client ID. - In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS - the frontend for the demo app sends the clientId in the request which is then used by the SDK. - */ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "apple", - Clients: []tpmodels.ProviderClientConfig{ - { - // For Android and website apps - ClientType: "web", - ClientID: "4398792-io.supertokens.example.service", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + /* + For Apple signin, iOS apps always use the bundle identifier as the client ID when communicating with Apple. Android, Web and other platforms + need to configure a Service ID on the Apple developer dashboard and use that as client ID. + In the example below 4398792-io.supertokens.example.service is the client ID for Web. Android etc and thus we mark it as default. For iOS + the frontend for the demo app sends the clientId in the request which is then used by the SDK. + */ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "apple", + Clients: []tpmodels.ProviderClientConfig{ + { + // For Android and website apps + ClientType: "web", + ClientID: "4398792-io.supertokens.example.service", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, - }, - { - // For iOS Apps - ClientType: "ios", - ClientID: "4398792-io.supertokens.example", - AdditionalConfig: map[string]interface{}{ - "keyId": "7M48Y4RYDL", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", - "teamId": "YWQCXGJRJL", + { + // For iOS Apps + ClientType: "ios", + ClientID: "4398792-io.supertokens.example", + AdditionalConfig: map[string]interface{}{ + "keyId": "7M48Y4RYDL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + "teamId": "YWQCXGJRJL", + }, }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "discord", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "4398792-907871294886928395", - ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "discord", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "4398792-907871294886928395", + ClientSecret: "His4yXGEovVp5TZkZhEAt0ZXGh8uOVDm", + }, }, }, }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google-workspaces", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientType: "web", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", - }, - { - // We use this for mobile apps - ClientType: "mobile", - ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", - ClientSecret: "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", + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google-workspaces", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientType: "web", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, + { + // We use this for mobile apps + ClientType: "mobile", + ClientID: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + ClientSecret: "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", + }, }, }, }, diff --git a/frontendDriverInterfaceSupported.json b/frontendDriverInterfaceSupported.json index 0d1267d8..8ca4e370 100644 --- a/frontendDriverInterfaceSupported.json +++ b/frontendDriverInterfaceSupported.json @@ -1,6 +1,7 @@ { "_comment": "contains a list of frontend-driver interfaces branch names that this core supports", "versions": [ - "1.17" + "1.17", + "2.0" ] } \ No newline at end of file diff --git a/recipe/dashboard/api/userdetails/userPasswordPut.go b/recipe/dashboard/api/userdetails/userPasswordPut.go index 672e195b..f66460c1 100644 --- a/recipe/dashboard/api/userdetails/userPasswordPut.go +++ b/recipe/dashboard/api/userdetails/userPasswordPut.go @@ -22,7 +22,6 @@ import ( "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -70,65 +69,13 @@ func UserPasswordPut(apiInterface dashboardmodels.APIInterface, tenantId string, } if recipeToUse == "none" { - tpepInstance := thirdpartyemailpassword.GetRecipeInstance() - - if tpepInstance != nil { - recipeToUse = "thirdpartyemailpassword" - } - } - - if recipeToUse == "none" { - // This means that neither emailpassword or thirdpartyemailpassword is initialised + // This means that emailpassword is not init return userPasswordPutResponse{}, errors.New("Should never come here") } - if recipeToUse == "emailpassword" { - var passwordField epmodels.NormalisedFormField - - for _, value := range emailPasswordInstance.Config.SignUpFeature.FormFields { - if value.ID == "password" { - passwordField = value - } - } - - validationError := passwordField.Validate(*readBody.NewPassword, tenantId) - - if validationError != nil { - return userPasswordPutResponse{ - Status: "INVALID_PASSWORD_ERROR", - Error: *validationError, - }, nil - } - - passwordResetToken, resetTokenErr := emailpassword.CreateResetPasswordToken(tenantId, *readBody.UserId, userContext) - - if resetTokenErr != nil { - return userPasswordPutResponse{}, resetTokenErr - } - - if passwordResetToken.UnknownUserIdError != nil { - // Techincally it can but its an edge case so we assume that it wont - return userPasswordPutResponse{}, errors.New("Should never come here") - } - - passwordResetResponse, passwordResetErr := emailpassword.ResetPasswordUsingToken(tenantId, passwordResetToken.OK.Token, *readBody.NewPassword, userContext) - - if passwordResetErr != nil { - return userPasswordPutResponse{}, passwordResetErr - } - - if passwordResetResponse.ResetPasswordInvalidTokenError != nil { - return userPasswordPutResponse{}, errors.New("Should never come here") - } - - return userPasswordPutResponse{ - Status: "OK", - }, nil - } - var passwordField epmodels.NormalisedFormField - for _, value := range thirdpartyemailpassword.GetRecipeInstance().GetEmailPasswordRecipe().Config.SignUpFeature.FormFields { + for _, value := range emailPasswordInstance.Config.SignUpFeature.FormFields { if value.ID == "password" { passwordField = value } @@ -143,7 +90,7 @@ func UserPasswordPut(apiInterface dashboardmodels.APIInterface, tenantId string, }, nil } - passwordResetToken, resetTokenErr := thirdpartyemailpassword.CreateResetPasswordToken(tenantId, *readBody.UserId, userContext) + passwordResetToken, resetTokenErr := emailpassword.CreateResetPasswordToken(tenantId, *readBody.UserId, userContext) if resetTokenErr != nil { return userPasswordPutResponse{}, resetTokenErr @@ -154,7 +101,7 @@ func UserPasswordPut(apiInterface dashboardmodels.APIInterface, tenantId string, return userPasswordPutResponse{}, errors.New("Should never come here") } - passwordResetResponse, passwordResetErr := thirdpartyemailpassword.ResetPasswordUsingToken(tenantId, passwordResetToken.OK.Token, *readBody.NewPassword, userContext) + passwordResetResponse, passwordResetErr := emailpassword.ResetPasswordUsingToken(tenantId, passwordResetToken.OK.Token, *readBody.NewPassword, userContext) if passwordResetErr != nil { return userPasswordPutResponse{}, passwordResetErr diff --git a/recipe/dashboard/api/userdetails/userPut.go b/recipe/dashboard/api/userdetails/userPut.go index c778f05d..431461bc 100644 --- a/recipe/dashboard/api/userdetails/userPut.go +++ b/recipe/dashboard/api/userdetails/userPut.go @@ -25,8 +25,6 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" "github.com/supertokens/supertokens-golang/recipe/passwordless" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless" "github.com/supertokens/supertokens-golang/recipe/usermetadata" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -95,46 +93,6 @@ func updateEmailForRecipeId(recipeId string, userId string, email string, tenant }, nil } - if recipeId == "thirdpartyemailpassword" { - var emailField epmodels.NormalisedFormField - - for _, value := range thirdpartyemailpassword.GetRecipeInstance().GetEmailPasswordRecipe().Config.SignUpFeature.FormFields { - if value.ID == "email" { - emailField = value - } - } - - validationError := emailField.Validate(email, tenantId) - - if validationError != nil { - return updateEmailResponse{ - Status: "INVALID_EMAIL_ERROR", - Error: *validationError, - }, nil - } - - tenantId := "public" - updateResponse, err := thirdpartyemailpassword.UpdateEmailOrPassword(userId, &email, nil, nil, &tenantId, userContext) - - if err != nil { - return updateEmailResponse{}, err - } - - if updateResponse.EmailAlreadyExistsError != nil { - return updateEmailResponse{ - Status: "EMAIL_ALREADY_EXISTS_ERROR", - }, nil - } - - if updateResponse.UnknownUserIdError != nil { - return updateEmailResponse{}, errors.New("Should never come here") - } - - return updateEmailResponse{ - Status: "OK", - }, nil - } - if recipeId == "passwordless" { isValidEmail := true validationError := "" @@ -192,63 +150,6 @@ func updateEmailForRecipeId(recipeId string, userId string, email string, tenant }, nil } - if recipeId == "thirdpartypasswordless" { - isValidEmail := true - validationError := "" - - passwordlessConfig := thirdpartypasswordless.GetRecipeInstance().Config - - if passwordlessConfig.ContactMethodPhone.Enabled { - validationResult := passwordless.DefaultValidateEmailAddress(email, tenantId) - - if validationResult != nil { - isValidEmail = false - validationError = *validationResult - } - } else if passwordlessConfig.ContactMethodEmail.Enabled { - validationResult := passwordlessConfig.ContactMethodEmail.ValidateEmailAddress(email, tenantId) - - if validationResult != nil { - isValidEmail = false - validationError = *validationResult - } - } else { - validationResult := passwordlessConfig.ContactMethodEmailOrPhone.ValidateEmailAddress(email, tenantId) - - if validationResult != nil { - isValidEmail = false - validationError = *validationResult - } - } - - if !isValidEmail { - return updateEmailResponse{ - Status: "INVALID_EMAIL_ERROR", - Error: validationError, - }, nil - } - - updateResponse, updateErr := thirdpartypasswordless.UpdatePasswordlessUser(userId, &email, nil, userContext) - - if updateErr != nil { - return updateEmailResponse{}, updateErr - } - - if updateResponse.UnknownUserIdError != nil { - return updateEmailResponse{}, errors.New("Should never come here") - } - - if updateResponse.EmailAlreadyExistsError != nil { - return updateEmailResponse{ - Status: "EMAIL_ALREADY_EXISTS_ERROR", - }, nil - } - - return updateEmailResponse{ - Status: "OK", - }, nil - } - return updateEmailResponse{}, errors.New("Should never come here") } @@ -310,63 +211,6 @@ func updatePhoneForRecipeId(recipeId string, userId string, phone string, tenant }, nil } - if recipeId == "thirdpartypasswordless" { - isValidPhone := true - validationError := "" - - passwordlessConfig := thirdpartypasswordless.GetRecipeInstance().Config - - if passwordlessConfig.ContactMethodEmail.Enabled { - validationResult := passwordless.DefaultValidatePhoneNumber(phone, tenantId) - - if validationResult != nil { - isValidPhone = false - validationError = *validationResult - } - } else if passwordlessConfig.ContactMethodPhone.Enabled { - validationResult := passwordlessConfig.ContactMethodPhone.ValidatePhoneNumber(phone, tenantId) - - if validationResult != nil { - isValidPhone = false - validationError = *validationResult - } - } else { - validationResult := passwordlessConfig.ContactMethodEmailOrPhone.ValidatePhoneNumber(phone, tenantId) - - if validationResult != nil { - isValidPhone = false - validationError = *validationResult - } - } - - if !isValidPhone { - return updatePhoneResponse{ - Status: "INVALID_PHONE_ERROR", - Error: validationError, - }, nil - } - - updateResponse, updateErr := thirdpartypasswordless.UpdatePasswordlessUser(userId, nil, &phone, userContext) - - if updateErr != nil { - return updatePhoneResponse{}, updateErr - } - - if updateResponse.UnknownUserIdError != nil { - return updatePhoneResponse{}, errors.New("Should never come here") - } - - if updateResponse.EmailAlreadyExistsError != nil { - return updatePhoneResponse{ - Status: "PHONE_ALREADY_EXISTS_ERROR", - }, nil - } - - return updatePhoneResponse{ - Status: "OK", - }, nil - } - /** * If it comes here then the user is a not a passwordless user in which case the UI should not have allowed this */ diff --git a/recipe/dashboard/api/utils.go b/recipe/dashboard/api/utils.go index 5134d1f3..d20846ca 100644 --- a/recipe/dashboard/api/utils.go +++ b/recipe/dashboard/api/utils.go @@ -1,14 +1,10 @@ package api import ( - "reflect" - "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/recipe/passwordless" "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless" "github.com/supertokens/supertokens-golang/supertokens" ) @@ -19,12 +15,10 @@ func IsValidRecipeId(recipeId string) bool { /* This function tries to fetch a user for the given user id and recipe id. The input recipe id should be one of the primary recipes (emailpassword, thirdparty, passwordless) but the returned -recipe will be the exact recipe that matched for the user (including thirdpartyemailpassword and -thirdpartypasswordless). +recipe will be the exact recipe that matched for the user When fetching a user we need to check for multiple recipes per input recipe id, for example a user -created using email and password could be present for the EmailPassword recipe and the ThirdPartyEmailPassword -recipe so we need to check for both. +created using email and password could be present for the EmailPassword recipe so we need to check for both. If this function returns an empty user struct, it should be treated as if the user does not exist */ @@ -45,24 +39,8 @@ func GetUserForRecipeId(userId string, recipeId string, userContext supertokens. recipeToReturn = emailpassword.RECIPE_ID } - - if reflect.DeepEqual(userToReturn, dashboardmodels.UserType{}) { - tpepResponse, tpepError := thirdpartyemailpassword.GetUserById(userId, userContext) - - if tpepError == nil && tpepResponse != nil { - userToReturn.Id = tpepResponse.ID - userToReturn.TimeJoined = tpepResponse.TimeJoined - userToReturn.FirstName = "" - userToReturn.LastName = "" - userToReturn.Email = tpepResponse.Email - userToReturn.TenantIds = tpepResponse.TenantIds - - recipeToReturn = thirdpartyemailpassword.RECIPE_ID - } - } } else if recipeId == thirdparty.RECIPE_ID { response, error := thirdparty.GetUserByID(userId, userContext) - if error == nil && response != nil { userToReturn.Id = response.ID userToReturn.TimeJoined = response.TimeJoined @@ -75,48 +53,6 @@ func GetUserForRecipeId(userId string, recipeId string, userContext supertokens. } userToReturn.TenantIds = response.TenantIds } - - if reflect.DeepEqual(userToReturn, dashboardmodels.UserType{}) { - tpepResponse, tpepError := thirdpartyemailpassword.GetUserById(userId, userContext) - - if tpepError == nil && tpepResponse != nil { - userToReturn.Id = tpepResponse.ID - userToReturn.TimeJoined = tpepResponse.TimeJoined - userToReturn.FirstName = "" - userToReturn.LastName = "" - userToReturn.Email = tpepResponse.Email - userToReturn.ThirdParty = &dashboardmodels.ThirdParty{ - Id: tpepResponse.ThirdParty.ID, - UserId: tpepResponse.ThirdParty.UserID, - } - userToReturn.TenantIds = tpepResponse.TenantIds - } - } - - if reflect.DeepEqual(userToReturn, dashboardmodels.UserType{}) { - tpplessResponse, tpplessError := thirdpartypasswordless.GetUserById(userId, userContext) - - if tpplessError == nil && tpplessResponse != nil { - userToReturn.Id = tpplessResponse.ID - userToReturn.TimeJoined = tpplessResponse.TimeJoined - userToReturn.FirstName = "" - userToReturn.LastName = "" - - if tpplessResponse.Email != nil { - userToReturn.Email = *tpplessResponse.Email - } - - if tpplessResponse.PhoneNumber != nil { - userToReturn.Phone = *tpplessResponse.PhoneNumber - } - - userToReturn.ThirdParty = &dashboardmodels.ThirdParty{ - Id: tpplessResponse.ThirdParty.ID, - UserId: tpplessResponse.ThirdParty.UserID, - } - userToReturn.TenantIds = tpplessResponse.TenantIds - } - } } else if recipeId == passwordless.RECIPE_ID { response, error := passwordless.GetUserByID(userId, userContext) @@ -136,27 +72,6 @@ func GetUserForRecipeId(userId string, recipeId string, userContext supertokens. userToReturn.TenantIds = response.TenantIds } - - if reflect.DeepEqual(userToReturn, dashboardmodels.UserType{}) { - tppResponse, tppError := thirdpartypasswordless.GetUserByID(userId, userContext) - - if tppError == nil && tppResponse != nil { - userToReturn.Id = tppResponse.ID - userToReturn.TimeJoined = tppResponse.TimeJoined - userToReturn.FirstName = "" - userToReturn.LastName = "" - - if tppResponse.Email != nil { - userToReturn.Email = *tppResponse.Email - } - - if tppResponse.PhoneNumber != nil { - userToReturn.Phone = *tppResponse.PhoneNumber - } - - userToReturn.TenantIds = tppResponse.TenantIds - } - } } return userToReturn, recipeToReturn @@ -171,50 +86,18 @@ func IsRecipeInitialised(recipeId string) bool { if err == nil { isRecipeInitialised = true } - - if !isRecipeInitialised { - _, err := thirdpartyemailpassword.GetRecipeInstanceOrThrowError() - - if err == nil { - isRecipeInitialised = true - } - } } else if recipeId == passwordless.RECIPE_ID { _, err := passwordless.GetRecipeInstanceOrThrowError() if err == nil { isRecipeInitialised = true } - - if !isRecipeInitialised { - _, err := thirdpartypasswordless.GetRecipeInstanceOrThrowError() - - if err == nil { - isRecipeInitialised = true - } - } } else if recipeId == thirdparty.RECIPE_ID { _, err := thirdparty.GetRecipeInstanceOrThrowError() if err == nil { isRecipeInitialised = true } - - if !isRecipeInitialised { - _, err := thirdpartyemailpassword.GetRecipeInstanceOrThrowError() - - if err == nil { - isRecipeInitialised = true - } - } - - if !isRecipeInitialised { - _, err := thirdpartypasswordless.GetRecipeInstanceOrThrowError() - - if err == nil { - isRecipeInitialised = true - } - } } return isRecipeInitialised diff --git a/recipe/dashboard/testingUtils.go b/recipe/dashboard/testingUtils.go index 0763f1f9..43e80591 100644 --- a/recipe/dashboard/testingUtils.go +++ b/recipe/dashboard/testingUtils.go @@ -20,7 +20,6 @@ import ( "github.com/supertokens/supertokens-golang/recipe/multitenancy" "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" @@ -30,7 +29,6 @@ func resetAll() { supertokens.ResetForTest() ResetForTest() session.ResetForTest() - thirdpartyemailpassword.ResetForTest() multitenancy.ResetForTest() } diff --git a/recipe/dashboard/userGet_test.go b/recipe/dashboard/userGet_test.go index 8bdd34a2..c50dc010 100644 --- a/recipe/dashboard/userGet_test.go +++ b/recipe/dashboard/userGet_test.go @@ -2,29 +2,28 @@ package dashboard import ( "encoding/json" - "github.com/supertokens/supertokens-golang/recipe/dashboard/api" - "github.com/supertokens/supertokens-golang/recipe/dashboard/api/userdetails" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" "io" "net/http" "net/http/httptest" "strings" "testing" + "github.com/supertokens/supertokens-golang/recipe/dashboard/api" + "github.com/supertokens/supertokens-golang/recipe/dashboard/api/userdetails" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" + "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/thirdparty" + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" + "github.com/stretchr/testify/assert" "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) /* -- Initialise with thirdpartyemailpassword and provide no custom form fields -- Create an emailpassword user using the thirdpartyemailpassword recipe - Get user from the user get API - Check that user has public tenant */ @@ -42,7 +41,7 @@ func TestThatUserGetReturnsTenantIDsCorrectly(t *testing.T) { WebsiteDomain: "supertokens.io", }, RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(nil), + emailpassword.Init(nil), Init(&dashboardmodels.TypeInput{ ApiKey: "testapikey", }), @@ -61,7 +60,7 @@ func TestThatUserGetReturnsTenantIDsCorrectly(t *testing.T) { testServer := httptest.NewServer(supertokens.Middleware(mux)) defer testServer.Close() - signupResponse, err := thirdpartyemailpassword.EmailPasswordSignUp("public", "testing@supertokens.com", "abcd1234") + signupResponse, err := emailpassword.SignUp("public", "testing@supertokens.com", "abcd1234") if err != nil { t.Error(err.Error()) } @@ -86,6 +85,33 @@ func TestThatUserGetReturnsTenantIDsCorrectly(t *testing.T) { assert.Equal(t, response.User.TenantIds[0], "public") } +var customProvider1 = tpmodels.ProviderInput{ + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "custom", + AuthorizationEndpoint: "https://test.com/oauth/auth", + TokenEndpoint: "https://test.com/oauth/token", + + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "supertokens", + }, + }, + }, + + Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { + originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { + return tpmodels.TypeUserInfo{ + ThirdPartyUserId: "user", + Email: &tpmodels.EmailStruct{ + ID: "email@test.com", + IsVerified: true, + }, + }, nil + } + return originalImplementation + }, +} + func TestThatUserGetReturnsValidUserForThirdPartyUserWhenUsingThirdPartyPasswordless(t *testing.T) { config := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ @@ -97,14 +123,18 @@ func TestThatUserGetReturnsValidUserForThirdPartyUserWhenUsingThirdPartyPassword WebsiteDomain: "supertokens.io", }, RecipeList: []supertokens.Recipe{ - thirdpartypasswordless.Init(tplmodels.TypeInput{ + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + customProvider1, + }, + }, + }), + passwordless.Init(plessmodels.TypeInput{ FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ Enabled: true, }, - Providers: []tpmodels.ProviderInput{ - thirdpartypasswordless.SigninupCustomProvider1, - }, }), Init(&dashboardmodels.TypeInput{ ApiKey: "testapikey", diff --git a/recipe/dashboard/userPasswordPut_test.go b/recipe/dashboard/userPasswordPut_test.go index a85421f9..30442361 100644 --- a/recipe/dashboard/userPasswordPut_test.go +++ b/recipe/dashboard/userPasswordPut_test.go @@ -8,14 +8,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) /* -- Initialise with thirdpartyemailpassword and provide no custom form fields -- Create an emailpassword user using the thirdpartyemailpassword recipe - Try to change the password of the user - Should result in no errors - Sign in with new password @@ -35,7 +33,7 @@ func TestThatUpdatingPasswordWithNoSignUpFeatureInTPEPWorks(t *testing.T) { WebsiteDomain: "supertokens.io", }, RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(nil), + emailpassword.Init(nil), Init(&dashboardmodels.TypeInput{ ApiKey: "testapikey", }), @@ -54,7 +52,7 @@ func TestThatUpdatingPasswordWithNoSignUpFeatureInTPEPWorks(t *testing.T) { testServer := httptest.NewServer(supertokens.Middleware(mux)) defer testServer.Close() - signupResponse, err := thirdpartyemailpassword.EmailPasswordSignUp("public", "testing@supertokens.com", "abcd1234") + signupResponse, err := emailpassword.SignUp("public", "testing@supertokens.com", "abcd1234") if err != nil { t.Error(err.Error()) } @@ -78,7 +76,7 @@ func TestThatUpdatingPasswordWithNoSignUpFeatureInTPEPWorks(t *testing.T) { assert.Equal(t, http.StatusOK, res.StatusCode) - signInResponse, err := thirdpartyemailpassword.EmailPasswordSignIn("public", "testing@supertokens.com", "newabcd1234") + signInResponse, err := emailpassword.SignIn("public", "testing@supertokens.com", "newabcd1234") if err != nil { t.Error(err.Error()) } diff --git a/recipe/dashboard/userPut_test.go b/recipe/dashboard/userPut_test.go index de8afb9a..fc15ec8c 100644 --- a/recipe/dashboard/userPut_test.go +++ b/recipe/dashboard/userPut_test.go @@ -8,14 +8,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) /* -- Initialise with thirdpartyemailpassword and provide no custom form fields -- Create an emailpassword user using the thirdpartyemailpassword recipe - Try to change the password of the user - Should result in no errors - Sign in with new password @@ -35,7 +33,7 @@ func TestThatUpdatingEmailWithNoSignUpFeatureInTPEPWorks(t *testing.T) { WebsiteDomain: "supertokens.io", }, RecipeList: []supertokens.Recipe{ - thirdpartyemailpassword.Init(nil), + emailpassword.Init(nil), Init(&dashboardmodels.TypeInput{ ApiKey: "testapikey", }), @@ -54,7 +52,7 @@ func TestThatUpdatingEmailWithNoSignUpFeatureInTPEPWorks(t *testing.T) { testServer := httptest.NewServer(supertokens.Middleware(mux)) defer testServer.Close() - signupResponse, err := thirdpartyemailpassword.EmailPasswordSignUp("public", "testing@supertokens.com", "abcd1234") + signupResponse, err := emailpassword.SignUp("public", "testing@supertokens.com", "abcd1234") if err != nil { t.Error(err.Error()) } @@ -78,7 +76,7 @@ func TestThatUpdatingEmailWithNoSignUpFeatureInTPEPWorks(t *testing.T) { assert.Equal(t, http.StatusOK, res.StatusCode) - signInResponse, err := thirdpartyemailpassword.EmailPasswordSignIn("public", "testing2@supertokens.com", "abcd1234") + signInResponse, err := emailpassword.SignIn("public", "testing2@supertokens.com", "abcd1234") if err != nil { t.Error(err.Error()) diff --git a/recipe/emailpassword/api/implementation.go b/recipe/emailpassword/api/implementation.go index 209fd5dd..09186496 100644 --- a/recipe/emailpassword/api/implementation.go +++ b/recipe/emailpassword/api/implementation.go @@ -68,7 +68,6 @@ func MakeAPIImplementation() epmodels.APIInterface { passwordResetLink, err := GetPasswordResetLink( options.AppInfo, - options.RecipeID, response.OK.Token, tenantId, options.Req, diff --git a/recipe/emailpassword/api/utils.go b/recipe/emailpassword/api/utils.go index fa6db009..a96faf94 100644 --- a/recipe/emailpassword/api/utils.go +++ b/recipe/emailpassword/api/utils.go @@ -126,17 +126,16 @@ func validateFormOrThrowError(configFormFields []epmodels.NormalisedFormField, i return nil } -func GetPasswordResetLink(appInfo supertokens.NormalisedAppinfo, recipeID string, token string, tenantId string, request *http.Request, userContext supertokens.UserContext) (string, error) { +func GetPasswordResetLink(appInfo supertokens.NormalisedAppinfo, token string, tenantId string, request *http.Request, userContext supertokens.UserContext) (string, error) { websiteDomain, err := appInfo.GetOrigin(request, userContext) if err != nil { return "", err } return fmt.Sprintf( - "%s%s/reset-password?token=%s&rid=%s&tenantId=%s", + "%s%s/reset-password?token=%s&tenantId=%s", websiteDomain.GetAsStringDangerous(), appInfo.WebsiteBasePath.GetAsStringDangerous(), token, - recipeID, tenantId, ), nil } diff --git a/recipe/emailpassword/authFlow_test.go b/recipe/emailpassword/authFlow_test.go index 42215638..b6eb6186 100644 --- a/recipe/emailpassword/authFlow_test.go +++ b/recipe/emailpassword/authFlow_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "io" + "io/ioutil" "net/http" "net/http/httptest" "strconv" @@ -31,10 +32,117 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" + "github.com/supertokens/supertokens-golang/recipe/thirdparty" + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestRightRidButRecipeMissingReturns404(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{ + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + res, err := unittesting.SignInRequest("random@gmail.com", "validpass123", testServer.URL) + + if err != nil { + t.Error(err.Error()) + } + + assert.NoError(t, err) + assert.Equal(t, 404, res.StatusCode) +} + +func TestSignInWorksWithThirdPartyEmailPasswordRid(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{ + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }), + Init(nil), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + res, err := unittesting.SignInRequestWithThirdpartyemailpasswordRid("random@gmail.com", "validpass123", testServer.URL) + + if err != nil { + t.Error(err.Error()) + } + + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) +} + // SigninFeature Tests func TestDisablingAPIDefaultSigninDoesNotWork(t *testing.T) { configValue := supertokens.TypeInput{ @@ -3016,3 +3124,56 @@ func TestInputEmailIsTrimmed(t *testing.T) { assert.Equal(t, "random@gmail.com", data["user"].(map[string]interface{})["email"]) } + +func TestSignUpAPIWorksWhenInputIsFine(t *testing.T) { + configValue := supertokens.TypeInput{ + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: "http://localhost:8080", + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + Init(nil), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) + if err != nil { + t.Error(err.Error()) + } + assert.Equal(t, http.StatusOK, resp.StatusCode) + dataInBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Error(err.Error()) + } + resp.Body.Close() + + var result map[string]interface{} + + err = json.Unmarshal(dataInBytes, &result) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", result["status"]) + assert.Equal(t, "random@gmail.com", result["user"].(map[string]interface{})["email"]) +} diff --git a/recipe/emailpassword/constants/constants.go b/recipe/emailpassword/constants/constants.go index 185be214..c27d8506 100644 --- a/recipe/emailpassword/constants/constants.go +++ b/recipe/emailpassword/constants/constants.go @@ -20,5 +20,6 @@ const ( SignInAPI = "/signin" GeneratePasswordResetTokenAPI = "/user/password/reset/token" PasswordResetAPI = "/user/password/reset" - SignupEmailExistsAPI = "/signup/email/exists" + SignupEmailExistsAPIOld = "/signup/email/exists" + SignupEmailExistsAPI = "/emailpassword/email/exists" ) diff --git a/recipe/emailpassword/emailExistsAndVerificationCheck_test.go b/recipe/emailpassword/emailExistsAndVerificationCheck_test.go index aa206463..08a84657 100644 --- a/recipe/emailpassword/emailExistsAndVerificationCheck_test.go +++ b/recipe/emailpassword/emailExistsAndVerificationCheck_test.go @@ -35,12 +35,139 @@ import ( "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" "github.com/supertokens/supertokens-golang/recipe/emailverification" "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" + "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/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestEmailExistsPicksRightRecipeDependingOnRid(t *testing.T) { + passwordlessEmailExists := false + emailpasswordEmailExists := false + configValue := supertokens.TypeInput{ + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: "http://localhost:8080", + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + Init(&epmodels.TypeInput{ + Override: &epmodels.OverrideStruct{ + APIs: func(originalImplementation epmodels.APIInterface) epmodels.APIInterface { + oEmailExists := *originalImplementation.EmailExistsGET + (*originalImplementation.EmailExistsGET) = func(email, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.EmailExistsGETResponse, error) { + emailpasswordEmailExists = true + return oEmailExists(email, tenantId, options, userContext) + } + + return originalImplementation + }, + }, + }), + passwordless.Init(plessmodels.TypeInput{ + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + FlowType: "USER_INPUT_CODE", + Override: &plessmodels.OverrideStruct{ + APIs: func(originalImplementation plessmodels.APIInterface) plessmodels.APIInterface { + oEmailExists := *originalImplementation.EmailExistsGET + (*originalImplementation.EmailExistsGET) = func(email, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.EmailExistsGETResponse, error) { + passwordlessEmailExists = true + return oEmailExists(email, tenantId, options, userContext) + } + + return originalImplementation + }, + }, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + { + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + q := req.URL.Query() + q.Add("email", "random@email.com") + req.Header.Add("rid", "emailpassword") + req.URL.RawQuery = q.Encode() + assert.NoError(t, err) + res, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + assert.True(t, emailpasswordEmailExists) + assert.False(t, passwordlessEmailExists) + } + + { + emailpasswordEmailExists = false + passwordlessEmailExists = false + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + q := req.URL.Query() + q.Add("email", "random@email.com") + req.Header.Add("rid", "passwordless") + req.URL.RawQuery = q.Encode() + assert.NoError(t, err) + res, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + assert.False(t, emailpasswordEmailExists) + assert.True(t, passwordlessEmailExists) + } + + { + emailpasswordEmailExists = false + passwordlessEmailExists = false + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + q := req.URL.Query() + q.Add("email", "random@email.com") + req.Header.Add("rid", "thirdpartypasswordless") + req.URL.RawQuery = q.Encode() + assert.NoError(t, err) + res, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + assert.False(t, emailpasswordEmailExists) + assert.True(t, passwordlessEmailExists) + } + + { + emailpasswordEmailExists = false + passwordlessEmailExists = false + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + q := req.URL.Query() + q.Add("email", "random@email.com") + req.Header.Add("rid", "thirdpartyemailpassword") + req.URL.RawQuery = q.Encode() + assert.NoError(t, err) + res, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + assert.True(t, emailpasswordEmailExists) + assert.False(t, passwordlessEmailExists) + } +} + // Email exists tests func TestEmailExistGetStopsWorkingWhenDisabled(t *testing.T) { configValue := supertokens.TypeInput{ @@ -172,6 +299,91 @@ func TestGoodInputsEmailExists(t *testing.T) { } +func TestGoodInputsEmailExistsNewPath(t *testing.T) { + configValue := supertokens.TypeInput{ + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: "http://localhost:8080", + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + Init(nil), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + passwordVal := "validPass123" + + emailVal := "random@email.com" + + formFields := map[string][]map[string]string{ + "formFields": { + { + "id": "email", + "value": emailVal, + }, + { + "id": "password", + "value": passwordVal, + }, + }, + } + + postBody, err := json.Marshal(formFields) + if err != nil { + t.Error(err.Error()) + } + + resp, err := http.Post(testServer.URL+"/auth/signup", "application/json", bytes.NewBuffer(postBody)) + + assert.Equal(t, 200, resp.StatusCode) + + assert.NoError(t, err) + data, _ := io.ReadAll(resp.Body) + resp.Body.Close() + var response map[string]interface{} + _ = json.Unmarshal(data, &response) + + assert.Equal(t, "OK", response["status"]) + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/emailpassword/email/exists", nil) + q := req.URL.Query() + q.Add("email", "random@email.com") + req.URL.RawQuery = q.Encode() + assert.NoError(t, err) + res, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + + data2, err := io.ReadAll(res.Body) + assert.NoError(t, err) + res.Body.Close() + var response2 map[string]interface{} + _ = json.Unmarshal(data2, &response2) + + assert.Equal(t, "OK", response2["status"]) + assert.Equal(t, true, response2["exists"]) + +} + func TestGoodInputsEmailDoesNotExists(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ @@ -430,76 +642,66 @@ func TestEmailDoesExistsWithBadInput(t *testing.T) { res, err := http.DefaultClient.Do(req) assert.NoError(t, err) assert.Equal(t, 400, res.StatusCode) + result := *unittesting.HttpResponseToConsumableInformation(res.Body) + assert.Equal(t, "Please provide the email as a GET param", result["message"]) } -//email_verify tests -// func TestGenerateTokenAPIWithValidInputAndEmailNotVerified(t *testing.T) { -// customAntiCsrfVal := "VIA_TOKEN" -// configValue := supertokens.TypeInput{ -// Supertokens: &supertokens.ConnectionInfo{ -// ConnectionURI: "http://localhost:8080", -// }, -// AppInfo: supertokens.AppInfo{ -// APIDomain: "api.supertokens.io", -// AppName: "SuperTokens", -// WebsiteDomain: "supertokens.io", -// }, -// RecipeList: []supertokens.Recipe{ -// Init(nil), -// session.Init(&sessmodels.TypeInput{ -// AntiCsrf: &customAntiCsrfVal, -// }), -// }, -// } - -// BeforeEach() -// unittesting.StartUpST("localhost", "8080") -// defer AfterEach() -// err := supertokens.Init(configValue) -// if err != nil { -// t.Error(err.Error()) -// } -// mux := http.NewServeMux() -// testServer := httptest.NewServer(supertokens.Middleware(mux)) -// defer testServer.Close() - -// resp, err := unittesting.SignupRequest("test@gmail.com", "testPass123", testServer.URL) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.NoError(t, err) -// assert.Equal(t, 200, resp.StatusCode) -// data, _ := io.ReadAll(resp.Body) -// resp.Body.Close() -// var response map[string]interface{} -// _ = json.Unmarshal(data, &response) -// assert.Equal(t, "OK", response["status"]) - -// userId := response["user"].(map[string]interface{})["id"] -// cookieData := unittesting.ExtractInfoFromResponse(resp) - -// verifyToken, err := CreateEmailVerificationToken(userId.(string)) -// if err != nil { -// t.Error(err.Error()) -// } -// VerifyEmailUsingToken(verifyToken.OK.Token) - -// resp1, err := unittesting.EmailVerifyTokenRequest(testServer.URL, userId.(string), cookieData["sAccessToken"], cookieData["antiCsrf"]) - -// if err != nil { -// t.Error(err.Error()) -// } - -// assert.NoError(t, err) -// assert.Equal(t, 200, resp1.StatusCode) -// data1, _ := io.ReadAll(resp1.Body) -// resp1.Body.Close() -// var response1 map[string]interface{} -// _ = json.Unmarshal(data1, &response1) - -// assert.Equal(t, "EMAIL_ALREADY_VERIFIED_ERROR", response1["status"]) - -// } +func TestGenerateTokenAPIWithValidInputAndEmailNotVerified(t *testing.T) { + customCSRFval := "VIA_TOKEN" + 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{ + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + }), + Init(nil), + session.Init(&sessmodels.TypeInput{ + AntiCsrf: &customCSRFval, + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + resp, err := unittesting.SignupRequest("random@gmail.com", "validPass123", testServer.URL) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + cookieData := unittesting.ExtractInfoFromResponse(resp) + + result := *unittesting.HttpResponseToConsumableInformation(resp.Body) + assert.Equal(t, "OK", result["status"]) + user := result["user"].(map[string]interface{}) + + rep1, err := unittesting.EmailVerifyTokenRequest(testServer.URL, user["id"].(string), cookieData["sAccessToken"], cookieData["antiCsrf"]) + if err != nil { + t.Error(err.Error()) + } + result1 := *unittesting.HttpResponseToConsumableInformation(rep1.Body) + assert.Equal(t, "OK", result1["status"]) +} func TestGenerateTokenAPIWithValidInputNoSessionAndCheckOutput(t *testing.T) { customAntiCsrfVal := "VIA_TOKEN" @@ -558,6 +760,69 @@ func TestGenerateTokenAPIWithValidInputNoSessionAndCheckOutput(t *testing.T) { assert.Equal(t, "unauthorised", response["message"]) } +func TestGenerateTokenAPIwithValidInputEmailVerifiedAndTestError(t *testing.T) { + customCSRFval := "VIA_TOKEN" + 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{ + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + }), + Init(nil), + session.Init(&sessmodels.TypeInput{ + AntiCsrf: &customCSRFval, + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + resp, err := unittesting.SignupRequest("random@gmail.com", "validPass123", testServer.URL) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + cookieData := unittesting.ExtractInfoFromResponse(resp) + + result := *unittesting.HttpResponseToConsumableInformation(resp.Body) + assert.Equal(t, "OK", result["status"]) + user := result["user"].(map[string]interface{}) + + verifyToken, err := emailverification.CreateEmailVerificationToken("public", user["id"].(string), nil) + if err != nil { + t.Error(err.Error()) + } + emailverification.VerifyEmailUsingToken("public", verifyToken.OK.Token) + + rep1, err := unittesting.EmailVerifyTokenRequest(testServer.URL, user["id"].(string), cookieData["sAccessToken"], cookieData["antiCsrf"]) + if err != nil { + t.Error(err.Error()) + } + result1 := *unittesting.HttpResponseToConsumableInformation(rep1.Body) + assert.Equal(t, "EMAIL_ALREADY_VERIFIED_ERROR", result1["status"]) +} + func TestGenerateTokenAPIWithExpiredAccessToken(t *testing.T) { customAntiCsrfVal := "VIA_TOKEN" configValue := supertokens.TypeInput{ diff --git a/recipe/thirdpartyemailpassword/loginmethods_test.go b/recipe/emailpassword/loginmethods_test.go similarity index 97% rename from recipe/thirdpartyemailpassword/loginmethods_test.go rename to recipe/emailpassword/loginmethods_test.go index ba09d6b6..846e5021 100644 --- a/recipe/thirdpartyemailpassword/loginmethods_test.go +++ b/recipe/emailpassword/loginmethods_test.go @@ -1,4 +1,4 @@ -package thirdpartyemailpassword +package emailpassword import ( "encoding/json" diff --git a/recipe/emailpassword/main.go b/recipe/emailpassword/main.go index e157d16a..45ae26b8 100644 --- a/recipe/emailpassword/main.go +++ b/recipe/emailpassword/main.go @@ -140,7 +140,6 @@ func CreateResetPasswordLink(tenantId string, userID string, userContext ...supe link, err := api.GetPasswordResetLink( instance.RecipeModule.GetAppInfo(), - instance.RecipeModule.GetRecipeID(), tokenResponse.OK.Token, tenantId, supertokens.GetRequestFromUserContext(userContext[0]), diff --git a/recipe/emailpassword/passwordReset_test.go b/recipe/emailpassword/passwordReset_test.go index 45d7d39d..abec9ce8 100644 --- a/recipe/emailpassword/passwordReset_test.go +++ b/recipe/emailpassword/passwordReset_test.go @@ -128,7 +128,7 @@ func TestEmailValidationCheckInGenerateTokenAPI(t *testing.T) { assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, "https://supertokens.io/auth/reset-password", resetURL) assert.NotEmpty(t, tokenInfo) - assert.True(t, strings.HasPrefix(ridInfo, "emailpassword")) + assert.True(t, strings.HasPrefix(ridInfo, "")) } func TestPasswordValidation(t *testing.T) { @@ -556,5 +556,5 @@ func TestPasswordResetLinkUsesOriginFunctionIfProvided(t *testing.T) { assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, "http://localhost:2000/auth/reset-password", resetURL) assert.NotEmpty(t, tokenInfo) - assert.True(t, strings.HasPrefix(ridInfo, "emailpassword")) + assert.True(t, strings.HasPrefix(ridInfo, "")) } diff --git a/recipe/emailpassword/recipe.go b/recipe/emailpassword/recipe.go index 0d501ccc..3fa20df4 100644 --- a/recipe/emailpassword/recipe.go +++ b/recipe/emailpassword/recipe.go @@ -120,6 +120,10 @@ func (r *Recipe) getAPIsHandled() ([]supertokens.APIHandled, error) { if err != nil { return nil, err } + signupEmailExistsAPIOld, err := supertokens.NewNormalisedURLPath(constants.SignupEmailExistsAPIOld) + if err != nil { + return nil, err + } signupEmailExistsAPI, err := supertokens.NewNormalisedURLPath(constants.SignupEmailExistsAPI) if err != nil { return nil, err @@ -144,6 +148,11 @@ func (r *Recipe) getAPIsHandled() ([]supertokens.APIHandled, error) { PathWithoutAPIBasePath: passwordResetAPI, ID: constants.PasswordResetAPI, Disabled: r.APIImpl.PasswordResetPOST == nil, + }, { + Method: http.MethodGet, + PathWithoutAPIBasePath: signupEmailExistsAPIOld, + ID: constants.SignupEmailExistsAPIOld, + Disabled: r.APIImpl.EmailExistsGET == nil, }, { Method: http.MethodGet, PathWithoutAPIBasePath: signupEmailExistsAPI, @@ -171,7 +180,7 @@ func (r *Recipe) handleAPIRequest(id string, tenantId string, req *http.Request, return api.GeneratePasswordResetToken(r.APIImpl, tenantId, options, userContext) } else if id == constants.PasswordResetAPI { return api.PasswordReset(r.APIImpl, tenantId, options, userContext) - } else if id == constants.SignupEmailExistsAPI { + } else if id == constants.SignupEmailExistsAPIOld || id == constants.SignupEmailExistsAPI { return api.EmailExists(r.APIImpl, tenantId, options, userContext) } return defaultErrors.New("should never come here") diff --git a/recipe/thirdpartyemailpassword/emailExist_test.go b/recipe/emailpassword/signinFeature_test.go similarity index 58% rename from recipe/thirdpartyemailpassword/emailExist_test.go rename to recipe/emailpassword/signinFeature_test.go index 2e42a3ef..cbc5a3cb 100644 --- a/recipe/thirdpartyemailpassword/emailExist_test.go +++ b/recipe/emailpassword/signinFeature_test.go @@ -13,22 +13,24 @@ * under the License. */ -package thirdpartyemailpassword +package emailpassword import ( + "encoding/json" + "io" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" + "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) -func TestIfDisableAPIdefaultEmailExistDoesNotWork(t *testing.T) { +func TestAfterDisablingTheDefaultSigninAPIdoesNotWork(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ ConnectionURI: "http://localhost:8080", @@ -39,10 +41,10 @@ func TestIfDisableAPIdefaultEmailExistDoesNotWork(t *testing.T) { WebsiteDomain: "supertokens.io", }, RecipeList: []supertokens.Recipe{ - Init(&tpepmodels.TypeInput{ - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - *originalImplementation.EmailPasswordEmailExistsGET = nil + Init(&epmodels.TypeInput{ + Override: &epmodels.OverrideStruct{ + APIs: func(originalImplementation epmodels.APIInterface) epmodels.APIInterface { + *originalImplementation.SignInPOST = nil return originalImplementation }, }, @@ -61,17 +63,14 @@ func TestIfDisableAPIdefaultEmailExistDoesNotWork(t *testing.T) { testServer := httptest.NewServer(supertokens.Middleware(mux)) defer testServer.Close() - req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) - q := req.URL.Query() - q.Add("email", "random@gmail.com") - req.URL.RawQuery = q.Encode() - assert.NoError(t, err) - res, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusNotFound, res.StatusCode) + resp, err := unittesting.SignInRequest("random@gmail.com", "validpass123", testServer.URL) + if err != nil { + t.Error(err.Error()) + } + assert.Equal(t, http.StatusNotFound, resp.StatusCode) } -func TestGoodInputEmailExist(t *testing.T) { +func TestSignInAPIWorksWhenInputIsFine(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ ConnectionURI: "http://localhost:8080", @@ -90,7 +89,6 @@ func TestGoodInputEmailExist(t *testing.T) { }), }, } - BeforeEach() unittesting.StartUpST("localhost", "8080") defer AfterEach() @@ -102,34 +100,47 @@ func TestGoodInputEmailExist(t *testing.T) { testServer := httptest.NewServer(supertokens.Middleware(mux)) defer testServer.Close() - resp, err := unittesting.SignupRequest("random@gmail.com", "validPass123", testServer.URL) + resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) if err != nil { t.Error(err.Error()) } assert.Equal(t, http.StatusOK, resp.StatusCode) + dataInBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Error(err.Error()) + } + var result map[string]interface{} + json.Unmarshal(dataInBytes, &result) + resp.Body.Close() - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) assert.Equal(t, "OK", result["status"]) - req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) - q := req.URL.Query() - q.Add("email", "random@gmail.com") - req.URL.RawQuery = q.Encode() + signupUserInfo := result["user"].(map[string]interface{}) + + resp1, err := unittesting.SignInRequest("random@gmail.com", "validpass123", testServer.URL) if err != nil { t.Error(err.Error()) } - res, err := http.DefaultClient.Do(req) + + assert.Equal(t, http.StatusOK, resp1.StatusCode) + dataInBytes1, err := io.ReadAll(resp1.Body) if err != nil { t.Error(err.Error()) } - result1 := *unittesting.HttpResponseToConsumableInformation(res.Body) - assert.Equal(t, http.StatusOK, res.StatusCode) + var result1 map[string]interface{} + json.Unmarshal(dataInBytes1, &result1) + resp1.Body.Close() + assert.Equal(t, "OK", result1["status"]) - assert.Equal(t, true, result1["exists"]) + + signInUserInfo := result1["user"].(map[string]interface{}) + + assert.Equal(t, signInUserInfo["id"], signupUserInfo["id"]) + assert.Equal(t, signInUserInfo["email"], signupUserInfo["email"]) } -func TestGoodInputEmailDoesNotExist(t *testing.T) { +func TestSigninAPIthrowsAnErrorWhenEmailDoesNotExist(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ ConnectionURI: "http://localhost:8080", @@ -148,7 +159,6 @@ func TestGoodInputEmailDoesNotExist(t *testing.T) { }), }, } - BeforeEach() unittesting.StartUpST("localhost", "8080") defer AfterEach() @@ -160,63 +170,35 @@ func TestGoodInputEmailDoesNotExist(t *testing.T) { testServer := httptest.NewServer(supertokens.Middleware(mux)) defer testServer.Close() - req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) - q := req.URL.Query() - q.Add("email", "random@gmail.com") - req.URL.RawQuery = q.Encode() + resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) if err != nil { t.Error(err.Error()) } - res, err := http.DefaultClient.Do(req) + + assert.Equal(t, http.StatusOK, resp.StatusCode) + dataInBytes, err := io.ReadAll(resp.Body) if err != nil { t.Error(err.Error()) } - result := *unittesting.HttpResponseToConsumableInformation(res.Body) - assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, "OK", result["status"]) - assert.Equal(t, false, result["exists"]) -} + var result map[string]interface{} + json.Unmarshal(dataInBytes, &result) + resp.Body.Close() -func TestBadInputDoNotPassEmail(t *testing.T) { - configValue := supertokens.TypeInput{ - Supertokens: &supertokens.ConnectionInfo{ - ConnectionURI: "http://localhost:8080", - }, - AppInfo: supertokens.AppInfo{ - APIDomain: "api.supertokens.io", - AppName: "SuperTokens", - WebsiteDomain: "supertokens.io", - }, - RecipeList: []supertokens.Recipe{ - Init(nil), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } + assert.Equal(t, "OK", result["status"]) - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) + resp1, err := unittesting.SignInRequest("rand@gmail.com", "validpass123", testServer.URL) if err != nil { t.Error(err.Error()) } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + assert.Equal(t, http.StatusOK, resp1.StatusCode) + dataInBytes1, err := io.ReadAll(resp1.Body) if err != nil { t.Error(err.Error()) } - res, err := http.DefaultClient.Do(req) - if err != nil { - t.Error(err.Error()) - } - result := *unittesting.HttpResponseToConsumableInformation(res.Body) - assert.Equal(t, http.StatusBadRequest, res.StatusCode) - assert.Equal(t, "Please provide the email as a GET param", result["message"]) + var result1 map[string]interface{} + json.Unmarshal(dataInBytes1, &result1) + resp1.Body.Close() + + assert.Equal(t, "WRONG_CREDENTIALS_ERROR", result1["status"]) } diff --git a/recipe/thirdpartyemailpassword/signoutFeature_test.go b/recipe/emailpassword/signoutFeature_test.go similarity index 94% rename from recipe/thirdpartyemailpassword/signoutFeature_test.go rename to recipe/emailpassword/signoutFeature_test.go index 288eb7de..85159c4e 100644 --- a/recipe/thirdpartyemailpassword/signoutFeature_test.go +++ b/recipe/emailpassword/signoutFeature_test.go @@ -13,7 +13,7 @@ * under the License. */ -package thirdpartyemailpassword +package emailpassword import ( "bytes" @@ -26,8 +26,6 @@ import ( "github.com/stretchr/testify/assert" "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/thirdpartyemailpassword/tpepmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" "gopkg.in/h2non/gock.v1" @@ -45,11 +43,7 @@ func TestDefaultRouteShouldRevokeSession(t *testing.T) { WebsiteDomain: "supertokens.io", }, RecipeList: []supertokens.Recipe{ - Init(&tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProvider2, - }, - }), + Init(nil), session.Init(&sessmodels.TypeInput{ AntiCsrf: &customAntiCsrfVal, GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { diff --git a/recipe/emailpassword/userIdMapping_create_test.go b/recipe/emailpassword/userIdMapping_create_test.go index f98d9461..13c4e5ac 100644 --- a/recipe/emailpassword/userIdMapping_create_test.go +++ b/recipe/emailpassword/userIdMapping_create_test.go @@ -213,3 +213,78 @@ func TestCreateUserIdMappingWithMetadataAndWithAndWithoutForce(t *testing.T) { assert.NotNil(t, createResp.OK) } } + +func TestEPCreateUserIdMappingGetUserById(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + initForUserIdMappingTest(t) + + querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") + assert.NoError(t, err) + + cdiVersion, err := querier.GetQuerierAPIVersion() + assert.NoError(t, err) + + if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { + return + } + + signUpResponse, err := SignUp("public", "test@example.com", "testpass123") + assert.NoError(t, err) + + assert.NotNil(t, signUpResponse.OK) + + externalUserId := "externalId" + externalUserIdInfo := "externalIdInfo" + createResp, err := supertokens.CreateUserIdMapping(signUpResponse.OK.User.ID, externalUserId, &externalUserIdInfo, nil) + assert.NoError(t, err) + assert.NotNil(t, createResp.OK) + + { // Using supertokens ID + userResp, err := GetUserByID(signUpResponse.OK.User.ID) + assert.NoError(t, err) + assert.Equal(t, externalUserId, userResp.ID) + } + + { // Using external ID + userResp, err := GetUserByID(externalUserId) + assert.NoError(t, err) + assert.Equal(t, externalUserId, userResp.ID) + } +} + +func TestEPCreateUserIdMappingGetUserByEmail(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + initForUserIdMappingTest(t) + + querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") + assert.NoError(t, err) + + cdiVersion, err := querier.GetQuerierAPIVersion() + assert.NoError(t, err) + + if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { + return + } + + signUpResponse, err := SignUp("public", "test@example.com", "testpass123") + assert.NoError(t, err) + + assert.NotNil(t, signUpResponse.OK) + + externalUserId := "externalId" + externalUserIdInfo := "externalIdInfo" + createResp, err := supertokens.CreateUserIdMapping(signUpResponse.OK.User.ID, externalUserId, &externalUserIdInfo, nil) + assert.NoError(t, err) + assert.NotNil(t, createResp.OK) + + userResp, err := GetUserByEmail("public", "test@example.com") + assert.NoError(t, err) + assert.NotNil(t, userResp) + assert.Equal(t, externalUserId, userResp.ID) +} diff --git a/recipe/emailverification/api/implementation.go b/recipe/emailverification/api/implementation.go index 5c144b13..f0493742 100644 --- a/recipe/emailverification/api/implementation.go +++ b/recipe/emailverification/api/implementation.go @@ -126,7 +126,6 @@ func MakeAPIImplementation() evmodels.APIInterface { emailVerificationURL, err := GetEmailVerifyLink( options.AppInfo, response.OK.Token, - options.RecipeID, sessionContainer.GetTenantIdWithContext(userContext), options.Req, userContext, diff --git a/recipe/emailverification/api/utils.go b/recipe/emailverification/api/utils.go index b18490c4..c9a4190e 100644 --- a/recipe/emailverification/api/utils.go +++ b/recipe/emailverification/api/utils.go @@ -7,17 +7,16 @@ import ( "github.com/supertokens/supertokens-golang/supertokens" ) -func GetEmailVerifyLink(appInfo supertokens.NormalisedAppinfo, token string, recipeID string, tenantId string, request *http.Request, userContext supertokens.UserContext) (string, error) { +func GetEmailVerifyLink(appInfo supertokens.NormalisedAppinfo, token string, tenantId string, request *http.Request, userContext supertokens.UserContext) (string, error) { websiteDomain, err := appInfo.GetOrigin(request, userContext) if err != nil { return "", err } return fmt.Sprintf( - "%s%s/verify-email?token=%s&rid=%s&tenantId=%s", + "%s%s/verify-email?token=%s&tenantId=%s", websiteDomain.GetAsStringDangerous(), appInfo.WebsiteBasePath.GetAsStringDangerous(), token, - recipeID, tenantId, ), nil } diff --git a/recipe/emailverification/emailverification_email_test.go b/recipe/emailverification/emailverification_email_test.go index ae210578..57d26e90 100644 --- a/recipe/emailverification/emailverification_email_test.go +++ b/recipe/emailverification/emailverification_email_test.go @@ -18,6 +18,7 @@ package emailverification import ( "net/http" + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -81,6 +82,7 @@ func TestBackwardCompatibilityServiceWithoutCustomFunction(t *testing.T) { func TestBackwardCompatibilityServiceWithOverride(t *testing.T) { funcCalled := false overrideCalled := false + ridInfo := "" configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ ConnectionURI: "http://localhost:8080", @@ -96,6 +98,11 @@ func TestBackwardCompatibilityServiceWithOverride(t *testing.T) { EmailDelivery: &emaildelivery.TypeInput{ Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { (*originalImplementation.SendEmail) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + u, err := url.Parse(input.EmailVerification.EmailVerifyLink) + if err != nil { + return err + } + ridInfo = u.Query().Get("rid") overrideCalled = true return nil } @@ -134,6 +141,7 @@ func TestBackwardCompatibilityServiceWithOverride(t *testing.T) { assert.Equal(t, EmailVerificationEmailSentForTest, false) assert.Equal(t, funcCalled, false) assert.Equal(t, overrideCalled, true) + assert.Equal(t, ridInfo, "") } func TestSMTPServiceOverride(t *testing.T) { diff --git a/recipe/emailverification/main.go b/recipe/emailverification/main.go index 61b68c0a..6a7d15b0 100644 --- a/recipe/emailverification/main.go +++ b/recipe/emailverification/main.go @@ -158,10 +158,7 @@ func CreateEmailVerificationLink(tenantId string, userID string, email *string, if err != nil { return evmodels.CreateEmailVerificationLinkResponse{}, err } - instance, err := getRecipeInstanceOrThrowError() - if err != nil { - return evmodels.CreateEmailVerificationLinkResponse{}, err - } + if len(userContext) == 0 { userContext = append(userContext, &map[string]interface{}{}) } @@ -176,7 +173,7 @@ func CreateEmailVerificationLink(tenantId string, userID string, email *string, }, nil } - link, err := api.GetEmailVerifyLink(st.AppInfo, emailVerificationTokenResponse.OK.Token, instance.RecipeModule.GetRecipeID(), tenantId, supertokens.GetRequestFromUserContext(userContext[0]), userContext[0]) + link, err := api.GetEmailVerifyLink(st.AppInfo, emailVerificationTokenResponse.OK.Token, tenantId, supertokens.GetRequestFromUserContext(userContext[0]), userContext[0]) if err != nil { return evmodels.CreateEmailVerificationLinkResponse{}, err diff --git a/recipe/passwordless/api/implementation.go b/recipe/passwordless/api/implementation.go index 8cf22001..4e07372b 100644 --- a/recipe/passwordless/api/implementation.go +++ b/recipe/passwordless/api/implementation.go @@ -101,7 +101,6 @@ func MakeAPIImplementation() plessmodels.APIInterface { if flowType == "MAGIC_LINK" || flowType == "USER_INPUT_CODE_AND_MAGIC_LINK" { link, err := GetMagicLink( options.AppInfo, - options.RecipeID, response.OK.PreAuthSessionID, response.OK.LinkCode, tenantId, @@ -283,7 +282,6 @@ func MakeAPIImplementation() plessmodels.APIInterface { if flowType == "MAGIC_LINK" || flowType == "USER_INPUT_CODE_AND_MAGIC_LINK" { link, err := GetMagicLink( options.AppInfo, - options.RecipeID, response.OK.PreAuthSessionID, response.OK.LinkCode, tenantId, diff --git a/recipe/passwordless/api/utils.go b/recipe/passwordless/api/utils.go index 95d002dd..77055497 100644 --- a/recipe/passwordless/api/utils.go +++ b/recipe/passwordless/api/utils.go @@ -7,16 +7,15 @@ import ( "github.com/supertokens/supertokens-golang/supertokens" ) -func GetMagicLink(appInfo supertokens.NormalisedAppinfo, recipeID string, preAuthSessionID string, linkCode string, tenantId string, request *http.Request, userContext supertokens.UserContext) (string, error) { +func GetMagicLink(appInfo supertokens.NormalisedAppinfo, preAuthSessionID string, linkCode string, tenantId string, request *http.Request, userContext supertokens.UserContext) (string, error) { websiteDomain, err := appInfo.GetOrigin(request, userContext) if err != nil { return "", err } return fmt.Sprintf( - "%s%s/verify?rid=%s&preAuthSessionId=%s&tenantId=%s#%s", + "%s%s/verify?preAuthSessionId=%s&tenantId=%s#%s", websiteDomain.GetAsStringDangerous(), appInfo.WebsiteBasePath.GetAsStringDangerous(), - recipeID, preAuthSessionID, tenantId, linkCode, diff --git a/recipe/passwordless/api_test.go b/recipe/passwordless/api_test.go index 0852fe56..f0a6c92a 100644 --- a/recipe/passwordless/api_test.go +++ b/recipe/passwordless/api_test.go @@ -21,6 +21,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "testing" "time" @@ -30,10 +31,1002 @@ import ( "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" + "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestCreateCodeAPIWithRidAsThirdpartypasswordless(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 + }, + }), + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }), + Init(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + BeforeEach() + unittesting.SetKeyValueInConfig("passwordless_code_lifetime", "1000") + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + validEmail := map[string]interface{}{ + "email": "test@example.com", + } + + validEmailBody, err := json.Marshal(validEmail) + if err != nil { + t.Error(err.Error()) + } + + client := &http.Client{} + req, _ := http.NewRequest("POST", testServer.URL+"/auth/signinup/code", bytes.NewBuffer(validEmailBody)) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("rid", "thirdpartypasswordless") + + validEmailResp, err := client.Do(req) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, validEmailResp.StatusCode) + + validEmailDataInBytes, err := io.ReadAll(validEmailResp.Body) + if err != nil { + t.Error(err.Error()) + } + validEmailResp.Body.Close() + + var validEmailResult map[string]interface{} + err = json.Unmarshal(validEmailDataInBytes, &validEmailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", validEmailResult["status"]) + assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", validEmailResult["flowType"]) + assert.Equal(t, 4, len(validEmailResult)) +} + +func TestCreateCodeAPIWithRidAsRandom(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 + }, + }), + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }), + Init(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + BeforeEach() + unittesting.SetKeyValueInConfig("passwordless_code_lifetime", "1000") + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + validEmail := map[string]interface{}{ + "email": "test@example.com", + } + + validEmailBody, err := json.Marshal(validEmail) + if err != nil { + t.Error(err.Error()) + } + + client := &http.Client{} + req, _ := http.NewRequest("POST", testServer.URL+"/auth/signinup/code", bytes.NewBuffer(validEmailBody)) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("rid", "random") + + validEmailResp, err := client.Do(req) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, validEmailResp.StatusCode) + + validEmailDataInBytes, err := io.ReadAll(validEmailResp.Body) + if err != nil { + t.Error(err.Error()) + } + validEmailResp.Body.Close() + + var validEmailResult map[string]interface{} + err = json.Unmarshal(validEmailDataInBytes, &validEmailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", validEmailResult["status"]) + assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", validEmailResult["flowType"]) + assert.Equal(t, 4, len(validEmailResult)) +} + +func TestCreateCodeAPIWithWrongRid(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 + }, + }), + thirdparty.Init(&tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }), + Init(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + BeforeEach() + unittesting.SetKeyValueInConfig("passwordless_code_lifetime", "1000") + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + validEmail := map[string]interface{}{ + "email": "test@example.com", + } + + validEmailBody, err := json.Marshal(validEmail) + if err != nil { + t.Error(err.Error()) + } + + client := &http.Client{} + req, _ := http.NewRequest("POST", testServer.URL+"/auth/signinup/code", bytes.NewBuffer(validEmailBody)) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("rid", "emailpassword") + + validEmailResp, err := client.Do(req) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, validEmailResp.StatusCode) + + validEmailDataInBytes, err := io.ReadAll(validEmailResp.Body) + if err != nil { + t.Error(err.Error()) + } + validEmailResp.Body.Close() + + var validEmailResult map[string]interface{} + err = json.Unmarshal(validEmailDataInBytes, &validEmailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", validEmailResult["status"]) + assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", validEmailResult["flowType"]) + assert.Equal(t, 4, len(validEmailResult)) +} + +func TestWithEmailExistAPI(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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + query := req.URL.Query() + query.Add("email", "test@example.com") + req.URL.RawQuery = query.Encode() + assert.NoError(t, err) + emailDoesNotExistResp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailDoesNotExistResp.StatusCode) + + emailDoesNotExistResponse := *unittesting.HttpResponseToConsumableInformation(emailDoesNotExistResp.Body) + + assert.Equal(t, "OK", emailDoesNotExistResponse["status"]) + assert.False(t, emailDoesNotExistResponse["exists"].(bool)) + + codeInfo, err := CreateCodeWithEmail("public", "test@example.com", nil) + assert.NoError(t, err) + + ConsumeCodeWithLinkCode("public", codeInfo.OK.LinkCode, codeInfo.OK.PreAuthSessionID) + + req, err = http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) + query = req.URL.Query() + query.Add("email", "test@example.com") + req.URL.RawQuery = query.Encode() + assert.NoError(t, err) + emailExistsResp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailExistsResp.StatusCode) + + emailExistsResponse := *unittesting.HttpResponseToConsumableInformation(emailExistsResp.Body) + + assert.Equal(t, "OK", emailExistsResponse["status"]) + assert.True(t, emailExistsResponse["exists"].(bool)) +} + +func TestWithEmailExistAPINewPath(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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/passwordless/email/exists", nil) + query := req.URL.Query() + query.Add("email", "test@example.com") + req.URL.RawQuery = query.Encode() + assert.NoError(t, err) + emailDoesNotExistResp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailDoesNotExistResp.StatusCode) + + emailDoesNotExistResponse := *unittesting.HttpResponseToConsumableInformation(emailDoesNotExistResp.Body) + + assert.Equal(t, "OK", emailDoesNotExistResponse["status"]) + assert.False(t, emailDoesNotExistResponse["exists"].(bool)) + + codeInfo, err := CreateCodeWithEmail("public", "test@example.com", nil) + assert.NoError(t, err) + + ConsumeCodeWithLinkCode("public", codeInfo.OK.LinkCode, codeInfo.OK.PreAuthSessionID) + + req, err = http.NewRequest(http.MethodGet, testServer.URL+"/auth/passwordless/email/exists", nil) + query = req.URL.Query() + query.Add("email", "test@example.com") + req.URL.RawQuery = query.Encode() + assert.NoError(t, err) + emailExistsResp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailExistsResp.StatusCode) + + emailExistsResponse := *unittesting.HttpResponseToConsumableInformation(emailExistsResp.Body) + + assert.Equal(t, "OK", emailExistsResponse["status"]) + assert.True(t, emailExistsResponse["exists"].(bool)) +} + +func TestMagicLinkFormatInCreateCodeAPI(t *testing.T) { + var magicLinkURL *url.URL + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + magicLinkURL, _ = url.Parse(*input.PasswordlessLogin.UrlWithLinkCode) + return nil + } + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + validCreateCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, validCreateCodeResp.StatusCode) + + validCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(validCreateCodeResp.Body) + + assert.Equal(t, "OK", validCreateCodeResponse["status"]) + assert.Equal(t, "supertokens.io", magicLinkURL.Hostname()) + assert.Equal(t, "/auth/verify", magicLinkURL.Path) + assert.Equal(t, "", magicLinkURL.Query().Get("rid")) + assert.Equal(t, validCreateCodeResponse["preAuthSessionId"], magicLinkURL.Query().Get("preAuthSessionId")) +} + +func TestPhoneNumberToAUsersInfoAndSigningInWillSignInTheSameUserUsingTheEmailOrPhoneContactMethod(t *testing.T) { + var userInputCodeRef string + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + userInputCodeRef = *input.PasswordlessLogin.UserInputCode + return nil + } + sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { + userInputCodeRef = *input.PasswordlessLogin.UserInputCode + return nil + } + + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + SmsDelivery: &smsdelivery.TypeInput{ + Service: &smsdelivery.SmsDeliveryInterface{ + SendSms: &sendSms, + }, + }, + ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + emailCreateCodeResult := *unittesting.HttpResponseToConsumableInformation(emailResp.Body) + + assert.Equal(t, "OK", emailCreateCodeResult["status"]) + + consumeCodePostData := map[string]interface{}{ + "preAuthSessionId": emailCreateCodeResult["preAuthSessionId"], + "userInputCode": userInputCodeRef, + "deviceId": emailCreateCodeResult["deviceId"], + } + + consumeCodePostBody, err := json.Marshal(consumeCodePostData) + if err != nil { + t.Error(err.Error()) + } + + consumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, consumeCodeResp.StatusCode) + + emailUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp.Body) + + assert.Equal(t, "OK", emailUserInputCodeResponse["status"]) + user := emailUserInputCodeResponse["user"].(map[string]interface{}) + + phoneNumber := "+12345678901" + UpdateUser(user["id"].(string), nil, &phoneNumber) + + phoneNumberPostData := map[string]interface{}{ + "phoneNumber": "+12345678901", + } + + phoneNumberPostBody, err := json.Marshal(phoneNumberPostData) + if err != nil { + t.Error(err.Error()) + } + + phoneNumberPostResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneNumberPostBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, phoneNumberPostResp.StatusCode) + + phoneCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(phoneNumberPostResp.Body) + + assert.Equal(t, "OK", phoneCreateCodeResponse["status"]) + + consumeCodePostData1 := map[string]interface{}{ + "preAuthSessionId": phoneCreateCodeResponse["preAuthSessionId"], + "userInputCode": userInputCodeRef, + "deviceId": phoneCreateCodeResponse["deviceId"], + } + + consumeCodePostBody1, err := json.Marshal(consumeCodePostData1) + if err != nil { + t.Error(err.Error()) + } + + consumeCodeResp1, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody1)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, consumeCodeResp1.StatusCode) + + phoneUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp1.Body) + + assert.Equal(t, "OK", phoneUserInputCodeResponse["status"]) + user1 := phoneUserInputCodeResponse["user"].(map[string]interface{}) + + assert.Equal(t, user["id"], user1["id"]) +} + +func TestWithInvalidInputToCreateCodeAPIWhileUsingTheEmailOrPhoneContactMethod(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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + postData := map[string]interface{}{ + "email": "test@example.com", + "phoneNumber": "+12345678901", + } + + postBody, err := json.Marshal(postData) + if err != nil { + t.Error(err.Error()) + } + + resp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(postBody)) + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + assert.NoError(t, err) + + result := *unittesting.HttpResponseToConsumableInformation(resp.Body) + + assert.Equal(t, "Please provide exactly one of email or phoneNumber", result["message"]) + + postData = map[string]interface{}{} + + postBody, err = json.Marshal(postData) + if err != nil { + t.Error(err.Error()) + } + + resp1, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(postBody)) + assert.Equal(t, http.StatusBadRequest, resp1.StatusCode) + assert.NoError(t, err) + + result1 := *unittesting.HttpResponseToConsumableInformation(resp1.Body) + + assert.Equal(t, "Please provide exactly one of email or phoneNumber", result1["message"]) +} + +func TestForCreatingACodeWithEmailAndThenResendingTheCodeAndCheckThatTheSendingCustomEmailFunctionIsCalledWhileUsingTheEmailOrPhoneContactMethod(t *testing.T) { + isCreateAndSendCustomEmailCalled := false + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + isCreateAndSendCustomEmailCalled = true + return nil + } + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + result := *unittesting.HttpResponseToConsumableInformation(emailResp.Body) + + assert.Equal(t, "OK", result["status"]) + assert.True(t, isCreateAndSendCustomEmailCalled) + + isCreateAndSendCustomEmailCalled = false + + codeResendPostData := map[string]interface{}{ + "deviceId": result["deviceId"], + "preAuthSessionId": result["preAuthSessionId"], + } + + codeResendPostBody, err := json.Marshal(codeResendPostData) + if err != nil { + t.Error(err.Error()) + } + + codeResendPostResp, err := http.Post(testServer.URL+"/auth/signinup/code/resend", "application/json", bytes.NewBuffer(codeResendPostBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, codeResendPostResp.StatusCode) + + codeResendResult := *unittesting.HttpResponseToConsumableInformation(codeResendPostResp.Body) + assert.Equal(t, "OK", codeResendResult["status"]) + assert.True(t, isCreateAndSendCustomEmailCalled) +} + +func TestSignUpSignInFlowWithPhoneNumberUsingEmailOrPhoneContactMethod(t *testing.T) { + var userInputCodeRef string + sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { + userInputCodeRef = *input.PasswordlessLogin.UserInputCode + return nil + } + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + SmsDelivery: &smsdelivery.TypeInput{ + Service: &smsdelivery.SmsDeliveryInterface{ + SendSms: &sendSms, + }, + }, + ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ + Enabled: true, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + phone := map[string]interface{}{ + "phoneNumber": "+12345678901", + } + + phoneBody, err := json.Marshal(phone) + if err != nil { + t.Error(err.Error()) + } + + phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, phoneResp.StatusCode) + + result := *unittesting.HttpResponseToConsumableInformation(phoneResp.Body) + + assert.Equal(t, "OK", result["status"]) + assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", result["flowType"]) + + consumeCodePostData := map[string]interface{}{ + "preAuthSessionId": result["preAuthSessionId"], + "userInputCode": userInputCodeRef, + "deviceId": result["deviceId"], + } + + consumeCodePostBody, err := json.Marshal(consumeCodePostData) + if err != nil { + t.Error(err.Error()) + } + + consumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, consumeCodeResp.StatusCode) + + codeConsumeResult := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp.Body) + + user := codeConsumeResult["user"].(map[string]interface{}) + assert.Equal(t, "OK", codeConsumeResult["status"]) + assert.True(t, codeConsumeResult["createdNewUser"].(bool)) + assert.NotNil(t, user) + assert.Nil(t, user["email"]) + assert.NotNil(t, user["id"]) + assert.NotNil(t, user["timeJoined"]) + assert.NotNil(t, user["phoneNumber"]) +} + func TestSignInUpFlowWithEmailUsingTheEmailOrPhoneContactMethod(t *testing.T) { var userInputCodeRef *string sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { @@ -1431,6 +2424,110 @@ func TestPhoneNumberExistsAPI(t *testing.T) { assert.Equal(t, true, phoneResult1["exists"]) } +func TestPhoneNumberExistsAPINewPath(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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ + Enabled: true, + }, + }), + }, + } + BeforeEach() + unittesting.SetKeyValueInConfig("passwordless_code_lifetime", "1000") + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/passwordless/phonenumber/exists", nil) + query := req.URL.Query() + query.Add("phoneNumber", "+1234567890") + req.URL.RawQuery = query.Encode() + assert.NoError(t, err) + phoneResp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, phoneResp.StatusCode) + + phoneDataInBytes, err := io.ReadAll(phoneResp.Body) + if err != nil { + t.Error(err.Error()) + } + phoneResp.Body.Close() + + var phoneResult map[string]interface{} + err = json.Unmarshal(phoneDataInBytes, &phoneResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", phoneResult["status"]) + assert.Equal(t, false, phoneResult["exists"]) + + codeInfo, err := CreateCodeWithPhoneNumber("public", "+1234567890", nil) + assert.NoError(t, err) + + _, err = ConsumeCodeWithLinkCode("public", codeInfo.OK.LinkCode, codeInfo.OK.PreAuthSessionID) + assert.NoError(t, err) + + req1, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/passwordless/phonenumber/exists", nil) + query1 := req.URL.Query() + query1.Add("phoneNumber", "+1234567890") + req1.URL.RawQuery = query1.Encode() + assert.NoError(t, err) + phoneResp1, err := http.DefaultClient.Do(req1) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, phoneResp1.StatusCode) + + phoneDataInBytes1, err := io.ReadAll(phoneResp1.Body) + if err != nil { + t.Error(err.Error()) + } + phoneResp1.Body.Close() + + var phoneResult1 map[string]interface{} + err = json.Unmarshal(phoneDataInBytes1, &phoneResult1) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", phoneResult1["status"]) + assert.Equal(t, true, phoneResult1["exists"]) +} + func TestResendCodeAPI(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ diff --git a/recipe/passwordless/config_test.go b/recipe/passwordless/config_test.go index 6189f9a7..868cfe07 100644 --- a/recipe/passwordless/config_test.go +++ b/recipe/passwordless/config_test.go @@ -34,6 +34,685 @@ import ( "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestForThatForCreateAndCustomEmailIfErrorIsThrownTheStatusInTheResponseShouldBeA500Error(t *testing.T) { + isCreateAndSendCustomEmailCalled := false + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + isCreateAndSendCustomEmailCalled = true + return errors.New("test message") + } + + 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(plessmodels.TypeInput{ + FlowType: "MAGIC_LINK", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + + assert.NoError(t, err) + assert.Equal(t, 500, emailResp.StatusCode) + assert.True(t, isCreateAndSendCustomEmailCalled) +} + +func TestWithCreateAndSendCustomEmailWithFlowTypeUserInputCodeAndMagicLinkAndEmailContactMethod(t *testing.T) { + isUserInputCodeAndUrlWithLinkCodeValid := false + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + if input.PasswordlessLogin.UserInputCode != nil && input.PasswordlessLogin.UrlWithLinkCode != nil { + isUserInputCodeAndUrlWithLinkCodeValid = true + } + return nil + } + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + emailDataInBytes, err := io.ReadAll(emailResp.Body) + if err != nil { + t.Error(err.Error()) + } + emailResp.Body.Close() + + var emailResult map[string]interface{} + err = json.Unmarshal(emailDataInBytes, &emailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", emailResult["status"]) + assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) +} + +func TestCreateAndSendCustomEmailWithFlowTypeMagicLinkAndEmailContactMethod(t *testing.T) { + isUserInputCodeAndUrlWithLinkCodeValid := false + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + if input.PasswordlessLogin.UserInputCode == nil && input.PasswordlessLogin.UrlWithLinkCode != nil { + isUserInputCodeAndUrlWithLinkCodeValid = true + } + return nil + } + 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(plessmodels.TypeInput{ + FlowType: "MAGIC_LINK", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + emailDataInBytes, err := io.ReadAll(emailResp.Body) + if err != nil { + t.Error(err.Error()) + } + emailResp.Body.Close() + + var emailResult map[string]interface{} + err = json.Unmarshal(emailDataInBytes, &emailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", emailResult["status"]) + assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) +} + +func TestCreateAndSendCustomEmailWithFlowTypeUserInputCodeAndEmailContactMethod(t *testing.T) { + isUserInputCodeAndUrlWithLinkCodeValid := false + sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + if input.PasswordlessLogin.UserInputCode != nil && input.PasswordlessLogin.UrlWithLinkCode == nil { + isUserInputCodeAndUrlWithLinkCodeValid = true + } + return nil + + } + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE", + EmailDelivery: &emaildelivery.TypeInput{ + Service: &emaildelivery.EmailDeliveryInterface{ + SendEmail: &sendEmail, + }, + }, + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + emailDataInBytes, err := io.ReadAll(emailResp.Body) + if err != nil { + t.Error(err.Error()) + } + emailResp.Body.Close() + + var emailResult map[string]interface{} + err = json.Unmarshal(emailDataInBytes, &emailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", emailResult["status"]) + assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) +} + +func TestValidateEmailAdressThrowsGenericErrorInCaseOfReturningAString(t *testing.T) { + isValidateEmailAddressCalled := false + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + ValidateEmailAddress: func(email interface{}, tenantId string) *string { + isValidateEmailAddressCalled = true + message := "test error" + return &message + }, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + emailDataInBytes, err := io.ReadAll(emailResp.Body) + if err != nil { + t.Error(err.Error()) + } + emailResp.Body.Close() + + var emailResult map[string]interface{} + err = json.Unmarshal(emailDataInBytes, &emailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "GENERAL_ERROR", emailResult["status"]) + assert.Equal(t, "test error", emailResult["message"]) + assert.True(t, isValidateEmailAddressCalled) +} + +func TestIfValidateEmailAdressIsCalledWithContactMethod(t *testing.T) { + isValidateEmailAddressCalled := false + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + ValidateEmailAddress: func(email interface{}, tenantId string) *string { + isValidateEmailAddressCalled = true + return nil + }, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + email := map[string]interface{}{ + "email": "test@example.com", + } + + emailBody, err := json.Marshal(email) + if err != nil { + t.Error(err.Error()) + } + + emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, emailResp.StatusCode) + + emailDataInBytes, err := io.ReadAll(emailResp.Body) + if err != nil { + t.Error(err.Error()) + } + emailResp.Body.Close() + + var emailResult map[string]interface{} + err = json.Unmarshal(emailDataInBytes, &emailResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", emailResult["status"]) + assert.True(t, isValidateEmailAddressCalled) +} + +func TestCreateAndSendCustomTextMessageIfErrorIsThrownItShouldReturnA500Error(t *testing.T) { + isUserInputCodeAndUrlWithLinkCodeValid := false + sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { + isUserInputCodeAndUrlWithLinkCodeValid = true + return errors.New("test message") + } + 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(plessmodels.TypeInput{ + FlowType: "MAGIC_LINK", + SmsDelivery: &smsdelivery.TypeInput{ + Service: &smsdelivery.SmsDeliveryInterface{ + SendSms: &sendSms, + }, + }, + ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ + Enabled: true, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + phone := map[string]interface{}{ + "phoneNumber": "+12345678901", + } + + phoneBody, err := json.Marshal(phone) + if err != nil { + t.Error(err.Error()) + } + + phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) + + assert.NoError(t, err) + assert.Equal(t, 500, phoneResp.StatusCode) + assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) +} + +func TestCreateAndSendCustomMessageWithFlowTypeUserInputCodeAndMagicLinkAndPhoneContactMethod(t *testing.T) { + isUserInputCodeAndUrlWithLinkCodeValid := false + sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { + if input.PasswordlessLogin.UserInputCode != nil && input.PasswordlessLogin.UrlWithLinkCode != nil { + isUserInputCodeAndUrlWithLinkCodeValid = true + } + return nil + } + + 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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + SmsDelivery: &smsdelivery.TypeInput{ + Service: &smsdelivery.SmsDeliveryInterface{ + SendSms: &sendSms, + }, + }, + ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ + Enabled: true, + }, + }), + }, + } + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + phone := map[string]interface{}{ + "phoneNumber": "+12345678901", + } + + phoneBody, err := json.Marshal(phone) + if err != nil { + t.Error(err.Error()) + } + + phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, phoneResp.StatusCode) + + phoneDataInBytes, err := io.ReadAll(phoneResp.Body) + if err != nil { + t.Error(err.Error()) + } + phoneResp.Body.Close() + + var phoneResult map[string]interface{} + err = json.Unmarshal(phoneDataInBytes, &phoneResult) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", phoneResult["status"]) + assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) +} + func TestMinimumConfigWithEmailOrPhoneContactMethod(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ diff --git a/recipe/passwordless/constants.go b/recipe/passwordless/constants.go index 9e004626..9be6b7a9 100644 --- a/recipe/passwordless/constants.go +++ b/recipe/passwordless/constants.go @@ -16,9 +16,11 @@ package passwordless const ( - createCodeAPI = "/signinup/code" - resendCodeAPI = "/signinup/code/resend" - consumeCodeAPI = "/signinup/code/consume" - doesEmailExistAPI = "/signup/email/exists" - doesPhoneNumberExistAPI = "/signup/phonenumber/exists" + createCodeAPI = "/signinup/code" + resendCodeAPI = "/signinup/code/resend" + consumeCodeAPI = "/signinup/code/consume" + doesEmailExistAPIOld = "/signup/email/exists" + doesPhoneNumberExistAPIOld = "/signup/phonenumber/exists" + doesEmailExistAPI = "/passwordless/email/exists" + doesPhoneNumberExistAPI = "/passwordless/phonenumber/exists" ) diff --git a/recipe/passwordless/passwordless_email_test.go b/recipe/passwordless/passwordless_email_test.go index 9ad85df0..368183d7 100644 --- a/recipe/passwordless/passwordless_email_test.go +++ b/recipe/passwordless/passwordless_email_test.go @@ -26,6 +26,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" + "github.com/supertokens/supertokens-golang/recipe/emailverification" + "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" + "github.com/supertokens/supertokens-golang/recipe/passwordless/emaildelivery/smtpService" "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" @@ -33,6 +36,302 @@ import ( "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestSMTPOverrideEmailVerifyForPasswordlessUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + getContentCalled := false + sendRawEmailCalled := false + email := "" + emailVerifyLink := "" + var userInputCode *string + + evSmtpService := smtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ + Settings: emaildelivery.SMTPSettings{ + Host: "", + From: emaildelivery.SMTPFrom{ + Name: "Test User", + Email: "", + }, + Port: 123, + Password: "", + }, + Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { + (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { + if input.EmailVerification != nil { + email = input.EmailVerification.User.Email + emailVerifyLink = input.EmailVerification.EmailVerifyLink + getContentCalled = true + } + return emaildelivery.EmailContent{}, nil + } + + (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { + sendRawEmailCalled = true + return nil + } + + return originalImplementation + }, + }) + tplSmtpService := MakeSMTPService(emaildelivery.SMTPServiceConfig{ + Settings: emaildelivery.SMTPSettings{ + Host: "", + From: emaildelivery.SMTPFrom{ + Name: "Test User", + Email: "", + }, + Port: 123, + Password: "", + }, + Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { + (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { + if input.PasswordlessLogin != nil { + userInputCode = input.PasswordlessLogin.UserInputCode + } + return emaildelivery.EmailContent{}, nil + } + + (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { + sendRawEmailCalled = true + return nil + } + + return originalImplementation + }, + }) + tplConfig := plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + EmailDelivery: &emaildelivery.TypeInput{ + Service: tplSmtpService, + }, + } + testServer := supertokensInitForTest( + t, + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + EmailDelivery: &emaildelivery.TypeInput{ + Service: evSmtpService, + }, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(tplConfig), + ) + defer testServer.Close() + + 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.10", cdiVersion) == "2.10" { + return + } + + resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + bodyBytes, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + var response map[string]interface{} + json.Unmarshal(bodyBytes, &response) + + resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *userInputCode, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + sendRawEmailCalled = false // it would be true for the passwordless login, so reset it + + cookies := resp.Cookies() + resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + bodyBytes, err = ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + json.Unmarshal(bodyBytes, &response) + assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") + + // Default handler not called + assert.False(t, emailverification.EmailVerificationEmailSentForTest) + assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) + assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) + + // Custom handler not called + assert.Empty(t, email) + assert.Empty(t, emailVerifyLink) + assert.False(t, getContentCalled) + assert.False(t, sendRawEmailCalled) +} + +func TestCustomOverrideEmailVerifyForPasswordlessUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + customCalled := false + email := "" + emailVerifyLink := "" + + tplConfig := plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + } + testServer := supertokensInitForTest( + t, + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + EmailDelivery: &emaildelivery.TypeInput{ + Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { + sendEmail := *originalImplementation.SendEmail + *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + if input.EmailVerification != nil { + customCalled = true + email = input.EmailVerification.User.Email + emailVerifyLink = input.EmailVerification.EmailVerifyLink + return nil + } + return sendEmail(input, userContext) + } + return originalImplementation + }, + }, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(tplConfig), + ) + defer testServer.Close() + + 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.10", cdiVersion) == "2.10" { + return + } + + resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + bodyBytes, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + var response map[string]interface{} + json.Unmarshal(bodyBytes, &response) + + resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *PasswordlessLoginEmailDataForTest.UserInputCode, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + cookies := resp.Cookies() + resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + bodyBytes, err = ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + json.Unmarshal(bodyBytes, &response) + assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") + + // Default handler not called + assert.False(t, emailverification.EmailVerificationEmailSentForTest) + assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) + assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) + + // Custom handler not called + assert.Empty(t, email) + assert.Empty(t, emailVerifyLink) + assert.False(t, customCalled) +} + +func TestDefaultBackwardCompatibilityEmailVerifyForUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + tplConfig := plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ + Enabled: true, + }, + } + testServer := supertokensInitForTest( + t, + emailverification.Init(evmodels.TypeInput{Mode: evmodels.ModeOptional}), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(tplConfig), + ) + defer testServer.Close() + + 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.10", cdiVersion) == "2.10" { + return + } + + resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + bodyBytes, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + var response map[string]interface{} + json.Unmarshal(bodyBytes, &response) + + resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *PasswordlessLoginEmailDataForTest.UserInputCode, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + cookies := resp.Cookies() + resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) + + bodyBytes, err = ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + json.Unmarshal(bodyBytes, &response) + assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.False(t, emailverification.EmailVerificationEmailSentForTest) + assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) + assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) +} + func TestDefaultBackwardCompatibilityPasswordlessLogin(t *testing.T) { BeforeEach() unittesting.StartUpST("localhost", "8080") diff --git a/recipe/passwordless/recipe.go b/recipe/passwordless/recipe.go index 244a80d3..5fe8cd3a 100644 --- a/recipe/passwordless/recipe.go +++ b/recipe/passwordless/recipe.go @@ -118,10 +118,18 @@ func (r *Recipe) getAPIsHandled() ([]supertokens.APIHandled, error) { if err != nil { return nil, err } + doesEmailExistsAPINormalisedOld, err := supertokens.NewNormalisedURLPath(doesEmailExistAPIOld) + if err != nil { + return nil, err + } doesEmailExistsAPINormalised, err := supertokens.NewNormalisedURLPath(doesEmailExistAPI) if err != nil { return nil, err } + doesPhoneNumberExistsAPINormalisedOld, err := supertokens.NewNormalisedURLPath(doesPhoneNumberExistAPIOld) + if err != nil { + return nil, err + } doesPhoneNumberExistsAPINormalised, err := supertokens.NewNormalisedURLPath(doesPhoneNumberExistAPI) if err != nil { return nil, err @@ -141,11 +149,21 @@ func (r *Recipe) getAPIsHandled() ([]supertokens.APIHandled, error) { PathWithoutAPIBasePath: createCodeAPINormalised, ID: createCodeAPI, Disabled: r.APIImpl.CreateCodePOST == nil, + }, { + Method: http.MethodGet, + PathWithoutAPIBasePath: doesEmailExistsAPINormalisedOld, + ID: doesEmailExistAPIOld, + Disabled: r.APIImpl.EmailExistsGET == nil, }, { Method: http.MethodGet, PathWithoutAPIBasePath: doesEmailExistsAPINormalised, ID: doesEmailExistAPI, Disabled: r.APIImpl.EmailExistsGET == nil, + }, { + Method: http.MethodGet, + PathWithoutAPIBasePath: doesPhoneNumberExistsAPINormalisedOld, + ID: doesPhoneNumberExistAPIOld, + Disabled: r.APIImpl.PhoneNumberExistsGET == nil, }, { Method: http.MethodGet, PathWithoutAPIBasePath: doesPhoneNumberExistsAPINormalised, @@ -175,9 +193,9 @@ func (r *Recipe) handleAPIRequest(id string, tenantId string, req *http.Request, return api.ConsumeCode(r.APIImpl, tenantId, options, userContext) } else if id == createCodeAPI { return api.CreateCode(r.APIImpl, tenantId, options, userContext) - } else if id == doesEmailExistAPI { + } else if id == doesEmailExistAPIOld || id == doesEmailExistAPI { return api.DoesEmailExist(r.APIImpl, tenantId, options, userContext) - } else if id == doesPhoneNumberExistAPI { + } else if id == doesPhoneNumberExistAPIOld || id == doesPhoneNumberExistAPI { return api.DoesPhoneNumberExist(r.APIImpl, tenantId, options, userContext) } else { return api.ResendCode(r.APIImpl, tenantId, options, userContext) @@ -212,7 +230,6 @@ func (r *Recipe) CreateMagicLink(email *string, phoneNumber *string, tenantId st } link, err := api.GetMagicLink( stInstance.AppInfo, - r.RecipeModule.GetRecipeID(), response.OK.PreAuthSessionID, response.OK.LinkCode, tenantId, diff --git a/recipe/passwordless/recipeFucntions_test.go b/recipe/passwordless/recipeFucntions_test.go index 44fde356..acda5fb6 100644 --- a/recipe/passwordless/recipeFucntions_test.go +++ b/recipe/passwordless/recipeFucntions_test.go @@ -22,6 +22,8 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/supertokens/supertokens-golang/recipe/emailverification" + "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" "github.com/supertokens/supertokens-golang/recipe/session" "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" @@ -29,6 +31,173 @@ import ( "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestWithThirdPartyPasswordlessGetUserFunctionality(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(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ + Enabled: true, + }, + }), + }, + } + + 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 + } + + user, err := GetUserByID("random") + assert.NoError(t, err) + assert.Nil(t, user) + + resp, err := SignInUpByEmail("public", "test@example.com") + assert.NoError(t, err) + userId := resp.User.ID + + user, err = GetUserByID(userId) + assert.NoError(t, err) + assert.NotNil(t, user) + + assert.Equal(t, userId, user.ID) + assert.Equal(t, resp.User.Email, user.Email) + assert.Nil(t, user.PhoneNumber) + + users, err := GetUserByEmail("public", "random") + assert.NoError(t, err) + assert.Nil(t, users) + + users, err = GetUserByEmail("public", "test@example.com") + assert.NoError(t, err) + assert.NotNil(t, users) + + userInfo := users + + assert.Equal(t, user.Email, userInfo.Email) + assert.Equal(t, user.ID, userInfo.ID) + assert.Equal(t, user.PhoneNumber, userInfo.PhoneNumber) + assert.Nil(t, userInfo.PhoneNumber) + assert.Equal(t, user.TimeJoined, userInfo.TimeJoined) + + user, err = GetUserByPhoneNumber("public", "random") + assert.NoError(t, err) + assert.Nil(t, user) + + resp, err = SignInUpByPhoneNumber("public", "+1234567890") + assert.NoError(t, err) + + user, err = GetUserByPhoneNumber("public", *resp.User.PhoneNumber) + assert.NoError(t, err) + assert.NotNil(t, user) + + assert.Equal(t, user.Email, resp.User.Email) + assert.Equal(t, user.ID, resp.User.ID) + assert.Equal(t, user.PhoneNumber, resp.User.PhoneNumber) + assert.Nil(t, user.Email) +} + +func TestForPasswordlessUserThatIsEmailVerifiedReturnsTrueForPhoneAndFalseForEmail(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{ + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(plessmodels.TypeInput{ + FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ + Enabled: true, + }, + }), + }, + } + + 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 + } + + response, err := SignInUpByEmail("public", "test@example.com") + assert.NoError(t, err) + + isVerified, err := emailverification.IsEmailVerified(response.User.ID, nil) + assert.NoError(t, err) + assert.False(t, isVerified) // this is a change in behavior + + emailVerificationResp, err := emailverification.CreateEmailVerificationToken("public", response.User.ID, nil) + assert.NoError(t, err) + assert.Nil(t, emailVerificationResp.EmailAlreadyVerifiedError) + assert.NotNil(t, emailVerificationResp.OK) + + response, err = SignInUpByPhoneNumber("public", "+123456789012") + assert.NoError(t, err) + + isVerified, err = emailverification.IsEmailVerified(response.User.ID, nil) + assert.NoError(t, err) + assert.True(t, isVerified) + + emailVerificationResp, err = emailverification.CreateEmailVerificationToken("public", response.User.ID, nil) + assert.NoError(t, err) + assert.NotNil(t, emailVerificationResp.EmailAlreadyVerifiedError) + assert.Nil(t, emailVerificationResp.OK) +} + func TestGetUser(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ @@ -875,7 +1044,7 @@ func TestCreatingMagicLink(t *testing.T) { assert.Equal(t, "supertokens.io", res.Host) assert.Equal(t, "/auth/verify", res.Path) - assert.Equal(t, "passwordless", res.Query().Get("rid")) + assert.Equal(t, "", res.Query().Get("rid")) } func TestSignInUp(t *testing.T) { diff --git a/recipe/thirdparty/authorisationUrlFeature_test.go b/recipe/thirdparty/authorisationUrlFeature_test.go index 68c06561..4e0c0ac1 100644 --- a/recipe/thirdparty/authorisationUrlFeature_test.go +++ b/recipe/thirdparty/authorisationUrlFeature_test.go @@ -25,11 +25,176 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" "github.com/supertokens/supertokens-golang/supertokens" "github.com/supertokens/supertokens-golang/test/unittesting" ) +func TestReqWithThirdPartyEmailPasswordRecipe(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{ + emailpassword.Init(nil), + Init( + &tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }, + ), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + + if err != nil { + t.Error(err.Error()) + } + + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + client := &http.Client{} + req, _ := http.NewRequest("GET", testServer.URL+"/auth/authorisationurl?thirdPartyId=google", nil) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("rid", "thirdpartyemailpassword") + + resp, err := client.Do(req) + + if err != nil { + t.Error(err.Error()) + } + + dataInBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Error(err.Error()) + } + resp.Body.Close() + + var data map[string]interface{} + err = json.Unmarshal(dataInBytes, &data) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", data["status"]) + + fetchedUrl, err := url.Parse(data["urlWithQueryParams"].(string)) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "supertokens.io", fetchedUrl.Host) + assert.Equal(t, "/dev/oauth/redirect-to-provider", fetchedUrl.Path) +} + +func TestReqWithThirdPartyEmailPasswordRecipe2(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( + &tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + { + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "google", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "4398792-test-id", + ClientSecret: "test-secret", + }, + }, + }, + }, + }, + }, + }, + ), + emailpassword.Init(nil), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + + if err != nil { + t.Error(err.Error()) + } + + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + client := &http.Client{} + req, _ := http.NewRequest("GET", testServer.URL+"/auth/authorisationurl?thirdPartyId=google", nil) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("rid", "thirdpartyemailpassword") + + resp, err := client.Do(req) + + if err != nil { + t.Error(err.Error()) + } + + dataInBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Error(err.Error()) + } + resp.Body.Close() + + var data map[string]interface{} + err = json.Unmarshal(dataInBytes, &data) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "OK", data["status"]) + + fetchedUrl, err := url.Parse(data["urlWithQueryParams"].(string)) + if err != nil { + t.Error(err.Error()) + } + + assert.Equal(t, "supertokens.io", fetchedUrl.Host) + assert.Equal(t, "/dev/oauth/redirect-to-provider", fetchedUrl.Path) +} + func TestUsingDevOAuthKeysWillUseDevAuthUrl(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ diff --git a/recipe/thirdparty/email_test.go b/recipe/thirdparty/email_test.go new file mode 100644 index 00000000..6812930d --- /dev/null +++ b/recipe/thirdparty/email_test.go @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2022, 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 thirdparty + +import ( + "bytes" + "encoding/json" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" + "github.com/supertokens/supertokens-golang/recipe/emailpassword" + "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" + "github.com/supertokens/supertokens-golang/recipe/emailverification" + "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" + "github.com/supertokens/supertokens-golang/recipe/passwordless/emaildelivery/smtpService" + "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/supertokens" + "github.com/supertokens/supertokens-golang/test/unittesting" +) + +func TestSMTPOverrideEmailVerifyForThirdpartyUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + getContentCalled := false + sendRawEmailCalled := false + email := "" + emailVerifyLink := "" + + smtpService := smtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ + Settings: emaildelivery.SMTPSettings{ + Host: "", + From: emaildelivery.SMTPFrom{ + Name: "Test User", + Email: "", + }, + Port: 123, + Password: "", + }, + Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { + (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { + if input.EmailVerification != nil { + email = input.EmailVerification.User.Email + emailVerifyLink = input.EmailVerification.EmailVerifyLink + getContentCalled = true + } + return emaildelivery.EmailContent{}, nil + } + + (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { + sendRawEmailCalled = true + return nil + } + + return originalImplementation + }, + }) + tplConfig := tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{customProviderForEmailVerification}, + }, + } + testServer := supertokensInitForTest( + t, + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + EmailDelivery: &emaildelivery.TypeInput{ + Service: smtpService, + }, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tplConfig), + ) + defer testServer.Close() + + signinupPostData := PostDataForCustomProvider{ + ThirdPartyId: "custom", + OAuthTokens: map[string]interface{}{ + "access_token": "saodiasjodai", + }, + } + + postBody, err := json.Marshal(signinupPostData) + resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + + cookies := resp.Cookies() + resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Default handler not called + assert.False(t, emailverification.EmailVerificationEmailSentForTest) + assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) + assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) + + assert.Equal(t, email, "test@example.com") + assert.NotEmpty(t, emailVerifyLink) + assert.Equal(t, getContentCalled, true) + assert.Equal(t, sendRawEmailCalled, true) +} + +func TestCustomOverrideEmailVerifyForThirdpartyUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + customCalled := false + email := "" + emailVerifyLink := "" + + tplConfig := tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{customProviderForEmailVerification}, + }, + } + testServer := supertokensInitForTest( + t, + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + EmailDelivery: &emaildelivery.TypeInput{ + Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { + sendEmail := *originalImplementation.SendEmail + *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + if input.EmailVerification != nil { + customCalled = true + email = input.EmailVerification.User.Email + emailVerifyLink = input.EmailVerification.EmailVerifyLink + return nil + } + return sendEmail(input, userContext) + } + return originalImplementation + }, + }, + }), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tplConfig), + ) + defer testServer.Close() + + signinupPostData := PostDataForCustomProvider{ + ThirdPartyId: "custom", + OAuthTokens: map[string]interface{}{ + "access_token": "saodiasjodai", + }, + } + + postBody, err := json.Marshal(signinupPostData) + resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + + cookies := resp.Cookies() + resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Default handler not called + assert.False(t, emailverification.EmailVerificationEmailSentForTest) + assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) + assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) + + // Custom handler called + assert.Equal(t, email, "test@example.com") + assert.NotEmpty(t, emailVerifyLink) + assert.True(t, customCalled) +} + +func TestDefaultBackwardCompatibilityEmailVerifyForThirdpartyUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + tplConfig := tpmodels.TypeInput{ + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + customProviderForEmailVerification, + }, + }, + } + testServer := supertokensInitForTest( + t, + emailverification.Init(evmodels.TypeInput{Mode: evmodels.ModeOptional}), + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(&tplConfig), + ) + defer testServer.Close() + + signinupPostData := PostDataForCustomProvider{ + ThirdPartyId: "custom", + OAuthTokens: map[string]interface{}{ + "access_token": "saodiasjodai", + }, + } + + postBody, err := json.Marshal(signinupPostData) + resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) + assert.NoError(t, err) + + cookies := resp.Cookies() + + resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.True(t, emailverification.EmailVerificationEmailSentForTest) + assert.Equal(t, emailverification.EmailVerificationDataForTest.User.Email, "test@example.com") + assert.NotEmpty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) +} + +func TestDefaultBackwardCompatibilityPasswordResetForThirdpartyUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + testServer := supertokensInitForTest( + t, + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(nil), + emailpassword.Init(nil), + ) + defer testServer.Close() + + ManuallyCreateOrUpdateUser("public", "custom", "user-id", "test@example.com") + resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.False(t, emailpassword.PasswordResetEmailSentForTest) + assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) + assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) +} + +func TestCustomOverrideResetPasswordForThirdpartyUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + customCalled := false + email := "" + passwordResetLink := "" + + epConfig := &epmodels.TypeInput{ + EmailDelivery: &emaildelivery.TypeInput{ + Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { + *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { + if input.PasswordReset != nil { + customCalled = true + email = input.PasswordReset.User.Email + passwordResetLink = input.PasswordReset.PasswordResetLink + } + return nil + } + return originalImplementation + }, + }, + } + testServer := supertokensInitForTest( + t, + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(nil), + emailpassword.Init(epConfig), + ) + defer testServer.Close() + + ManuallyCreateOrUpdateUser("public", "custom", "user-id", "test@example.com") + resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Default handler not called + assert.False(t, emailpassword.PasswordResetEmailSentForTest) + assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) + assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) + + // Custom handler not called + assert.Empty(t, email) + assert.Empty(t, passwordResetLink) + assert.False(t, customCalled) +} + +func TestSMTPOverridePasswordResetForThirdpartyUser(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + + getContentCalled := false + sendRawEmailCalled := false + email := "" + passwordResetLink := "" + + smtpService := emailpassword.MakeSMTPService(emaildelivery.SMTPServiceConfig{ + Settings: emaildelivery.SMTPSettings{ + Host: "", + From: emaildelivery.SMTPFrom{ + Name: "Test User", + Email: "", + }, + Port: 123, + Password: "", + }, + Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { + (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { + if input.PasswordReset != nil { + email = input.PasswordReset.User.Email + passwordResetLink = input.PasswordReset.PasswordResetLink + getContentCalled = true + } + return emaildelivery.EmailContent{}, nil + } + + (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { + sendRawEmailCalled = true + return nil + } + + return originalImplementation + }, + }) + epConfig := &epmodels.TypeInput{ + EmailDelivery: &emaildelivery.TypeInput{ + Service: smtpService, + }, + } + testServer := supertokensInitForTest( + t, + session.Init(&sessmodels.TypeInput{ + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + Init(nil), + emailpassword.Init(epConfig), + ) + defer testServer.Close() + + ManuallyCreateOrUpdateUser("public", "custom", "user-id", "test@example.com") + resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Default handler not called + assert.False(t, emailpassword.PasswordResetEmailSentForTest) + assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) + assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) + + // Custom handler not called + assert.Empty(t, email) + assert.Empty(t, passwordResetLink) + assert.False(t, getContentCalled) + assert.False(t, sendRawEmailCalled) +} 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/thirdparty/signinupFeature_test.go b/recipe/thirdparty/signinupFeature_test.go index 589ec9bc..b6fe46b8 100644 --- a/recipe/thirdparty/signinupFeature_test.go +++ b/recipe/thirdparty/signinupFeature_test.go @@ -35,6 +35,75 @@ import ( "gopkg.in/h2non/gock.v1" ) +func TestThatThirdPartyUserThatIsEmailVerifiedReturnsTheCorrectEmailVerificationStatus(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{ + emailverification.Init(evmodels.TypeInput{ + Mode: evmodels.ModeOptional, + }), + 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{ + customProvider1, + }, + }, + }), + }, + } + + 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 + } + + resp, err := ManuallyCreateOrUpdateUser("public", "custom", "verifiedUser", "test@example.com") + assert.NoError(t, err) + + emailVerificationToken, err := emailverification.CreateEmailVerificationToken("public", resp.OK.User.ID, nil) + assert.NoError(t, err) + + emailverification.VerifyEmailUsingToken("public", emailVerificationToken.OK.Token) + + isVerfied, err := emailverification.IsEmailVerified(resp.OK.User.ID, nil) + assert.NoError(t, err) + assert.True(t, isVerfied) + + resp1, err := ManuallyCreateOrUpdateUser("public", "custom2", "NotVerifiedUser", "test@example.com") + assert.NoError(t, err) + + isVerfied1, err := emailverification.IsEmailVerified(resp1.OK.User.ID, nil) + assert.NoError(t, err) + assert.False(t, isVerfied1) +} + func TestWithDisabledAPIDefaultSigninupAPIdoesnNotWork(t *testing.T) { configValue := supertokens.TypeInput{ Supertokens: &supertokens.ConnectionInfo{ @@ -671,6 +740,87 @@ func TestInvalidPostParamsForThirdPartyModule(t *testing.T) { assert.Equal(t, "Please provide one of redirectURIInfo or oAuthTokens in the request body", response2["message"]) } +func TestErrorThrownFromGetProfileInfoFunction(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{ + signinupCustomProvider4, + }, + }, + }), + }, + } + + 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() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + defer gock.OffAll() + gock.New("https://test.com/"). + Post("oauth/token"). + Reply(200). + JSON(map[string]string{"access_token": "abcdefghj"}) + + postData := map[string]interface{}{ + "thirdPartyId": "custom", + "redirectURIInfo": map[string]interface{}{ + "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", + "redirectURIQueryParams": map[string]interface{}{ + "code": "abcdefghj", + }, + }, + } + + postBody, err := json.Marshal(postData) + 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)) + if err != nil { + t.Error(err.Error()) + } + assert.Equal(t, 500, resp.StatusCode) +} + func TestEmailNotReturnedInGetProfileInfoFunction(t *testing.T) { customAntiCsrfValue := "VIA_TOKEN" configValue := supertokens.TypeInput{ @@ -969,3 +1119,106 @@ func TestGetUserByThirdPartyInfoWhenUserDoesNotExist(t *testing.T) { assert.Equal(t, userInfoAfterSignup.ID, user["id"].(string)) assert.Equal(t, userInfoAfterSignup.Email, user["email"].(string)) } + +func TestHandlePostSignUpInGetsSetCorrectly(t *testing.T) { + userId := "" + loginType := "" + customAntiCsrfVal := "VIA_TOKEN" + 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(&tpmodels.TypeInput{ + Override: &tpmodels.OverrideStruct{ + APIs: func(originalImplementation tpmodels.APIInterface) tpmodels.APIInterface { + originalSignInUpPost := *originalImplementation.SignInUpPOST + *originalImplementation.SignInUpPOST = func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.SignInUpPOSTResponse, error) { + resp, err := originalSignInUpPost(provider, input, tenantId, options, userContext) + if err != nil { + t.Error(err.Error()) + } + userId = resp.OK.User.ID + loginType = "thirdparty" + return resp, err + } + return originalImplementation + }, + }, + SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ + Providers: []tpmodels.ProviderInput{ + customProvider2, + }, + }, + }), + session.Init(&sessmodels.TypeInput{ + AntiCsrf: &customAntiCsrfVal, + GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { + return sessmodels.CookieTransferMethod + }, + }), + }, + } + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(configValue) + if err != nil { + t.Error(err.Error()) + } + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + defer gock.OffAll() + gock.New("https://test.com/"). + Post("oauth/token"). + Reply(200). + JSON(map[string]string{"access_token": "abcdefghj", "email": "test@example.com"}) + + postData := map[string]interface{}{ + "thirdPartyId": "custom", + "redirectURIInfo": map[string]interface{}{ + "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", + "redirectURIQueryParams": map[string]interface{}{ + "code": "abcdefghj", + }, + }, + } + + postBody, err := json.Marshal(postData) + 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)) + if err != nil { + t.Error(err.Error()) + } + assert.Equal(t, http.StatusOK, resp.StatusCode) + dataInBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Error(err.Error()) + } + resp.Body.Close() + + var result map[string]interface{} + + err = json.Unmarshal(dataInBytes, &result) + if err != nil { + t.Error(err.Error()) + } + + user := result["user"].(map[string]interface{}) + + assert.Equal(t, userId, user["id"]) + assert.Equal(t, "thirdparty", loginType) +} diff --git a/recipe/thirdparty/testingUtils.go b/recipe/thirdparty/testingUtils.go index f3bf239d..a39b6e78 100644 --- a/recipe/thirdparty/testingUtils.go +++ b/recipe/thirdparty/testingUtils.go @@ -16,6 +16,7 @@ package thirdparty import ( + "errors" "net/http" "net/http/httptest" "testing" @@ -179,7 +180,7 @@ var customProvider2 = tpmodels.ProviderInput{ Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { return tpmodels.TypeUserInfo{ - ThirdPartyUserId: oAuthTokens["id"].(string), + ThirdPartyUserId: oAuthTokens["access_token"].(string), Email: &tpmodels.EmailStruct{ ID: oAuthTokens["email"].(string), IsVerified: true, @@ -239,3 +240,22 @@ var customProvider3 = tpmodels.ProviderInput{ return originalImplementation }, } + +var signinupCustomProvider4 = tpmodels.ProviderInput{ + Config: tpmodels.ProviderConfig{ + ThirdPartyId: "custom", + Clients: []tpmodels.ProviderClientConfig{ + { + ClientID: "supertokens", + }, + }, + AuthorizationEndpoint: "https://test.com/oauth/auth", + TokenEndpoint: "https://test.com/oauth/token", + }, + Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { + originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { + return tpmodels.TypeUserInfo{}, errors.New("error from getUserInfo") + } + return originalImplementation + }, +} diff --git a/recipe/thirdpartyemailpassword/api/emailPasswordAPIImplementation.go b/recipe/thirdpartyemailpassword/api/emailPasswordAPIImplementation.go deleted file mode 100644 index eb8a1531..00000000 --- a/recipe/thirdpartyemailpassword/api/emailPasswordAPIImplementation.go +++ /dev/null @@ -1,103 +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 api - -import ( - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func GetEmailPasswordIterfaceImpl(apiImplmentation tpepmodels.APIInterface) epmodels.APIInterface { - - result := epmodels.APIInterface{ - EmailExistsGET: apiImplmentation.EmailPasswordEmailExistsGET, - GeneratePasswordResetTokenPOST: apiImplmentation.GeneratePasswordResetTokenPOST, - PasswordResetPOST: apiImplmentation.PasswordResetPOST, - SignInPOST: nil, - SignUpPOST: nil, - } - - if apiImplmentation.EmailPasswordSignInPOST != nil && (*apiImplmentation.EmailPasswordSignInPOST) != nil { - signInPOST := func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.SignInPOSTResponse, error) { - result, err := (*apiImplmentation.EmailPasswordSignInPOST)(formFields, tenantId, options, userContext) - if err != nil { - return epmodels.SignInPOSTResponse{}, err - } - if result.OK != nil { - return epmodels.SignInPOSTResponse{ - OK: &struct { - User epmodels.User - Session sessmodels.SessionContainer - }{ - - User: epmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - }, - Session: result.OK.Session, - }, - }, nil - } else if result.WrongCredentialsError != nil { - return epmodels.SignInPOSTResponse{ - WrongCredentialsError: &struct{}{}, - }, nil - } else { - return epmodels.SignInPOSTResponse{ - GeneralError: result.GeneralError, - }, nil - } - } - result.SignInPOST = &signInPOST - } - - if apiImplmentation.EmailPasswordSignUpPOST != nil && (*apiImplmentation.EmailPasswordSignUpPOST) != nil { - signUpPOST := func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.SignUpPOSTResponse, error) { - result, err := (*apiImplmentation.EmailPasswordSignUpPOST)(formFields, tenantId, options, userContext) - if err != nil { - return epmodels.SignUpPOSTResponse{}, err - } - if result.OK != nil { - return epmodels.SignUpPOSTResponse{ - OK: &struct { - User epmodels.User - Session sessmodels.SessionContainer - }{ - User: epmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - }, - Session: result.OK.Session, - }, - }, nil - } else if result.EmailAlreadyExistsError != nil { - return epmodels.SignUpPOSTResponse{ - EmailAlreadyExistsError: &struct{}{}, - }, nil - } else { - return epmodels.SignUpPOSTResponse{ - GeneralError: result.GeneralError, - }, nil - } - } - result.SignUpPOST = &signUpPOST - } - - return result -} diff --git a/recipe/thirdpartyemailpassword/api/implementation.go b/recipe/thirdpartyemailpassword/api/implementation.go deleted file mode 100644 index b1b24bb2..00000000 --- a/recipe/thirdpartyemailpassword/api/implementation.go +++ /dev/null @@ -1,185 +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 api - -import ( - epapi "github.com/supertokens/supertokens-golang/recipe/emailpassword/api" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - tpapi "github.com/supertokens/supertokens-golang/recipe/thirdparty/api" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeAPIImplementation() tpepmodels.APIInterface { - emailPasswordImplementation := epapi.MakeAPIImplementation() - thirdPartyImplementation := tpapi.MakeAPIImplementation() - - ogEmailExistsGET := *emailPasswordImplementation.EmailExistsGET - emailExistsGET := func(email string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.EmailExistsGETResponse, error) { - return ogEmailExistsGET(email, tenantId, options, userContext) - - } - - ogGeneratePasswordResetTokenPOST := *emailPasswordImplementation.GeneratePasswordResetTokenPOST - generatePasswordResetTokenPOST := func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.GeneratePasswordResetTokenPOSTResponse, error) { - return ogGeneratePasswordResetTokenPOST(formFields, tenantId, options, userContext) - } - - ogPasswordResetPOST := *emailPasswordImplementation.PasswordResetPOST - passwordResetPOST := func(formFields []epmodels.TypeFormField, token string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.ResetPasswordPOSTResponse, error) { - return ogPasswordResetPOST(formFields, token, tenantId, options, userContext) - } - - ogSignInPOST := *emailPasswordImplementation.SignInPOST - emailPasswordSignInPOST := func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.SignInPOSTResponse, error) { - response, err := ogSignInPOST(formFields, tenantId, options, userContext) - if err != nil { - return tpepmodels.SignInPOSTResponse{}, err - } - if response.OK != nil { - return tpepmodels.SignInPOSTResponse{ - OK: &struct { - User tpepmodels.User - Session sessmodels.SessionContainer - }{ - User: tpepmodels.User{ - ID: response.OK.User.ID, - Email: response.OK.User.Email, - TimeJoined: response.OK.User.TimeJoined, - ThirdParty: nil, - }, - Session: response.OK.Session, - }, - }, nil - } else if response.WrongCredentialsError != nil { - return tpepmodels.SignInPOSTResponse{ - WrongCredentialsError: &struct{}{}, - }, nil - } else { - return tpepmodels.SignInPOSTResponse{ - GeneralError: response.GeneralError, - }, nil - } - } - - ogSignUpPOST := *emailPasswordImplementation.SignUpPOST - emailPasswordSignUpPOST := func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.SignUpPOSTResponse, error) { - response, err := ogSignUpPOST(formFields, tenantId, options, userContext) - if err != nil { - return tpepmodels.SignUpPOSTResponse{}, err - } - if response.OK != nil { - return tpepmodels.SignUpPOSTResponse{ - OK: &struct { - User tpepmodels.User - Session sessmodels.SessionContainer - }{ - User: tpepmodels.User{ - ID: response.OK.User.ID, - Email: response.OK.User.Email, - TimeJoined: response.OK.User.TimeJoined, - ThirdParty: nil, - }, - Session: response.OK.Session, - }, - }, nil - } else if response.EmailAlreadyExistsError != nil { - return tpepmodels.SignUpPOSTResponse{ - EmailAlreadyExistsError: &struct{}{}, - }, nil - } else { - return tpepmodels.SignUpPOSTResponse{ - GeneralError: response.GeneralError, - }, nil - } - } - - ogSignInUpPOST := *thirdPartyImplementation.SignInUpPOST - thirdPartySignInUpPOST := func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.ThirdPartySignInUpPOSTResponse, error) { - response, err := ogSignInUpPOST(provider, input, tenantId, options, userContext) - if err != nil { - return tpepmodels.ThirdPartySignInUpPOSTResponse{}, err - } - if response.GeneralError != nil { - return tpepmodels.ThirdPartySignInUpPOSTResponse{ - GeneralError: response.GeneralError, - }, nil - } else if response.NoEmailGivenByProviderError != nil { - return tpepmodels.ThirdPartySignInUpPOSTResponse{ - NoEmailGivenByProviderError: &struct{}{}, - }, nil - } else { - return tpepmodels.ThirdPartySignInUpPOSTResponse{ - OK: &struct { - CreatedNewUser bool - User tpepmodels.User - Session *sessmodels.TypeSessionContainer - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: response.OK.CreatedNewUser, - User: tpepmodels.User{ - ID: response.OK.User.ID, - TimeJoined: response.OK.User.TimeJoined, - Email: response.OK.User.Email, - ThirdParty: &response.OK.User.ThirdParty, - }, - Session: response.OK.Session, - OAuthTokens: response.OK.OAuthTokens, - RawUserInfoFromProvider: response.OK.RawUserInfoFromProvider, - }, - }, nil - } - } - - ogAuthorisationUrlGET := *thirdPartyImplementation.AuthorisationUrlGET - authorisationUrlGET := func(provider *tpmodels.TypeProvider, redirectURIOnProviderDashboard string, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.AuthorisationUrlGETResponse, error) { - return ogAuthorisationUrlGET(provider, redirectURIOnProviderDashboard, tenantId, options, userContext) - } - - ogAppleRedirectHandlerPOST := *thirdPartyImplementation.AppleRedirectHandlerPOST - appleRedirectHandlerPOST := func(formPostInfoFromProvider map[string]interface{}, options tpmodels.APIOptions, userContext supertokens.UserContext) error { - return ogAppleRedirectHandlerPOST(formPostInfoFromProvider, options, userContext) - } - - result := tpepmodels.APIInterface{ - AuthorisationUrlGET: &authorisationUrlGET, - ThirdPartySignInUpPOST: &thirdPartySignInUpPOST, - AppleRedirectHandlerPOST: &appleRedirectHandlerPOST, - - EmailPasswordEmailExistsGET: &emailExistsGET, - GeneratePasswordResetTokenPOST: &generatePasswordResetTokenPOST, - PasswordResetPOST: &passwordResetPOST, - EmailPasswordSignInPOST: &emailPasswordSignInPOST, - EmailPasswordSignUpPOST: &emailPasswordSignUpPOST, - } - - modifiedEP := GetEmailPasswordIterfaceImpl(result) - (*emailPasswordImplementation.EmailExistsGET) = *modifiedEP.EmailExistsGET - (*emailPasswordImplementation.GeneratePasswordResetTokenPOST) = *modifiedEP.GeneratePasswordResetTokenPOST - (*emailPasswordImplementation.PasswordResetPOST) = *modifiedEP.PasswordResetPOST - (*emailPasswordImplementation.SignInPOST) = *modifiedEP.SignInPOST - (*emailPasswordImplementation.SignUpPOST) = *modifiedEP.SignUpPOST - - modifiedTP := GetThirdPartyIterfaceImpl(result) - (*thirdPartyImplementation.AuthorisationUrlGET) = *modifiedTP.AuthorisationUrlGET - (*thirdPartyImplementation.SignInUpPOST) = *modifiedTP.SignInUpPOST - (*thirdPartyImplementation.AppleRedirectHandlerPOST) = *modifiedTP.AppleRedirectHandlerPOST - - return result -} diff --git a/recipe/thirdpartyemailpassword/api/thirdPartyAPIImplementation.go b/recipe/thirdpartyemailpassword/api/thirdPartyAPIImplementation.go deleted file mode 100644 index a3dbdc5b..00000000 --- a/recipe/thirdpartyemailpassword/api/thirdPartyAPIImplementation.go +++ /dev/null @@ -1,78 +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 api - -import ( - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func GetThirdPartyIterfaceImpl(apiImplmentation tpepmodels.APIInterface) tpmodels.APIInterface { - if apiImplmentation.ThirdPartySignInUpPOST == nil || (*apiImplmentation.ThirdPartySignInUpPOST) == nil { - return tpmodels.APIInterface{ - AuthorisationUrlGET: apiImplmentation.AuthorisationUrlGET, - AppleRedirectHandlerPOST: apiImplmentation.AppleRedirectHandlerPOST, - SignInUpPOST: nil, - } - } - - signInUpPOST := func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.SignInUpPOSTResponse, error) { - result, err := (*apiImplmentation.ThirdPartySignInUpPOST)(provider, input, tenantId, options, userContext) - if err != nil { - return tpmodels.SignInUpPOSTResponse{}, err - } - - if result.OK != nil { - return tpmodels.SignInUpPOSTResponse{ - OK: &struct { - CreatedNewUser bool - User tpmodels.User - Session *sessmodels.TypeSessionContainer - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - TenantIds: result.OK.User.TenantIds, - TimeJoined: result.OK.User.TimeJoined, - ThirdParty: *result.OK.User.ThirdParty, - }, - Session: result.OK.Session, - OAuthTokens: result.OK.OAuthTokens, - RawUserInfoFromProvider: result.OK.RawUserInfoFromProvider, - }, - }, nil - } else if result.NoEmailGivenByProviderError != nil { - return tpmodels.SignInUpPOSTResponse{ - NoEmailGivenByProviderError: &struct{}{}, - }, nil - } else { - return tpmodels.SignInUpPOSTResponse{ - GeneralError: result.GeneralError, - }, nil - } - } - - return tpmodels.APIInterface{ - AuthorisationUrlGET: apiImplmentation.AuthorisationUrlGET, - AppleRedirectHandlerPOST: apiImplmentation.AppleRedirectHandlerPOST, - SignInUpPOST: &signInUpPOST, - } -} diff --git a/recipe/thirdpartyemailpassword/authorisationUrlFeature_test.go b/recipe/thirdpartyemailpassword/authorisationUrlFeature_test.go deleted file mode 100644 index 84487fd0..00000000 --- a/recipe/thirdpartyemailpassword/authorisationUrlFeature_test.go +++ /dev/null @@ -1,164 +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 thirdpartyemailpassword - -import ( - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "net/url" - "testing" - - "github.com/stretchr/testify/assert" - "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/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestMinimumConfigForThirdPartyModule(t *testing.T) { - customAntiCsrfVal := "VIA_TOKEN" - 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( - &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProvider1, - }, - }, - ), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customAntiCsrfVal, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - - if err != nil { - t.Error(err.Error()) - } - - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := http.Get(testServer.URL + "/auth/authorisationurl?thirdPartyId=custom") - if err != nil { - t.Error(err.Error()) - } - - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - resp.Body.Close() - - var data map[string]interface{} - err = json.Unmarshal(dataInBytes, &data) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", data["status"]) - - fetchedUrl, err := url.Parse(data["urlWithQueryParams"].(string)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, "test.com", fetchedUrl.Host) - assert.Equal(t, "/oauth/auth", fetchedUrl.Path) -} - -func TestThirdPartyProviderDoesnotExsit(t *testing.T) { - customAntiCsrfVal := "VIA_TOKEN" - 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( - &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProvider1, - }, - }, - ), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customAntiCsrfVal, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - - if err != nil { - t.Error(err.Error()) - } - - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := http.Get(testServer.URL + "/auth/authorisationurl?thirdPartyId=google") - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - resp.Body.Close() - - var data map[string]interface{} - err = json.Unmarshal(dataInBytes, &data) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "the provider google could not be found in the configuration", data["message"].(string)) -} diff --git a/recipe/thirdpartyemailpassword/config_test.go b/recipe/thirdpartyemailpassword/config_test.go deleted file mode 100644 index 43d5e844..00000000 --- a/recipe/thirdpartyemailpassword/config_test.go +++ /dev/null @@ -1,142 +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 thirdpartyemailpassword - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestDefaultConfigForThirdPartyEmailPasswordRecipe(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( - &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{}, - }, - ), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - - if err != nil { - t.Error(err.Error()) - } - - thirdpartyemailpassword, err := GetRecipeInstanceOrThrowError() - if err != nil { - t.Error(err.Error()) - } - - assert.NotNil(t, thirdpartyemailpassword.thirdPartyRecipe) // thirdPartyRecipe must be created always - - emailpassword := thirdpartyemailpassword.emailPasswordRecipe - signUpFeature := emailpassword.Config.SignUpFeature - assert.Equal(t, 2, len(signUpFeature.FormFields)) - for _, formField := range signUpFeature.FormFields { - assert.False(t, formField.Optional) - assert.NotNil(t, formField.Validate) - } - - signInFeature := emailpassword.Config.SignInFeature - assert.Equal(t, 2, len(signInFeature.FormFields)) - for _, formField := range signInFeature.FormFields { - assert.False(t, formField.Optional) - assert.NotNil(t, formField.Validate) - } - - resetPasswordUsingTokenFeature := emailpassword.Config.ResetPasswordUsingTokenFeature - assert.Equal(t, 1, len(resetPasswordUsingTokenFeature.FormFieldsForGenerateTokenForm)) - assert.Equal(t, "email", resetPasswordUsingTokenFeature.FormFieldsForGenerateTokenForm[0].ID) - assert.Equal(t, 1, len(resetPasswordUsingTokenFeature.FormFieldsForPasswordResetForm)) - assert.Equal(t, "password", resetPasswordUsingTokenFeature.FormFieldsForPasswordResetForm[0].ID) -} - -func TestDefaultConfigForThirdPartyEmailPasswordRecipeWithProvider(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( - &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProvider2, - }, - }, - ), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - - if err != nil { - t.Error(err.Error()) - } - - thirdpartyemailpassword, err := GetRecipeInstanceOrThrowError() - if err != nil { - t.Error(err.Error()) - } - - assert.NotNil(t, thirdpartyemailpassword.thirdPartyRecipe) - - emailpassword := thirdpartyemailpassword.emailPasswordRecipe - signUpFeature := emailpassword.Config.SignUpFeature - assert.Equal(t, 2, len(signUpFeature.FormFields)) - for _, formField := range signUpFeature.FormFields { - assert.False(t, formField.Optional) - assert.NotNil(t, formField.Validate) - } - - signInFeature := emailpassword.Config.SignInFeature - assert.Equal(t, 2, len(signInFeature.FormFields)) - for _, formField := range signInFeature.FormFields { - assert.False(t, formField.Optional) - assert.NotNil(t, formField.Validate) - } - - resetPasswordUsingTokenFeature := emailpassword.Config.ResetPasswordUsingTokenFeature - assert.Equal(t, 1, len(resetPasswordUsingTokenFeature.FormFieldsForGenerateTokenForm)) - assert.Equal(t, "email", resetPasswordUsingTokenFeature.FormFieldsForGenerateTokenForm[0].ID) - assert.Equal(t, 1, len(resetPasswordUsingTokenFeature.FormFieldsForPasswordResetForm)) - assert.Equal(t, "password", resetPasswordUsingTokenFeature.FormFieldsForPasswordResetForm[0].ID) -} diff --git a/recipe/thirdpartyemailpassword/emailVerify_test.go b/recipe/thirdpartyemailpassword/emailVerify_test.go deleted file mode 100644 index d2c5cc7d..00000000 --- a/recipe/thirdpartyemailpassword/emailVerify_test.go +++ /dev/null @@ -1,250 +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 thirdpartyemailpassword - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" - "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestTheGenerateTokenAPIwithValidInputEmailNotVerified(t *testing.T) { - customCSRFval := "VIA_TOKEN" - 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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - Init(nil), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customCSRFval, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("random@gmail.com", "validPass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - - cookieData := unittesting.ExtractInfoFromResponse(resp) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "OK", result["status"]) - user := result["user"].(map[string]interface{}) - - rep1, err := unittesting.EmailVerifyTokenRequest(testServer.URL, user["id"].(string), cookieData["sAccessToken"], cookieData["antiCsrf"]) - if err != nil { - t.Error(err.Error()) - } - result1 := *unittesting.HttpResponseToConsumableInformation(rep1.Body) - assert.Equal(t, "OK", result1["status"]) -} - -func TestGenerateTokenAPIwithValidInputEmailVerifiedAndTestError(t *testing.T) { - customCSRFval := "VIA_TOKEN" - 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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - Init(nil), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customCSRFval, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("random@gmail.com", "validPass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - - cookieData := unittesting.ExtractInfoFromResponse(resp) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "OK", result["status"]) - user := result["user"].(map[string]interface{}) - - verifyToken, err := emailverification.CreateEmailVerificationToken("public", user["id"].(string), nil) - if err != nil { - t.Error(err.Error()) - } - emailverification.VerifyEmailUsingToken("public", verifyToken.OK.Token) - - rep1, err := unittesting.EmailVerifyTokenRequest(testServer.URL, user["id"].(string), cookieData["sAccessToken"], cookieData["antiCsrf"]) - if err != nil { - t.Error(err.Error()) - } - result1 := *unittesting.HttpResponseToConsumableInformation(rep1.Body) - assert.Equal(t, "EMAIL_ALREADY_VERIFIED_ERROR", result1["status"]) -} - -func TestGenerateTokenAPIWithValidInputNoSessionAndCheckOutput(t *testing.T) { - customCSRFval := "VIA_TOKEN" - 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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - Init(nil), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customCSRFval, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := http.Post(testServer.URL+"/auth/user/email/verify/token", "application/json", nil) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "unauthorised", result["message"]) -} - -func TestEmailVerifyAPIwithInvalidTokenCheckError(t *testing.T) { - customCSRFval := "VIA_TOKEN" - 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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - Init(nil), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customCSRFval, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - formFields := map[string]string{ - "method": "token", - "token": "randomToken", - } - - postBody, err := json.Marshal(formFields) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/user/email/verify", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR", result["status"]) -} diff --git a/recipe/thirdpartyemailpassword/emaildelivery/backwardCompatibilityService/main.go b/recipe/thirdpartyemailpassword/emaildelivery/backwardCompatibilityService/main.go deleted file mode 100644 index 4d79995a..00000000 --- a/recipe/thirdpartyemailpassword/emaildelivery/backwardCompatibilityService/main.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022, 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 backwardCompatibilityService - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - emailPasswordBackwardsCompatibilityService "github.com/supertokens/supertokens-golang/recipe/emailpassword/emaildelivery/backwardCompatibilityService" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeBackwardCompatibilityService(recipeInterfaceImpl tpepmodels.RecipeInterface, emailPasswordRecipeInterfaceImpl epmodels.RecipeInterface, appInfo supertokens.NormalisedAppinfo, sendResetPasswordEmail func(user epmodels.User, passwordResetURLWithToken string, userContext supertokens.UserContext)) emaildelivery.EmailDeliveryInterface { - // We are using evmodels.User as opposed to tpmodels.User because TypeInput of thirdparty accepts evmodels.TypeInput for EmailVerificationFeature - // Similarly with epmodels.User as well - emailPasswordService := emailPasswordBackwardsCompatibilityService.MakeBackwardCompatibilityService(emailPasswordRecipeInterfaceImpl, appInfo, sendResetPasswordEmail) - - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordReset != nil { - return (*emailPasswordService.SendEmail)(input, userContext) - - } else { - return errors.New("should never come here") - } - } - - return emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - } -} diff --git a/recipe/thirdpartyemailpassword/emaildelivery/smtpService/main.go b/recipe/thirdpartyemailpassword/emaildelivery/smtpService/main.go deleted file mode 100644 index d852f922..00000000 --- a/recipe/thirdpartyemailpassword/emaildelivery/smtpService/main.go +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, 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 smtpService - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - epsmtpService "github.com/supertokens/supertokens-golang/recipe/emailpassword/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeSMTPService(config emaildelivery.SMTPServiceConfig) *emaildelivery.EmailDeliveryInterface { - emailPasswordServiceImpl := epsmtpService.MakeSMTPService(config) - - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordReset != nil { - return (*emailPasswordServiceImpl.SendEmail)(input, userContext) - - } else { - return errors.New("should never come here") - } - } - - return &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - } -} diff --git a/recipe/thirdpartyemailpassword/main.go b/recipe/thirdpartyemailpassword/main.go deleted file mode 100644 index c297bf89..00000000 --- a/recipe/thirdpartyemailpassword/main.go +++ /dev/null @@ -1,240 +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 thirdpartyemailpassword - -import ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/api" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func Init(config *tpepmodels.TypeInput) supertokens.Recipe { - return recipeInit(config) -} - -func ThirdPartyManuallyCreateOrUpdateUser(tenantId string, thirdPartyID string, thirdPartyUserID string, email string, userContext ...supertokens.UserContext) (tpepmodels.ManuallyCreateOrUpdateUserResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return tpepmodels.ManuallyCreateOrUpdateUserResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ThirdPartyManuallyCreateOrUpdateUser)(thirdPartyID, thirdPartyUserID, email, tenantId, userContext[0]) -} - -func ThirdPartyGetProvider(tenantId string, thirdPartyID string, clientType *string, userContext ...supertokens.UserContext) (*tpmodels.TypeProvider, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ThirdPartyGetProvider)(thirdPartyID, clientType, tenantId, userContext[0]) -} - -func GetUserByThirdPartyInfo(tenantId string, thirdPartyID string, thirdPartyUserID string, userContext ...supertokens.UserContext) (*tpepmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUserByThirdPartyInfo)(thirdPartyID, thirdPartyUserID, tenantId, userContext[0]) -} - -func EmailPasswordSignUp(tenantId string, email, password string, userContext ...supertokens.UserContext) (tpepmodels.SignUpResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return tpepmodels.SignUpResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.EmailPasswordSignUp)(email, password, tenantId, userContext[0]) -} - -func EmailPasswordSignIn(tenantId string, email, password string, userContext ...supertokens.UserContext) (tpepmodels.SignInResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return tpepmodels.SignInResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.EmailPasswordSignIn)(email, password, tenantId, userContext[0]) -} - -func GetUserById(userID string, userContext ...supertokens.UserContext) (*tpepmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUserByID)(userID, userContext[0]) -} - -func GetUsersByEmail(tenantId string, email string, userContext ...supertokens.UserContext) ([]tpepmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUsersByEmail)(email, tenantId, userContext[0]) -} - -func CreateResetPasswordToken(tenantId string, userID string, userContext ...supertokens.UserContext) (epmodels.CreateResetPasswordTokenResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return epmodels.CreateResetPasswordTokenResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.CreateResetPasswordToken)(userID, tenantId, userContext[0]) -} - -func ResetPasswordUsingToken(tenantId string, token, newPassword string, userContext ...supertokens.UserContext) (epmodels.ResetPasswordUsingTokenResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return epmodels.ResetPasswordUsingTokenResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ResetPasswordUsingToken)(token, newPassword, tenantId, userContext[0]) -} - -func UpdateEmailOrPassword(userId string, email *string, password *string, applyPasswordPolicy *bool, tenantIdForPasswordPolicy *string, userContext ...supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return epmodels.UpdateEmailOrPasswordResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - if tenantIdForPasswordPolicy == nil { - tenantId := supertokens.DefaultTenantId - tenantIdForPasswordPolicy = &tenantId - } - return (*instance.RecipeImpl.UpdateEmailOrPassword)(userId, email, password, applyPasswordPolicy, *tenantIdForPasswordPolicy, userContext[0]) -} - -func SendEmail(input emaildelivery.EmailType, userContext ...supertokens.UserContext) error { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.EmailDelivery.IngredientInterfaceImpl.SendEmail)(input, userContext[0]) -} - -func CreateResetPasswordLink(tenantId string, userID string, userContext ...supertokens.UserContext) (epmodels.CreateResetPasswordLinkResponse, error) { - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - tokenResponse, err := CreateResetPasswordToken(tenantId, userID, userContext...) - if err != nil { - return epmodels.CreateResetPasswordLinkResponse{}, err - } - if tokenResponse.UnknownUserIdError != nil { - return epmodels.CreateResetPasswordLinkResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } - - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return epmodels.CreateResetPasswordLinkResponse{}, err - } - - link, err := api.GetPasswordResetLink( - instance.RecipeModule.GetAppInfo(), - instance.RecipeModule.GetRecipeID(), - tokenResponse.OK.Token, - tenantId, - supertokens.GetRequestFromUserContext(userContext[0]), - userContext[0], - ) - - if err != nil { - return epmodels.CreateResetPasswordLinkResponse{}, err - } - - return epmodels.CreateResetPasswordLinkResponse{ - OK: &struct{ Link string }{ - Link: link, - }, - }, nil -} - -func SendResetPasswordEmail(tenantId string, userID string, userContext ...supertokens.UserContext) (epmodels.SendResetPasswordEmailResponse, error) { - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - linkResponse, err := CreateResetPasswordLink(tenantId, userID, userContext...) - if err != nil { - return epmodels.SendResetPasswordEmailResponse{}, err - } - if linkResponse.UnknownUserIdError != nil { - return epmodels.SendResetPasswordEmailResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } - - userInfo, err := GetUserById(userID, userContext...) - if err != nil { - return epmodels.SendResetPasswordEmailResponse{}, err - } - if userInfo == nil { - return epmodels.SendResetPasswordEmailResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } - err = SendEmail(emaildelivery.EmailType{ - PasswordReset: &emaildelivery.PasswordResetType{ - User: emaildelivery.User{ - ID: userInfo.ID, - Email: userInfo.Email, - }, - PasswordResetLink: linkResponse.OK.Link, - TenantId: tenantId, - }, - }, userContext...) - if err != nil { - return epmodels.SendResetPasswordEmailResponse{}, err - } - - return epmodels.SendResetPasswordEmailResponse{ - OK: &struct{}{}, - }, nil -} - -func MakeSMTPService(config emaildelivery.SMTPServiceConfig) *emaildelivery.EmailDeliveryInterface { - return smtpService.MakeSMTPService(config) -} diff --git a/recipe/thirdpartyemailpassword/recipe.go b/recipe/thirdpartyemailpassword/recipe.go deleted file mode 100644 index 382f385b..00000000 --- a/recipe/thirdpartyemailpassword/recipe.go +++ /dev/null @@ -1,220 +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 thirdpartyemailpassword - -import ( - "errors" - "net/http" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/recipe/emailpassword" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/api" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/recipeimplementation" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -const RECIPE_ID = "thirdpartyemailpassword" - -type Recipe struct { - RecipeModule supertokens.RecipeModule - Config tpepmodels.TypeNormalisedInput - emailPasswordRecipe *emailpassword.Recipe - thirdPartyRecipe *thirdparty.Recipe - RecipeImpl tpepmodels.RecipeInterface - APIImpl tpepmodels.APIInterface - EmailDelivery emaildelivery.Ingredient - GetEmailPasswordRecipe func() *emailpassword.Recipe -} - -var singletonInstance *Recipe - -func MakeRecipe(recipeId string, appInfo supertokens.NormalisedAppinfo, config *tpepmodels.TypeInput, emailVerificationInstance *emailverification.Recipe, thirdPartyInstance *thirdparty.Recipe, emailPasswordInstance *emailpassword.Recipe, emailDeliveryIngredient *emaildelivery.Ingredient, onSuperTokensAPIError func(err error, req *http.Request, res http.ResponseWriter)) (Recipe, error) { - r := &Recipe{} - r.RecipeModule = supertokens.MakeRecipeModule(recipeId, appInfo, r.handleAPIRequest, r.getAllCORSHeaders, r.getAPIsHandled, nil, r.handleError, onSuperTokensAPIError) - - verifiedConfig, err := validateAndNormaliseUserInput(r, appInfo, config) - if err != nil { - return Recipe{}, err - } - r.Config = verifiedConfig - { - emailpasswordquerierInstance, err := supertokens.GetNewQuerierInstanceOrThrowError(emailpassword.RECIPE_ID) - if err != nil { - return Recipe{}, err - } - thirdpartyquerierInstance, err := supertokens.GetNewQuerierInstanceOrThrowError(thirdparty.RECIPE_ID) - if err != nil { - return Recipe{}, err - } - var getEmailPasswordConfig = func() epmodels.TypeNormalisedInput { - return r.emailPasswordRecipe.Config - } - r.RecipeImpl = verifiedConfig.Override.Functions(recipeimplementation.MakeRecipeImplementation(*emailpasswordquerierInstance, thirdpartyquerierInstance, getEmailPasswordConfig, verifiedConfig.Providers)) - } - r.APIImpl = verifiedConfig.Override.APIs(api.MakeAPIImplementation()) - - var emailPasswordRecipe emailpassword.Recipe - emailPasswordRecipeImpl := recipeimplementation.MakeEmailPasswordRecipeImplementation(r.RecipeImpl) - if emailDeliveryIngredient != nil { - r.EmailDelivery = *emailDeliveryIngredient - } else { - r.EmailDelivery = emaildelivery.MakeIngredient(verifiedConfig.GetEmailDeliveryConfig(r.RecipeImpl, emailPasswordRecipeImpl)) - } - - if emailPasswordInstance == nil { - emailPasswordConfig := &epmodels.TypeInput{ - SignUpFeature: verifiedConfig.SignUpFeature, - Override: &epmodels.OverrideStruct{ - Functions: func(_ epmodels.RecipeInterface) epmodels.RecipeInterface { - return emailPasswordRecipeImpl - }, - APIs: func(_ epmodels.APIInterface) epmodels.APIInterface { - return api.GetEmailPasswordIterfaceImpl(r.APIImpl) - }, - }, - } - emailPasswordRecipe, err = emailpassword.MakeRecipe(recipeId, appInfo, emailPasswordConfig, &r.EmailDelivery, onSuperTokensAPIError) - if err != nil { - return Recipe{}, err - } - r.emailPasswordRecipe = &emailPasswordRecipe - } else { - r.emailPasswordRecipe = emailPasswordInstance - } - - if thirdPartyInstance == nil { - thirdPartyConfig := &tpmodels.TypeInput{ - SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ - Providers: verifiedConfig.Providers, - }, - Override: &tpmodels.OverrideStruct{ - Functions: func(_ tpmodels.RecipeInterface) tpmodels.RecipeInterface { - return recipeimplementation.MakeThirdPartyRecipeImplementation(r.RecipeImpl) - }, - APIs: func(_ tpmodels.APIInterface) tpmodels.APIInterface { - return api.GetThirdPartyIterfaceImpl(r.APIImpl) - }, - }, - } - thirdPartyRecipeinstance, err := thirdparty.MakeRecipe(recipeId, appInfo, thirdPartyConfig, &r.EmailDelivery, onSuperTokensAPIError) - if err != nil { - return Recipe{}, err - } - r.thirdPartyRecipe = &thirdPartyRecipeinstance - } else { - r.thirdPartyRecipe = thirdPartyInstance - } - - r.GetEmailPasswordRecipe = func() *emailpassword.Recipe { - return r.emailPasswordRecipe - } - - return *r, nil -} - -func recipeInit(config *tpepmodels.TypeInput) supertokens.Recipe { - return func(appInfo supertokens.NormalisedAppinfo, onSuperTokensAPIError func(err error, req *http.Request, res http.ResponseWriter)) (*supertokens.RecipeModule, error) { - if singletonInstance == nil { - recipe, err := MakeRecipe(RECIPE_ID, appInfo, config, nil, nil, nil, nil, onSuperTokensAPIError) - if err != nil { - return nil, err - } - singletonInstance = &recipe - return &singletonInstance.RecipeModule, nil - } - return nil, errors.New("ThirdPartyEmailPassword recipe has already been initialised. Please check your code for bugs.") - } -} - -func GetRecipeInstanceOrThrowError() (*Recipe, error) { - if singletonInstance != nil { - return singletonInstance, nil - } - return nil, errors.New("Initialisation not done. Did you forget to call the init function?") -} - -func GetRecipeInstance() *Recipe { - return singletonInstance -} - -// implement RecipeModule - -func (r *Recipe) getAPIsHandled() ([]supertokens.APIHandled, error) { - emailpasswordAPIhandled, err := r.emailPasswordRecipe.RecipeModule.GetAPIsHandled() - if err != nil { - return nil, err - } - apisHandled := append(emailpasswordAPIhandled) - if r.thirdPartyRecipe != nil { - thirdpartyAPIhandled, err := r.thirdPartyRecipe.RecipeModule.GetAPIsHandled() - if err != nil { - return nil, err - } - apisHandled = append(apisHandled, thirdpartyAPIhandled...) - } - return apisHandled, nil -} - -func (r *Recipe) handleAPIRequest(id string, tenantId string, req *http.Request, res http.ResponseWriter, theirHandler http.HandlerFunc, path supertokens.NormalisedURLPath, method string, userContext supertokens.UserContext) error { - ok, _, err := r.emailPasswordRecipe.RecipeModule.ReturnAPIIdIfCanHandleRequest(path, method, userContext) - if err != nil { - return err - } - if ok != nil { - return r.emailPasswordRecipe.RecipeModule.HandleAPIRequest(id, tenantId, req, res, theirHandler, path, method, userContext) - } - if r.thirdPartyRecipe != nil { - ok, _, err := r.thirdPartyRecipe.RecipeModule.ReturnAPIIdIfCanHandleRequest(path, method, userContext) - if err != nil { - return err - } - if ok != nil { - return r.thirdPartyRecipe.RecipeModule.HandleAPIRequest(id, tenantId, req, res, theirHandler, path, method, userContext) - } - } - return errors.New("should not come here") -} - -func (r *Recipe) getAllCORSHeaders() []string { - corsHeaders := r.emailPasswordRecipe.RecipeModule.GetAllCORSHeaders() - if r.thirdPartyRecipe != nil { - corsHeaders = append(corsHeaders, r.thirdPartyRecipe.RecipeModule.GetAllCORSHeaders()...) - } - return corsHeaders -} - -func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) { - handleError, err := r.emailPasswordRecipe.RecipeModule.HandleError(err, req, res, userContext) - if err != nil || handleError { - return handleError, err - } - if r.thirdPartyRecipe != nil { - handleError, err = r.thirdPartyRecipe.RecipeModule.HandleError(err, req, res, userContext) - if err != nil || handleError { - return handleError, err - } - } - return false, nil -} - -func ResetForTest() { - singletonInstance = nil -} diff --git a/recipe/thirdpartyemailpassword/recipeimplementation/emailPasswordRecipeImplementation.go b/recipe/thirdpartyemailpassword/recipeimplementation/emailPasswordRecipeImplementation.go deleted file mode 100644 index 86919fd1..00000000 --- a/recipe/thirdpartyemailpassword/recipeimplementation/emailPasswordRecipeImplementation.go +++ /dev/null @@ -1,122 +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 recipeimplementation - -import ( - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeEmailPasswordRecipeImplementation(recipeImplementation tpepmodels.RecipeInterface) epmodels.RecipeInterface { - - signUp := func(email, password string, tenantId string, userContext supertokens.UserContext) (epmodels.SignUpResponse, error) { - response, err := (*recipeImplementation.EmailPasswordSignUp)(email, password, tenantId, userContext) - if err != nil { - return epmodels.SignUpResponse{}, err - } - if response.EmailAlreadyExistsError != nil { - return epmodels.SignUpResponse{ - EmailAlreadyExistsError: &struct{}{}, - }, nil - } - return epmodels.SignUpResponse{ - OK: &struct{ User epmodels.User }{ - User: epmodels.User{ - ID: response.OK.User.ID, - Email: response.OK.User.Email, - TimeJoined: response.OK.User.TimeJoined, - }, - }, - }, nil - } - - signIn := func(email, password string, tenantId string, userContext supertokens.UserContext) (epmodels.SignInResponse, error) { - response, err := (*recipeImplementation.EmailPasswordSignIn)(email, password, tenantId, userContext) - if err != nil { - return epmodels.SignInResponse{}, err - } - if response.WrongCredentialsError != nil { - return epmodels.SignInResponse{ - WrongCredentialsError: &struct{}{}, - }, nil - } - return epmodels.SignInResponse{ - OK: &struct{ User epmodels.User }{ - User: epmodels.User{ - ID: response.OK.User.ID, - Email: response.OK.User.Email, - TimeJoined: response.OK.User.TimeJoined, - }, - }, - }, nil - } - - getUserByID := func(userId string, userContext supertokens.UserContext) (*epmodels.User, error) { - user, err := (*recipeImplementation.GetUserByID)(userId, userContext) - if err != nil { - return nil, err - } - if user == nil || user.ThirdParty != nil { - return nil, nil - } - return &epmodels.User{ - ID: user.ID, - Email: user.Email, - TimeJoined: user.TimeJoined, - }, nil - } - - getUserByEmail := func(email string, tenantId string, userContext supertokens.UserContext) (*epmodels.User, error) { - users, err := (*recipeImplementation.GetUsersByEmail)(email, tenantId, userContext) - if err != nil { - return nil, err - } - - for _, user := range users { - if user.ThirdParty == nil { - return &epmodels.User{ - ID: user.ID, - Email: user.Email, - TimeJoined: user.TimeJoined, - }, nil - } - } - return nil, nil - } - - createResetPasswordToken := func(userID string, tenantId string, userContext supertokens.UserContext) (epmodels.CreateResetPasswordTokenResponse, error) { - return (*recipeImplementation.CreateResetPasswordToken)(userID, tenantId, userContext) - } - - resetPasswordUsingToken := func(token, newPassword string, tenantId string, userContext supertokens.UserContext) (epmodels.ResetPasswordUsingTokenResponse, error) { - return (*recipeImplementation.ResetPasswordUsingToken)(token, newPassword, tenantId, userContext) - } - - updateEmailOrPassword := func(userId string, email, password *string, applyPasswordPolicy *bool, tenantIdForPasswordPolicy string, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) { - return (*recipeImplementation.UpdateEmailOrPassword)(userId, email, password, applyPasswordPolicy, tenantIdForPasswordPolicy, userContext) - } - - return epmodels.RecipeInterface{ - SignUp: &signUp, - SignIn: &signIn, - GetUserByID: &getUserByID, - GetUserByEmail: &getUserByEmail, - CreateResetPasswordToken: &createResetPasswordToken, - ResetPasswordUsingToken: &resetPasswordUsingToken, - UpdateEmailOrPassword: &updateEmailOrPassword, - } -} diff --git a/recipe/thirdpartyemailpassword/recipeimplementation/main.go b/recipe/thirdpartyemailpassword/recipeimplementation/main.go deleted file mode 100644 index f5a2a3d7..00000000 --- a/recipe/thirdpartyemailpassword/recipeimplementation/main.go +++ /dev/null @@ -1,328 +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 recipeimplementation - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/recipe/emailpassword" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeRecipeImplementation(emailPasswordQuerier supertokens.Querier, thirdPartyQuerier *supertokens.Querier, getEmailPasswordConfig func() epmodels.TypeNormalisedInput, providers []tpmodels.ProviderInput) tpepmodels.RecipeInterface { - result := tpepmodels.RecipeInterface{} - emailPasswordImplementation := emailpassword.MakeRecipeImplementation(emailPasswordQuerier, getEmailPasswordConfig) - var thirdPartyImplementation *tpmodels.RecipeInterface - if thirdPartyQuerier != nil { - thirdPartyImplementationTemp := thirdparty.MakeRecipeImplementation(*thirdPartyQuerier, providers) - thirdPartyImplementation = &thirdPartyImplementationTemp - } - - ogSignUp := *emailPasswordImplementation.SignUp - signUp := func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) { - response, err := ogSignUp(email, password, tenantId, userContext) - if err != nil { - return tpepmodels.SignUpResponse{}, err - } - if response.EmailAlreadyExistsError != nil { - return tpepmodels.SignUpResponse{ - EmailAlreadyExistsError: &struct{}{}, - }, nil - } - return tpepmodels.SignUpResponse{ - OK: &struct { - User tpepmodels.User - }{ - User: tpepmodels.User{ - ID: response.OK.User.ID, - Email: response.OK.User.Email, - TimeJoined: response.OK.User.TimeJoined, - ThirdParty: nil, - }, - }, - }, nil - } - - ogSignIn := *emailPasswordImplementation.SignIn - signIn := func(email, password string, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInResponse, error) { - response, err := ogSignIn(email, password, tenantId, userContext) - if err != nil { - return tpepmodels.SignInResponse{}, err - } - if response.WrongCredentialsError != nil { - return tpepmodels.SignInResponse{ - WrongCredentialsError: &struct{}{}, - }, nil - } - return tpepmodels.SignInResponse{ - OK: &struct{ User tpepmodels.User }{ - User: tpepmodels.User{ - ID: response.OK.User.ID, - Email: response.OK.User.Email, - TimeJoined: response.OK.User.TimeJoined, - ThirdParty: nil, - }, - }, - }, nil - } - - var ogSignInUp func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) = nil - if thirdPartyImplementation != nil { - ogSignInUp = *thirdPartyImplementation.SignInUp - } - signInUp := func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) { - if ogSignInUp == nil { - return tpepmodels.SignInUpResponse{}, errors.New("no thirdparty provider configured") - } - result, err := ogSignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - if err != nil { - return tpepmodels.SignInUpResponse{}, err - } - - return tpepmodels.SignInUpResponse{ - OK: &struct { - CreatedNewUser bool - User tpepmodels.User - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpepmodels.User{ - ID: result.OK.User.ID, - TimeJoined: result.OK.User.TimeJoined, - Email: result.OK.User.Email, - ThirdParty: &result.OK.User.ThirdParty, - }, - OAuthTokens: result.OK.OAuthTokens, - RawUserInfoFromProvider: result.OK.RawUserInfoFromProvider, - }, - }, nil - } - - var ogManuallyCreateOrUpdateUser func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (tpmodels.ManuallyCreateOrUpdateUserResponse, error) = nil - if thirdPartyImplementation != nil { - ogManuallyCreateOrUpdateUser = *thirdPartyImplementation.ManuallyCreateOrUpdateUser - } - manuallyCreateOrUpdateUser := func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (tpepmodels.ManuallyCreateOrUpdateUserResponse, error) { - if ogManuallyCreateOrUpdateUser == nil { - return tpepmodels.ManuallyCreateOrUpdateUserResponse{}, errors.New("no thirdparty provider configured") - } - result, err := ogManuallyCreateOrUpdateUser(thirdPartyID, thirdPartyUserID, email, tenantId, userContext) - if err != nil { - return tpepmodels.ManuallyCreateOrUpdateUserResponse{}, err - } - - return tpepmodels.ManuallyCreateOrUpdateUserResponse{ - OK: &struct { - CreatedNewUser bool - User tpepmodels.User - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpepmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - ThirdParty: &result.OK.User.ThirdParty, - }, - }, - }, nil - } - - var ogGetProvider func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) - if thirdPartyImplementation != nil { - ogGetProvider = *thirdPartyImplementation.GetProvider - } - - getProvider := func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) { - if ogGetProvider == nil { - return nil, errors.New("no thirdparty provider configured") - } - - return ogGetProvider(thirdPartyID, clientType, tenantId, userContext) - } - - ogEPGetUserByID := *emailPasswordImplementation.GetUserByID - var ogTPGetUserById func(userID string, userContext supertokens.UserContext) (*tpmodels.User, error) = nil - if thirdPartyImplementation != nil { - ogTPGetUserById = *thirdPartyImplementation.GetUserByID - } - getUserByID := func(userID string, userContext supertokens.UserContext) (*tpepmodels.User, error) { - user, err := ogEPGetUserByID(userID, userContext) - if err != nil { - return nil, err - } - if user != nil { - return &tpepmodels.User{ - ID: user.ID, - Email: user.Email, - TimeJoined: user.TimeJoined, - TenantIds: user.TenantIds, - ThirdParty: nil, - }, nil - } - if ogTPGetUserById == nil { - return nil, nil - } - - userinfo, err := ogTPGetUserById(userID, userContext) - if err != nil { - return nil, err - } - - if userinfo != nil { - return &tpepmodels.User{ - ID: userinfo.ID, - Email: userinfo.Email, - TimeJoined: userinfo.TimeJoined, - ThirdParty: &userinfo.ThirdParty, - TenantIds: userinfo.TenantIds, - }, nil - } - return nil, nil - } - - ogEPGetUserByEmail := *emailPasswordImplementation.GetUserByEmail - var ogTPGetUsersByEmail func(email string, tenantId string, userContext supertokens.UserContext) ([]tpmodels.User, error) = nil - if thirdPartyImplementation != nil { - ogTPGetUsersByEmail = *thirdPartyImplementation.GetUsersByEmail - } - getUsersByEmail := func(email string, tenantId string, userContext supertokens.UserContext) ([]tpepmodels.User, error) { - fromEP, err := ogEPGetUserByEmail(email, tenantId, userContext) - if err != nil { - return []tpepmodels.User{}, err - } - - fromTP := []tpmodels.User{} - if ogTPGetUsersByEmail != nil { - fromTP, err = ogTPGetUsersByEmail(email, tenantId, userContext) - if err != nil { - return []tpepmodels.User{}, err - } - } - finalResult := []tpepmodels.User{} - - if fromEP != nil { - finalResult = append(finalResult, tpepmodels.User{ - ID: fromEP.ID, - TimeJoined: fromEP.TimeJoined, - Email: fromEP.Email, - ThirdParty: nil, - }) - } - - for _, tpUser := range fromTP { - finalResult = append(finalResult, tpepmodels.User{ - ID: tpUser.ID, - TimeJoined: tpUser.TimeJoined, - Email: tpUser.Email, - ThirdParty: &tpUser.ThirdParty, - }) - } - - return finalResult, nil - } - - var ogGetUserByThirdPartyInfo func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*tpmodels.User, error) = nil - if thirdPartyImplementation != nil { - ogGetUserByThirdPartyInfo = *thirdPartyImplementation.GetUserByThirdPartyInfo - } - getUserByThirdPartyInfo := func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*tpepmodels.User, error) { - if ogGetUserByThirdPartyInfo == nil { - return nil, nil - } - - userinfo, err := ogGetUserByThirdPartyInfo(thirdPartyID, thirdPartyUserID, tenantId, userContext) - if err != nil { - return nil, err - } - - if userinfo != nil { - return &tpepmodels.User{ - ID: userinfo.ID, - Email: userinfo.Email, - TimeJoined: userinfo.TimeJoined, - TenantIds: userinfo.TenantIds, - ThirdParty: &userinfo.ThirdParty, - }, nil - } - return nil, nil - } - - ogCreateResetPasswordToken := *emailPasswordImplementation.CreateResetPasswordToken - createResetPasswordToken := func(userID string, tenantId string, userContext supertokens.UserContext) (epmodels.CreateResetPasswordTokenResponse, error) { - return ogCreateResetPasswordToken(userID, tenantId, userContext) - } - - ogResetPasswordUsingToken := *emailPasswordImplementation.ResetPasswordUsingToken - resetPasswordUsingToken := func(token, newPassword string, tenantId string, userContext supertokens.UserContext) (epmodels.ResetPasswordUsingTokenResponse, error) { - return ogResetPasswordUsingToken(token, newPassword, tenantId, userContext) - } - - ogUpdateEmailOrPassword := *emailPasswordImplementation.UpdateEmailOrPassword - updateEmailOrPassword := func(userId string, email, password *string, applyPasswordPolicy *bool, tenantIdForPasswordPolicy string, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) { - user, err := (*result.GetUserByID)(userId, userContext) - if err != nil { - return epmodels.UpdateEmailOrPasswordResponse{}, err - } - - if user == nil { - return epmodels.UpdateEmailOrPasswordResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } else if user.ThirdParty != nil { - return epmodels.UpdateEmailOrPasswordResponse{}, errors.New("cannot update email or password of a user who signed up using third party login") - } - - return ogUpdateEmailOrPassword(userId, email, password, applyPasswordPolicy, tenantIdForPasswordPolicy, userContext) - } - - result.GetUserByID = &getUserByID - result.GetUsersByEmail = &getUsersByEmail - result.GetUserByThirdPartyInfo = &getUserByThirdPartyInfo - result.ThirdPartySignInUp = &signInUp - result.ThirdPartyManuallyCreateOrUpdateUser = &manuallyCreateOrUpdateUser - result.ThirdPartyGetProvider = &getProvider - result.EmailPasswordSignUp = &signUp - result.EmailPasswordSignIn = &signIn - result.CreateResetPasswordToken = &createResetPasswordToken - result.ResetPasswordUsingToken = &resetPasswordUsingToken - result.UpdateEmailOrPassword = &updateEmailOrPassword - - modifiedEp := MakeEmailPasswordRecipeImplementation(result) - (*emailPasswordImplementation.CreateResetPasswordToken) = *modifiedEp.CreateResetPasswordToken - (*emailPasswordImplementation.GetUserByEmail) = *modifiedEp.GetUserByEmail - (*emailPasswordImplementation.GetUserByID) = *modifiedEp.GetUserByID - (*emailPasswordImplementation.ResetPasswordUsingToken) = *modifiedEp.ResetPasswordUsingToken - (*emailPasswordImplementation.SignIn) = *modifiedEp.SignIn - (*emailPasswordImplementation.SignUp) = *modifiedEp.SignUp - (*emailPasswordImplementation.UpdateEmailOrPassword) = *modifiedEp.UpdateEmailOrPassword - - if thirdPartyImplementation != nil { - modifiedTp := MakeThirdPartyRecipeImplementation(result) - (*thirdPartyImplementation.GetUserByID) = *modifiedTp.GetUserByID - (*thirdPartyImplementation.GetUserByThirdPartyInfo) = *modifiedTp.GetUserByThirdPartyInfo - (*thirdPartyImplementation.GetUsersByEmail) = *modifiedTp.GetUsersByEmail - (*thirdPartyImplementation.SignInUp) = *modifiedTp.SignInUp - (*thirdPartyImplementation.ManuallyCreateOrUpdateUser) = *modifiedTp.ManuallyCreateOrUpdateUser - (*thirdPartyImplementation.GetProvider) = *modifiedTp.GetProvider - } - - return result -} diff --git a/recipe/thirdpartyemailpassword/recipeimplementation/thirdPartyRecipeImplementation.go b/recipe/thirdpartyemailpassword/recipeimplementation/thirdPartyRecipeImplementation.go deleted file mode 100644 index e1dd3b6e..00000000 --- a/recipe/thirdpartyemailpassword/recipeimplementation/thirdPartyRecipeImplementation.go +++ /dev/null @@ -1,149 +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 recipeimplementation - -import ( - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeThirdPartyRecipeImplementation(recipeImplementation tpepmodels.RecipeInterface) tpmodels.RecipeInterface { - - getUserByThirdPartyInfo := func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*tpmodels.User, error) { - user, err := (*recipeImplementation.GetUserByThirdPartyInfo)(thirdPartyID, thirdPartyUserID, tenantId, userContext) - if err != nil { - return nil, err - } - if user == nil || user.ThirdParty == nil { - return nil, nil - } - return &tpmodels.User{ - ID: user.ID, - Email: user.Email, - TimeJoined: user.TimeJoined, - TenantIds: user.TenantIds, - ThirdParty: *user.ThirdParty, - }, nil - } - - signInUp := func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) { - result, err := (*recipeImplementation.ThirdPartySignInUp)(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - if err != nil { - return tpmodels.SignInUpResponse{}, err - } - - return tpmodels.SignInUpResponse{ - OK: &struct { - CreatedNewUser bool - User tpmodels.User - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - TenantIds: result.OK.User.TenantIds, - ThirdParty: *result.OK.User.ThirdParty, - }, - OAuthTokens: result.OK.OAuthTokens, - RawUserInfoFromProvider: result.OK.RawUserInfoFromProvider, - }, - }, nil - } - - manuallyCreateOrUpdateUser := func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (tpmodels.ManuallyCreateOrUpdateUserResponse, error) { - result, err := (*recipeImplementation.ThirdPartyManuallyCreateOrUpdateUser)(thirdPartyID, thirdPartyUserID, email, tenantId, userContext) - if err != nil { - return tpmodels.ManuallyCreateOrUpdateUserResponse{}, err - } - return tpmodels.ManuallyCreateOrUpdateUserResponse{ - OK: &struct { - CreatedNewUser bool - User tpmodels.User - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - TenantIds: result.OK.User.TenantIds, - ThirdParty: struct { - ID string "json:\"id\"" - UserID string "json:\"userId\"" - }{ - ID: result.OK.User.ThirdParty.ID, - UserID: result.OK.User.ThirdParty.UserID, - }, - }, - }, - }, nil - } - - getUserByID := func(userID string, userContext supertokens.UserContext) (*tpmodels.User, error) { - user, err := (*recipeImplementation.GetUserByID)(userID, userContext) - if err != nil { - return nil, err - } - if user == nil || user.ThirdParty == nil { - return nil, nil - } - return &tpmodels.User{ - ID: user.ID, - Email: user.Email, - TimeJoined: user.TimeJoined, - TenantIds: user.TenantIds, - ThirdParty: *user.ThirdParty, - }, nil - } - - getUserByEmail := func(email string, tenantId string, userContext supertokens.UserContext) ([]tpmodels.User, error) { - users, err := (*recipeImplementation.GetUsersByEmail)(email, tenantId, userContext) - if err != nil { - return nil, err - } - - finalResult := []tpmodels.User{} - - for _, tpepUser := range users { - if tpepUser.ThirdParty != nil { - finalResult = append(finalResult, tpmodels.User{ - ID: tpepUser.ID, - TimeJoined: tpepUser.TimeJoined, - Email: tpepUser.Email, - TenantIds: tpepUser.TenantIds, - ThirdParty: *tpepUser.ThirdParty, - }) - } - } - return finalResult, nil - } - - getProvider := func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) { - return (*recipeImplementation.ThirdPartyGetProvider)(thirdPartyID, clientType, tenantId, userContext) - } - - return tpmodels.RecipeInterface{ - GetUserByID: &getUserByID, - GetUsersByEmail: &getUserByEmail, - GetUserByThirdPartyInfo: &getUserByThirdPartyInfo, - SignInUp: &signInUp, - ManuallyCreateOrUpdateUser: &manuallyCreateOrUpdateUser, - GetProvider: &getProvider, - } -} diff --git a/recipe/thirdpartyemailpassword/signinFeature_test.go b/recipe/thirdpartyemailpassword/signinFeature_test.go deleted file mode 100644 index 21d36280..00000000 --- a/recipe/thirdpartyemailpassword/signinFeature_test.go +++ /dev/null @@ -1,458 +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 thirdpartyemailpassword - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "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/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" - "gopkg.in/h2non/gock.v1" -) - -func TestAfterDisablingTheDefaultSigninupAPIdoesNotWork(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(&tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - *originalImplementation.ThirdPartySignInUpPOST = nil - return originalImplementation - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - signinupPostData := map[string]interface{}{ - "thirdPartyId": "google", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(signinupPostData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestAfterDisablingTheDefaultSigninAPIdoesNotWork(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(&tpepmodels.TypeInput{ - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - *originalImplementation.EmailPasswordSignInPOST = nil - return originalImplementation - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignInRequest("random@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestHandlePostSignUpInGetsSetCorrectly(t *testing.T) { - userId := "" - loginType := "" - customAntiCsrfVal := "VIA_TOKEN" - 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(&tpepmodels.TypeInput{ - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - originalSignInUpPost := *originalImplementation.ThirdPartySignInUpPOST - *originalImplementation.ThirdPartySignInUpPOST = func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.ThirdPartySignInUpPOSTResponse, error) { - resp, err := originalSignInUpPost(provider, input, tenantId, options, userContext) - if err != nil { - t.Error(err.Error()) - } - userId = resp.OK.User.ID - loginType = "thirdparty" - return resp, err - } - return originalImplementation - }, - }, - Providers: []tpmodels.ProviderInput{ - customProvider2, - }, - }), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customAntiCsrfVal, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - resp.Body.Close() - - var result map[string]interface{} - - err = json.Unmarshal(dataInBytes, &result) - if err != nil { - t.Error(err.Error()) - } - - user := result["user"].(map[string]interface{}) - - assert.Equal(t, userId, user["id"]) - assert.Equal(t, "thirdparty", loginType) -} - -func TestSignInAPIWorksWhenInputIsFine(t *testing.T) { - configValue := supertokens.TypeInput{ - Supertokens: &supertokens.ConnectionInfo{ - ConnectionURI: "http://localhost:8080", - }, - AppInfo: supertokens.AppInfo{ - APIDomain: "api.supertokens.io", - AppName: "SuperTokens", - WebsiteDomain: "supertokens.io", - }, - RecipeList: []supertokens.Recipe{ - Init(nil), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - var result map[string]interface{} - json.Unmarshal(dataInBytes, &result) - resp.Body.Close() - - assert.Equal(t, "OK", result["status"]) - - signupUserInfo := result["user"].(map[string]interface{}) - - resp1, err := unittesting.SignInRequest("random@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp1.StatusCode) - dataInBytes1, err := io.ReadAll(resp1.Body) - if err != nil { - t.Error(err.Error()) - } - var result1 map[string]interface{} - json.Unmarshal(dataInBytes1, &result1) - resp1.Body.Close() - - assert.Equal(t, "OK", result1["status"]) - - signInUserInfo := result1["user"].(map[string]interface{}) - - assert.Equal(t, signInUserInfo["id"], signupUserInfo["id"]) - assert.Equal(t, signInUserInfo["email"], signupUserInfo["email"]) -} - -func TestSigninAPIthrowsAnErrorWhenEmailDoesNotExist(t *testing.T) { - configValue := supertokens.TypeInput{ - Supertokens: &supertokens.ConnectionInfo{ - ConnectionURI: "http://localhost:8080", - }, - AppInfo: supertokens.AppInfo{ - APIDomain: "api.supertokens.io", - AppName: "SuperTokens", - WebsiteDomain: "supertokens.io", - }, - RecipeList: []supertokens.Recipe{ - Init(nil), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - var result map[string]interface{} - json.Unmarshal(dataInBytes, &result) - resp.Body.Close() - - assert.Equal(t, "OK", result["status"]) - - resp1, err := unittesting.SignInRequest("rand@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp1.StatusCode) - dataInBytes1, err := io.ReadAll(resp1.Body) - if err != nil { - t.Error(err.Error()) - } - var result1 map[string]interface{} - json.Unmarshal(dataInBytes1, &result1) - resp1.Body.Close() - - assert.Equal(t, "WRONG_CREDENTIALS_ERROR", result1["status"]) -} - -func TestCustomEmailValidatorsToSignupAndMakeSureTheyAreAppliedToSignIn(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(&tpepmodels.TypeInput{ - SignUpFeature: &epmodels.TypeInputSignUp{ - FormFields: []epmodels.TypeInputFormField{ - { - ID: "email", - Validate: func(value interface{}, tenantId string) *string { - customErrorMessage := "email does not start with test" - if strings.HasPrefix(value.(string), "test") { - return nil - } - return &customErrorMessage - }, - }, - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("testrandom@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - var result map[string]interface{} - json.Unmarshal(dataInBytes, &result) - resp.Body.Close() - - assert.Equal(t, "OK", result["status"]) - - resp1, err := unittesting.SignInRequest("rand@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, resp1.StatusCode) - dataInBytes1, err := io.ReadAll(resp1.Body) - if err != nil { - t.Error(err.Error()) - } - var result1 map[string]interface{} - json.Unmarshal(dataInBytes1, &result1) - resp1.Body.Close() - - assert.Equal(t, "FIELD_ERROR", result1["status"]) - assert.Equal(t, "email does not start with test", result1["formFields"].([]interface{})[0].(map[string]interface{})["error"]) - assert.Equal(t, "email", result1["formFields"].([]interface{})[0].(map[string]interface{})["id"]) -} diff --git a/recipe/thirdpartyemailpassword/signupFeature_test.go b/recipe/thirdpartyemailpassword/signupFeature_test.go deleted file mode 100644 index be319ea4..00000000 --- a/recipe/thirdpartyemailpassword/signupFeature_test.go +++ /dev/null @@ -1,295 +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 thirdpartyemailpassword - -import ( - "bytes" - "encoding/json" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "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/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" - "gopkg.in/h2non/gock.v1" -) - -func TestDisablingDefaultAPIDoesNotWork(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(&tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - *originalImplementation.ThirdPartySignInUpPOST = nil - return originalImplementation - }, - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - signinupPostData := map[string]interface{}{ - "thirdPartyId": "google", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(signinupPostData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestThatIfDisableAPIDefaultSignupAPIDoesNotWork(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(&tpepmodels.TypeInput{ - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - *originalImplementation.EmailPasswordSignUpPOST = nil - return originalImplementation - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestMinimumConfigForOneProvider(t *testing.T) { - customAntiCsrfVal := "VIA_TOKEN" - 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(&tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProvider2, - }, - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - *originalImplementation.EmailPasswordSignUpPOST = nil - return originalImplementation - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customAntiCsrfVal, - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - resp.Body.Close() - - var result map[string]interface{} - - err = json.Unmarshal(dataInBytes, &result) - if err != nil { - t.Error(err.Error()) - } - - user := result["user"].(map[string]interface{}) - - assert.Equal(t, "OK", result["status"]) - assert.Equal(t, true, result["createdNewUser"]) - assert.Equal(t, "email@test.com", user["email"]) - assert.Equal(t, "custom", user["thirdParty"].(map[string]interface{})["id"]) - assert.Equal(t, "user", user["thirdParty"].(map[string]interface{})["userId"]) -} - -func TestSignUpAPIWorksWhenInputIsFine(t *testing.T) { - configValue := supertokens.TypeInput{ - Supertokens: &supertokens.ConnectionInfo{ - ConnectionURI: "http://localhost:8080", - }, - AppInfo: supertokens.AppInfo{ - APIDomain: "api.supertokens.io", - AppName: "SuperTokens", - WebsiteDomain: "supertokens.io", - }, - RecipeList: []supertokens.Recipe{ - Init(nil), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - }, - } - - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - err := supertokens.Init(configValue) - if err != nil { - t.Error(err.Error()) - } - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("random@gmail.com", "validpass123", testServer.URL) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - dataInBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - resp.Body.Close() - - var result map[string]interface{} - - err = json.Unmarshal(dataInBytes, &result) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", result["status"]) - assert.Equal(t, "random@gmail.com", result["user"].(map[string]interface{})["email"]) -} diff --git a/recipe/thirdpartyemailpassword/testingUtils.go b/recipe/thirdpartyemailpassword/testingUtils.go deleted file mode 100644 index aa81b282..00000000 --- a/recipe/thirdpartyemailpassword/testingUtils.go +++ /dev/null @@ -1,160 +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 thirdpartyemailpassword - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/emailpassword" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/multitenancy" - "github.com/supertokens/supertokens-golang/recipe/session" - "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func resetAll() { - supertokens.ResetForTest() - ResetForTest() - session.ResetForTest() - emailverification.ResetForTest() - thirdparty.ResetForTest() - emailpassword.ResetForTest() - multitenancy.ResetForTest() -} - -func BeforeEach() { - unittesting.KillAllST() - resetAll() - unittesting.SetUpST() -} - -func AfterEach() { - unittesting.KillAllST() - resetAll() - unittesting.CleanST() -} - -type PostDataForCustomProvider struct { - ThirdPartyId string `json:"thirdPartyId"` - OAuthTokens map[string]interface{} `json:"oAuthTokens"` -} - -var customProvider1 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - AuthorizationEndpointQueryParams: map[string]interface{}{ - "scope": "test", - "client_id": "supertokens", - }, - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "email@test.com", - IsVerified: true, - }, - }, nil - - } - return originalImplementation - }, -} - -var customProvider2 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "email@test.com", - IsVerified: true, - }, - }, nil - } - return originalImplementation - }, -} - -func supertokensInitForTest(t *testing.T, recipes ...supertokens.Recipe) *httptest.Server { - config := supertokens.TypeInput{ - Supertokens: &supertokens.ConnectionInfo{ - ConnectionURI: "http://localhost:8080", - }, - AppInfo: supertokens.AppInfo{ - APIDomain: "api.supertokens.io", - AppName: "SuperTokens", - WebsiteDomain: "supertokens.io", - }, - RecipeList: recipes, - } - - err := supertokens.Init(config) - assert.NoError(t, err) - - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - return testServer -} - -var customProviderForEmailVerification = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "test@example.com", - IsVerified: false, - }, - }, nil - } - return originalImplementation - }, -} diff --git a/recipe/thirdpartyemailpassword/tpep_email_test.go b/recipe/thirdpartyemailpassword/tpep_email_test.go deleted file mode 100644 index 828eeb8c..00000000 --- a/recipe/thirdpartyemailpassword/tpep_email_test.go +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright (c) 2022, 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 thirdpartyemailpassword - -import ( - "bytes" - "encoding/json" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/recipe/emailpassword" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/emailverification/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" - "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/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestDefaultBackwardCompatibilityPasswordResetForEmailPasswordUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(nil), - ) - defer testServer.Close() - - EmailPasswordSignUp("public", "test@example.com", "1234abcd") - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.True(t, emailpassword.PasswordResetEmailSentForTest) - assert.Equal(t, emailpassword.PasswordResetDataForTest.User.Email, "test@example.com") - assert.NotEmpty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) -} - -func TestDefaultBackwardCompatibilityPasswordResetForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(nil), - ) - defer testServer.Close() - - ThirdPartyManuallyCreateOrUpdateUser("public", "custom", "user-id", "test@example.com") - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) -} - -func TestDefaultBackwardCompatibilityPasswordResetForNonExistantUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(nil), - ) - defer testServer.Close() - - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) -} - -func TestCustomOverrideResetPasswordForEmailPasswordUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - passwordResetLink := "" - - tpepConfig := &tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordReset != nil { - customCalled = true - email = input.PasswordReset.User.Email - passwordResetLink = input.PasswordReset.PasswordResetLink - } - return nil - } - return originalImplementation - }, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - EmailPasswordSignUp("public", "test@example.com", "1234abcd") - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler called - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, passwordResetLink) - assert.True(t, customCalled) -} - -func TestCustomOverrideResetPasswordForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - passwordResetLink := "" - - tpepConfig := &tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordReset != nil { - customCalled = true - email = input.PasswordReset.User.Email - passwordResetLink = input.PasswordReset.PasswordResetLink - } - return nil - } - return originalImplementation - }, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - ThirdPartyManuallyCreateOrUpdateUser("public", "custom", "user-id", "test@example.com") - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler not called - assert.Empty(t, email) - assert.Empty(t, passwordResetLink) - assert.False(t, customCalled) -} - -func TestCustomOverrideResetPasswordForNonExistantUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - passwordResetLink := "" - - tpepConfig := &tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordReset != nil { - customCalled = true - email = input.PasswordReset.User.Email - passwordResetLink = input.PasswordReset.PasswordResetLink - } - return nil - } - return originalImplementation - }, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler not called - assert.Empty(t, email) - assert.Empty(t, passwordResetLink) - assert.False(t, customCalled) -} - -func TestSMTPOverridePasswordResetForEmailPasswordUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - passwordResetLink := "" - - smtpService := MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.PasswordReset != nil { - email = input.PasswordReset.User.Email - passwordResetLink = input.PasswordReset.PasswordResetLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tpepConfig := &tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - EmailPasswordSignUp("public", "test@example.com", "1234abcd") - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, passwordResetLink) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawEmailCalled, true) -} - -func TestSMTPOverridePasswordResetForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - passwordResetLink := "" - - smtpService := MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.PasswordReset != nil { - email = input.PasswordReset.User.Email - passwordResetLink = input.PasswordReset.PasswordResetLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tpepConfig := &tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - ThirdPartyManuallyCreateOrUpdateUser("public", "custom", "user-id", "test@example.com") - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler not called - assert.Empty(t, email) - assert.Empty(t, passwordResetLink) - assert.False(t, getContentCalled) - assert.False(t, sendRawEmailCalled) -} - -func TestSMTPOverridePasswordResetForNonExistantUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - passwordResetLink := "" - - smtpService := MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.PasswordReset != nil { - email = input.PasswordReset.User.Email - passwordResetLink = input.PasswordReset.PasswordResetLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tpepConfig := &tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - resp, err := unittesting.PasswordResetTokenRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler not called - assert.Empty(t, email) - assert.Empty(t, passwordResetLink) - assert.False(t, getContentCalled) - assert.False(t, sendRawEmailCalled) -} - -func TestDefaultBackwardCompatibilityEmailVerifyForEmailPasswordUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{Mode: evmodels.ModeOptional}), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(nil), - ) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("test@example.com", "1234abcd", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.True(t, emailverification.EmailVerificationEmailSentForTest) - assert.Equal(t, emailverification.EmailVerificationDataForTest.User.Email, "test@example.com") - assert.NotEmpty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) -} - -func TestDefaultBackwardCompatibilityEmailVerifyForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - tpepConfig := &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProviderForEmailVerification, - }, - } - testServer := supertokensInitForTest(t, - emailverification.Init(evmodels.TypeInput{Mode: evmodels.ModeOptional}), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - signinupPostData := PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - - cookies := resp.Cookies() - - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.True(t, emailverification.EmailVerificationEmailSentForTest) - assert.Equal(t, emailverification.EmailVerificationDataForTest.User.Email, "test@example.com") - assert.NotEmpty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) -} - -// func TestBackwardCompatibilityEmailVerifyForEmailPasswordUser(t *testing.T) { -// BeforeEach() -// unittesting.StartUpST("localhost", "8080") -// defer AfterEach() - -// customCalled := false -// email := "" -// emailVerifyLink := "" - -// tpepConfig := &tpepmodels.TypeInput{ -// EmailVerificationFeature: &tpepmodels.TypeInputEmailVerificationFeature{ -// CreateAndSendCustomEmail: func(user tpepmodels.User, emailVerificationURLWithToken string, userContext supertokens.UserContext) { -// email = user.Email -// emailVerifyLink = emailVerificationURLWithToken -// customCalled = true -// }, -// }, -// } -// testServer := supertokensInitForTest(t, session.Init(nil), Init(tpepConfig)) -// defer testServer.Close() - -// resp, err := unittesting.SignupRequest("test@example.com", "1234abcd", testServer.URL) -// assert.NoError(t, err) - -// cookies := resp.Cookies() -// resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, resp.StatusCode) - -// // Default handler not called -// assert.False(t, emailpassword.PasswordResetEmailSentForTest) -// assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) -// assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - -// // Custom handler called -// assert.Equal(t, email, "test@example.com") -// assert.NotEmpty(t, emailVerifyLink) -// assert.True(t, customCalled) -// } - -// func TestBackwardCompatibilityEmailVerifyForThirdpartyUser(t *testing.T) { -// BeforeEach() -// unittesting.StartUpST("localhost", "8080") -// defer AfterEach() - -// customCalled := false -// email := "" -// emailVerifyLink := "" -// var thirdparty *struct { -// ID string `json:"id"` -// UserID string `json:"userId"` -// } - -// tpepConfig := &tpepmodels.TypeInput{ -// EmailVerificationFeature: &tpepmodels.TypeInputEmailVerificationFeature{ -// CreateAndSendCustomEmail: func(user tpepmodels.User, emailVerificationURLWithToken string, userContext supertokens.UserContext) { -// email = user.Email -// emailVerifyLink = emailVerificationURLWithToken -// thirdparty = user.ThirdParty -// customCalled = true -// }, -// }, -// Providers: []tpmodels.TypeProvider{customProviderForEmailVerification}, -// } -// testServer := supertokensInitForTest(t, session.Init(nil), Init(tpepConfig)) -// defer testServer.Close() - -// signinupPostData := PostDataForCustomProvider{ -// ThirdPartyId: "custom", -// AuthCodeResponse: map[string]string{ -// "access_token": "saodiasjodai", -// }, -// RedirectUri: "http://127.0.0.1/callback", -// } - -// postBody, err := json.Marshal(signinupPostData) -// resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) -// assert.NoError(t, err) - -// cookies := resp.Cookies() -// resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, resp.StatusCode) - -// // Default handler not called -// assert.False(t, emailpassword.PasswordResetEmailSentForTest) -// assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) -// assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - -// // Custom handler called -// assert.Equal(t, email, "test@example.com") -// assert.NotEmpty(t, emailVerifyLink) -// assert.NotNil(t, thirdparty) -// assert.True(t, customCalled) -// } - -func TestCustomOverrideEmailVerifyForEmailPasswordUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - emailVerifyLink := "" - - tpepConfig := &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProviderForEmailVerification, - }, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.EmailVerification != nil { - customCalled = true - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - } - return nil - } - return originalImplementation - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("test@example.com", "1234abcd", testServer.URL) - assert.NoError(t, err) - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler called - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, emailVerifyLink) - assert.True(t, customCalled) -} - -func TestCustomOverrideEmailVerifyForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - emailVerifyLink := "" - - tpepConfig := &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - customProviderForEmailVerification, - }, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.EmailVerification != nil { - customCalled = true - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - } - return nil - } - return originalImplementation - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - signinupPostData := PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - // Custom handler called - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, emailVerifyLink) - assert.True(t, customCalled) -} - -func TestSMTPOverrideEmailVerifyForEmailPasswordUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - emailVerifyLink := "" - - smtpService := smtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.EmailVerification != nil { - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tpepConfig := &tpepmodels.TypeInput{} - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - resp, err := unittesting.SignupRequest("test@example.com", "1234abcd", testServer.URL) - assert.NoError(t, err) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, emailVerifyLink) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawEmailCalled, true) -} - -func TestSMTPOverrideEmailVerifyForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - emailVerifyLink := "" - - smtpService := smtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.EmailVerification != nil { - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tpepConfig := &tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{customProviderForEmailVerification}, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tpepConfig), - ) - defer testServer.Close() - - signinupPostData := PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailpassword.PasswordResetEmailSentForTest) - assert.Empty(t, emailpassword.PasswordResetDataForTest.User.Email) - assert.Empty(t, emailpassword.PasswordResetDataForTest.PasswordResetURLWithToken) - - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, emailVerifyLink) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawEmailCalled, true) -} - -func TestSendResetPassworEmailFunction(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - resetLink := "" - - testServer := supertokensInitForTest(t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(&tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - resetLink = input.PasswordReset.PasswordResetLink - return nil - } - return originalImplementation - }, - }, - }), - ) - defer testServer.Close() - - user, err := EmailPasswordSignUp("public", "test@example.com", "pass1234") - assert.NoError(t, err) - - resp, err := SendResetPasswordEmail("public", user.OK.User.ID) - assert.NoError(t, err) - assert.True(t, resp.OK != nil) - - assert.Contains(t, resetLink, "rid=thirdpartyemailpassword") - assert.Contains(t, resetLink, "tenantId=public") - assert.Contains(t, resetLink, "token=") -} diff --git a/recipe/thirdpartyemailpassword/tpep_userIdMapping_test.go b/recipe/thirdpartyemailpassword/tpep_userIdMapping_test.go deleted file mode 100644 index 34ecd32f..00000000 --- a/recipe/thirdpartyemailpassword/tpep_userIdMapping_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package thirdpartyemailpassword - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func initForUserIdMappingTest(t *testing.T) { - - config := 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(&tpepmodels.TypeInput{ - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "test", - ClientSecret: "test-secret", - }, - }, - }, - }, - }, - })}, - } - - err := supertokens.Init(config) - assert.NoError(t, err) -} - -func TestCreateUserIdMappingUsingEmail(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - initForUserIdMappingTest(t) - - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - assert.NoError(t, err) - - cdiVersion, err := querier.GetQuerierAPIVersion() - assert.NoError(t, err) - - if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { - return - } - - signUpResponse, err := ThirdPartyManuallyCreateOrUpdateUser("public", "google", "googleID", "test@example.com") - assert.NoError(t, err) - - externalUserId := "externalId" - externalUserIdInfo := "externalIdInfo" - createResp, err := supertokens.CreateUserIdMapping(signUpResponse.OK.User.ID, externalUserId, &externalUserIdInfo, nil) - assert.NoError(t, err) - assert.NotNil(t, createResp.OK) - - { // Using supertokens ID - userResp, err := GetUserById(signUpResponse.OK.User.ID) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using external ID - userResp, err := GetUserById(externalUserId) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using thirdparty info - userResp, err := GetUserByThirdPartyInfo("public", "google", "googleID") - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } -} - -func TestEPCreateUserIdMappingGetUserById(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - initForUserIdMappingTest(t) - - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - assert.NoError(t, err) - - cdiVersion, err := querier.GetQuerierAPIVersion() - assert.NoError(t, err) - - if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { - return - } - - signUpResponse, err := EmailPasswordSignUp("public", "test@example.com", "testpass123") - assert.NoError(t, err) - - assert.NotNil(t, signUpResponse.OK) - - externalUserId := "externalId" - externalUserIdInfo := "externalIdInfo" - createResp, err := supertokens.CreateUserIdMapping(signUpResponse.OK.User.ID, externalUserId, &externalUserIdInfo, nil) - assert.NoError(t, err) - assert.NotNil(t, createResp.OK) - - { // Using supertokens ID - userResp, err := GetUserById(signUpResponse.OK.User.ID) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using external ID - userResp, err := GetUserById(externalUserId) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } -} - -func TestEPCreateUserIdMappingGetUserByEmail(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - initForUserIdMappingTest(t) - - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - assert.NoError(t, err) - - cdiVersion, err := querier.GetQuerierAPIVersion() - assert.NoError(t, err) - - if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { - return - } - - signUpResponse, err := EmailPasswordSignUp("public", "test@example.com", "testpass123") - assert.NoError(t, err) - - assert.NotNil(t, signUpResponse.OK) - - externalUserId := "externalId" - externalUserIdInfo := "externalIdInfo" - createResp, err := supertokens.CreateUserIdMapping(signUpResponse.OK.User.ID, externalUserId, &externalUserIdInfo, nil) - assert.NoError(t, err) - assert.NotNil(t, createResp.OK) - - userResp, err := GetUsersByEmail("public", "test@example.com") - assert.NoError(t, err) - assert.NotNil(t, userResp) - assert.Equal(t, 1, len(userResp)) - for _, user := range userResp { - assert.Equal(t, externalUserId, user.ID) - } -} diff --git a/recipe/thirdpartyemailpassword/tpepmodels/apiInterface.go b/recipe/thirdpartyemailpassword/tpepmodels/apiInterface.go deleted file mode 100644 index d2f7a758..00000000 --- a/recipe/thirdpartyemailpassword/tpepmodels/apiInterface.go +++ /dev/null @@ -1,80 +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 tpepmodels - -import ( - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -type APIInterface struct { - AuthorisationUrlGET *func(provider *tpmodels.TypeProvider, redirectURIOnProviderDashboard string, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.AuthorisationUrlGETResponse, error) - AppleRedirectHandlerPOST *func(formPostInfoFromProvider map[string]interface{}, options tpmodels.APIOptions, userContext supertokens.UserContext) error - ThirdPartySignInUpPOST *func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (ThirdPartySignInUpPOSTResponse, error) - - EmailPasswordEmailExistsGET *func(email string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.EmailExistsGETResponse, error) - GeneratePasswordResetTokenPOST *func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.GeneratePasswordResetTokenPOSTResponse, error) - PasswordResetPOST *func(formFields []epmodels.TypeFormField, token string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.ResetPasswordPOSTResponse, error) - EmailPasswordSignInPOST *func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (SignInPOSTResponse, error) - EmailPasswordSignUpPOST *func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (SignUpPOSTResponse, error) -} - -type SignUpPOSTResponse struct { - OK *struct { - User User - Session sessmodels.SessionContainer - } - EmailAlreadyExistsError *struct{} - GeneralError *supertokens.GeneralErrorResponse -} - -type SignInPOSTResponse struct { - OK *struct { - User User - Session sessmodels.SessionContainer - } - WrongCredentialsError *struct{} - GeneralError *supertokens.GeneralErrorResponse -} - -type EmailpasswordInput struct { - IsSignIn bool - FormFields []epmodels.TypeFormField - Options epmodels.APIOptions -} - -type EmailpasswordOutput struct { - OK *struct { - User User - CreatedNewUser bool - } - EmailAlreadyExistsError *struct{} - WrongCredentialsError *struct{} -} - -type ThirdPartySignInUpPOSTResponse struct { - OK *struct { - CreatedNewUser bool - User User - Session sessmodels.SessionContainer - OAuthTokens tpmodels.TypeOAuthTokens - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - } - NoEmailGivenByProviderError *struct{} - GeneralError *supertokens.GeneralErrorResponse -} diff --git a/recipe/thirdpartyemailpassword/tpepmodels/models.go b/recipe/thirdpartyemailpassword/tpepmodels/models.go deleted file mode 100644 index 369b4631..00000000 --- a/recipe/thirdpartyemailpassword/tpepmodels/models.go +++ /dev/null @@ -1,62 +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 tpepmodels - -import ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" -) - -type User struct { - ID string `json:"id"` - TimeJoined uint64 `json:"timeJoined"` - Email string `json:"email"` - ThirdParty *struct { - ID string `json:"id"` - UserID string `json:"userId"` - } `json:"thirdParty"` - TenantIds []string `json:"tenantIds"` -} - -type TypeContext struct { - FormFields []epmodels.TypeFormField - ThirdPartyAuthCodeResponse interface{} -} - -type TypeInput struct { - SignUpFeature *epmodels.TypeInputSignUp - Providers []tpmodels.ProviderInput - Override *OverrideStruct - EmailDelivery *emaildelivery.TypeInput -} - -type TypeNormalisedInput struct { - SignUpFeature *epmodels.TypeInputSignUp - Providers []tpmodels.ProviderInput - Override OverrideStruct - GetEmailDeliveryConfig func(recipeImpl RecipeInterface, epRecipeImpl epmodels.RecipeInterface) emaildelivery.TypeInputWithService -} - -type OverrideStruct struct { - Functions func(originalImplementation RecipeInterface) RecipeInterface - APIs func(originalImplementation APIInterface) APIInterface -} - -type EmailStruct struct { - ID string `json:"id"` - IsVerified bool `json:"isVerified"` -} diff --git a/recipe/thirdpartyemailpassword/tpepmodels/recipeInterface.go b/recipe/thirdpartyemailpassword/tpepmodels/recipeInterface.go deleted file mode 100644 index c99f7e00..00000000 --- a/recipe/thirdpartyemailpassword/tpepmodels/recipeInterface.go +++ /dev/null @@ -1,68 +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 tpepmodels - -import ( - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -type RecipeInterface struct { - GetUserByID *func(userID string, userContext supertokens.UserContext) (*User, error) - GetUsersByEmail *func(email string, tenantId string, userContext supertokens.UserContext) ([]User, error) - GetUserByThirdPartyInfo *func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*User, error) - - ThirdPartySignInUp *func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (SignInUpResponse, error) - ThirdPartyManuallyCreateOrUpdateUser *func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (ManuallyCreateOrUpdateUserResponse, error) - ThirdPartyGetProvider *func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) - - EmailPasswordSignUp *func(email string, password string, tenantId string, userContext supertokens.UserContext) (SignUpResponse, error) - EmailPasswordSignIn *func(email string, password string, tenantId string, userContext supertokens.UserContext) (SignInResponse, error) - CreateResetPasswordToken *func(userID string, tenantId string, userContext supertokens.UserContext) (epmodels.CreateResetPasswordTokenResponse, error) - ResetPasswordUsingToken *func(token string, newPassword string, tenantId string, userContext supertokens.UserContext) (epmodels.ResetPasswordUsingTokenResponse, error) - UpdateEmailOrPassword *func(userId string, email *string, password *string, applyPasswordPolicy *bool, tenantIdForPasswordPolicy string, userContext supertokens.UserContext) (epmodels.UpdateEmailOrPasswordResponse, error) -} - -type SignInUpResponse struct { - OK *struct { - CreatedNewUser bool - User User - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - } -} - -type ManuallyCreateOrUpdateUserResponse struct { - OK *struct { - CreatedNewUser bool - User User - } -} - -type SignUpResponse struct { - OK *struct { - User User - } - EmailAlreadyExistsError *struct{} -} - -type SignInResponse struct { - OK *struct { - User User - } - WrongCredentialsError *struct{} -} diff --git a/recipe/thirdpartyemailpassword/utils.go b/recipe/thirdpartyemailpassword/utils.go deleted file mode 100644 index 0f351079..00000000 --- a/recipe/thirdpartyemailpassword/utils.go +++ /dev/null @@ -1,79 +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 thirdpartyemailpassword - -import ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/recipe/emailpassword" - "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/emaildelivery/backwardCompatibilityService" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func validateAndNormaliseUserInput(recipeInstance *Recipe, appInfo supertokens.NormalisedAppinfo, config *tpepmodels.TypeInput) (tpepmodels.TypeNormalisedInput, error) { - typeNormalisedInput := makeTypeNormalisedInput(recipeInstance) - - if config != nil && config.SignUpFeature != nil { - typeNormalisedInput.SignUpFeature = config.SignUpFeature - } - - if config != nil && config.Providers != nil { - typeNormalisedInput.Providers = config.Providers - } - - typeNormalisedInput.GetEmailDeliveryConfig = func(recipeImpl tpepmodels.RecipeInterface, epRecipeImpl epmodels.RecipeInterface) emaildelivery.TypeInputWithService { - sendPasswordResetEmail := emailpassword.DefaultCreateAndSendCustomPasswordResetEmail(appInfo) - emailService := backwardCompatibilityService.MakeBackwardCompatibilityService(recipeImpl, epRecipeImpl, appInfo, sendPasswordResetEmail) - if config != nil && config.EmailDelivery != nil && config.EmailDelivery.Service != nil { - emailService = *config.EmailDelivery.Service - } - result := emaildelivery.TypeInputWithService{ - Service: emailService, - } - if config != nil && config.EmailDelivery != nil && config.EmailDelivery.Override != nil { - result.Override = config.EmailDelivery.Override - } - - return result - } - - if config != nil && config.Override != nil { - if config.Override.Functions != nil { - typeNormalisedInput.Override.Functions = config.Override.Functions - } - if config.Override.APIs != nil { - typeNormalisedInput.Override.APIs = config.Override.APIs - } - } - - return typeNormalisedInput, nil -} - -func makeTypeNormalisedInput(recipeInstance *Recipe) tpepmodels.TypeNormalisedInput { - return tpepmodels.TypeNormalisedInput{ - SignUpFeature: nil, - Providers: nil, - Override: tpepmodels.OverrideStruct{ - Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface { - return originalImplementation - }, - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - return originalImplementation - }, - }, - } -} diff --git a/recipe/thirdpartypasswordless/api/implementation.go b/recipe/thirdpartypasswordless/api/implementation.go deleted file mode 100644 index 2d7f7c13..00000000 --- a/recipe/thirdpartypasswordless/api/implementation.go +++ /dev/null @@ -1,168 +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 api - -import ( - plessapi "github.com/supertokens/supertokens-golang/recipe/passwordless/api" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - tpapi "github.com/supertokens/supertokens-golang/recipe/thirdparty/api" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeAPIImplementation() tplmodels.APIInterface { - passwordlessImplementation := plessapi.MakeAPIImplementation() - thirdPartyImplementation := tpapi.MakeAPIImplementation() - - ogSignInUpPOST := *thirdPartyImplementation.SignInUpPOST - thirdPartySignInUpPOST := func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tplmodels.ThirdPartySignInUpPOSTResponse, error) { - response, err := ogSignInUpPOST(provider, input, tenantId, options, userContext) - if err != nil { - return tplmodels.ThirdPartySignInUpPOSTResponse{}, err - } - if response.GeneralError != nil { - return tplmodels.ThirdPartySignInUpPOSTResponse{ - GeneralError: response.GeneralError, - }, nil - } else if response.NoEmailGivenByProviderError != nil { - return tplmodels.ThirdPartySignInUpPOSTResponse{ - NoEmailGivenByProviderError: &struct{}{}, - }, nil - } else { - return tplmodels.ThirdPartySignInUpPOSTResponse{ - OK: &struct { - CreatedNewUser bool - User tplmodels.User - Session *sessmodels.TypeSessionContainer - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: response.OK.CreatedNewUser, - User: tplmodels.User{ - ID: response.OK.User.ID, - TimeJoined: response.OK.User.TimeJoined, - Email: &response.OK.User.Email, - ThirdParty: &response.OK.User.ThirdParty, - }, - Session: response.OK.Session, - OAuthTokens: response.OK.OAuthTokens, - RawUserInfoFromProvider: response.OK.RawUserInfoFromProvider, - }, - }, nil - } - } - - ogAuthorisationUrlGET := *thirdPartyImplementation.AuthorisationUrlGET - authorisationUrlGET := func(provider *tpmodels.TypeProvider, redirectURIOnProviderDashboard string, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.AuthorisationUrlGETResponse, error) { - return ogAuthorisationUrlGET(provider, redirectURIOnProviderDashboard, tenantId, options, userContext) - } - - ogAppleRedirectHandlerPOST := *thirdPartyImplementation.AppleRedirectHandlerPOST - appleRedirectHandlerPOST := func(formPostInfoFromProvider map[string]interface{}, options tpmodels.APIOptions, userContext supertokens.UserContext) error { - return ogAppleRedirectHandlerPOST(formPostInfoFromProvider, options, userContext) - } - - ogConsumeCodePOST := *passwordlessImplementation.ConsumeCodePOST - consumeCodePOST := func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (tplmodels.ConsumeCodePOSTResponse, error) { - resp, err := ogConsumeCodePOST(userInput, linkCode, preAuthSessionID, tenantId, options, userContext) - if err != nil { - return tplmodels.ConsumeCodePOSTResponse{}, err - } - if resp.OK != nil { - return tplmodels.ConsumeCodePOSTResponse{ - OK: &struct { - CreatedNewUser bool - User tplmodels.User - Session sessmodels.SessionContainer - }{ - CreatedNewUser: resp.OK.CreatedNewUser, - Session: resp.OK.Session, - User: tplmodels.User{ - ID: resp.OK.User.ID, - TimeJoined: resp.OK.User.TimeJoined, - Email: resp.OK.User.Email, - PhoneNumber: resp.OK.User.PhoneNumber, - ThirdParty: nil, - }, - }, - }, nil - } else if resp.ExpiredUserInputCodeError != nil { - return tplmodels.ConsumeCodePOSTResponse{ - ExpiredUserInputCodeError: resp.ExpiredUserInputCodeError, - }, nil - } else if resp.IncorrectUserInputCodeError != nil { - return tplmodels.ConsumeCodePOSTResponse{ - IncorrectUserInputCodeError: resp.IncorrectUserInputCodeError, - }, nil - } else if resp.RestartFlowError != nil { - return tplmodels.ConsumeCodePOSTResponse{ - RestartFlowError: &struct{}{}, - }, nil - } else { - return tplmodels.ConsumeCodePOSTResponse{ - GeneralError: resp.GeneralError, - }, nil - } - } - - ogCreateCodePOST := *passwordlessImplementation.CreateCodePOST - createCodePOST := func(email *string, phoneNumber *string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.CreateCodePOSTResponse, error) { - return ogCreateCodePOST(email, phoneNumber, tenantId, options, userContext) - } - - ogEmailExistGET := *passwordlessImplementation.EmailExistsGET - passwordlessEmailExistsGET := func(email string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.EmailExistsGETResponse, error) { - return ogEmailExistGET(email, tenantId, options, userContext) - } - - ogPhoneNumberExistsGET := *passwordlessImplementation.PhoneNumberExistsGET - passwordlessPhoneNumberExistsGET := func(phoneNumber string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.PhoneNumberExistsGETResponse, error) { - return ogPhoneNumberExistsGET(phoneNumber, tenantId, options, userContext) - } - - ogResendCodePOST := *passwordlessImplementation.ResendCodePOST - resendCodePOST := func(deviceID string, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.ResendCodePOSTResponse, error) { - return ogResendCodePOST(deviceID, preAuthSessionID, tenantId, options, userContext) - } - - result := tplmodels.APIInterface{ - AuthorisationUrlGET: &authorisationUrlGET, - ThirdPartySignInUpPOST: &thirdPartySignInUpPOST, - AppleRedirectHandlerPOST: &appleRedirectHandlerPOST, - - CreateCodePOST: &createCodePOST, - ResendCodePOST: &resendCodePOST, - ConsumeCodePOST: &consumeCodePOST, - PasswordlessEmailExistsGET: &passwordlessEmailExistsGET, - PasswordlessPhoneNumberExistsGET: &passwordlessPhoneNumberExistsGET, - } - - modifiedPwdless := GetPasswordlessIterfaceImpl(result) - (*passwordlessImplementation.ConsumeCodePOST) = *modifiedPwdless.ConsumeCodePOST - (*passwordlessImplementation.CreateCodePOST) = *modifiedPwdless.CreateCodePOST - (*passwordlessImplementation.EmailExistsGET) = *modifiedPwdless.EmailExistsGET - (*passwordlessImplementation.PhoneNumberExistsGET) = *modifiedPwdless.PhoneNumberExistsGET - (*passwordlessImplementation.ResendCodePOST) = *modifiedPwdless.ResendCodePOST - - modifiedTP := GetThirdPartyIterfaceImpl(result) - (*thirdPartyImplementation.AuthorisationUrlGET) = *modifiedTP.AuthorisationUrlGET - (*thirdPartyImplementation.SignInUpPOST) = *modifiedTP.SignInUpPOST - (*thirdPartyImplementation.AppleRedirectHandlerPOST) = *modifiedTP.AppleRedirectHandlerPOST - - return result -} diff --git a/recipe/thirdpartypasswordless/api/passwordlessAPIImplementation.go b/recipe/thirdpartypasswordless/api/passwordlessAPIImplementation.go deleted file mode 100644 index 0cbecc9d..00000000 --- a/recipe/thirdpartypasswordless/api/passwordlessAPIImplementation.go +++ /dev/null @@ -1,78 +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 api - -import ( - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func GetPasswordlessIterfaceImpl(apiImplmentation tplmodels.APIInterface) plessmodels.APIInterface { - - result := plessmodels.APIInterface{ - CreateCodePOST: apiImplmentation.CreateCodePOST, - ResendCodePOST: apiImplmentation.ResendCodePOST, - EmailExistsGET: apiImplmentation.PasswordlessEmailExistsGET, - PhoneNumberExistsGET: apiImplmentation.PasswordlessPhoneNumberExistsGET, - ConsumeCodePOST: nil, - } - - if apiImplmentation.ConsumeCodePOST != nil && (*apiImplmentation.ConsumeCodePOST) != nil { - consumeCodePOST := func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.ConsumeCodePOSTResponse, error) { - result, err := (*apiImplmentation.ConsumeCodePOST)(userInput, linkCode, preAuthSessionID, tenantId, options, userContext) - if err != nil { - return plessmodels.ConsumeCodePOSTResponse{}, err - } - if result.OK != nil { - return plessmodels.ConsumeCodePOSTResponse{OK: &struct { - CreatedNewUser bool - User plessmodels.User - Session sessmodels.SessionContainer - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: plessmodels.User{ - ID: result.OK.User.ID, - Email: result.OK.User.Email, - PhoneNumber: result.OK.User.PhoneNumber, - TimeJoined: result.OK.User.TimeJoined, - }, - Session: result.OK.Session, - }}, nil - } else if result.ExpiredUserInputCodeError != nil { - return plessmodels.ConsumeCodePOSTResponse{ - ExpiredUserInputCodeError: result.ExpiredUserInputCodeError, - }, nil - } else if result.IncorrectUserInputCodeError != nil { - return plessmodels.ConsumeCodePOSTResponse{ - IncorrectUserInputCodeError: result.IncorrectUserInputCodeError, - }, nil - } else if result.RestartFlowError != nil { - return plessmodels.ConsumeCodePOSTResponse{ - RestartFlowError: &struct{}{}, - }, nil - } else { - return plessmodels.ConsumeCodePOSTResponse{ - GeneralError: result.GeneralError, - }, nil - } - } - result.ConsumeCodePOST = &consumeCodePOST - } - - return result -} diff --git a/recipe/thirdpartypasswordless/api/thirdPartyAPIImplementation.go b/recipe/thirdpartypasswordless/api/thirdPartyAPIImplementation.go deleted file mode 100644 index b5bf0341..00000000 --- a/recipe/thirdpartypasswordless/api/thirdPartyAPIImplementation.go +++ /dev/null @@ -1,78 +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 api - -import ( - "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" -) - -func GetThirdPartyIterfaceImpl(apiImplmentation tplmodels.APIInterface) tpmodels.APIInterface { - if apiImplmentation.ThirdPartySignInUpPOST == nil || (*apiImplmentation.ThirdPartySignInUpPOST) == nil { - return tpmodels.APIInterface{ - AuthorisationUrlGET: apiImplmentation.AuthorisationUrlGET, - AppleRedirectHandlerPOST: apiImplmentation.AppleRedirectHandlerPOST, - SignInUpPOST: nil, - } - } - - signInUpPOST := func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.SignInUpPOSTResponse, error) { - result, err := (*apiImplmentation.ThirdPartySignInUpPOST)(provider, input, tenantId, options, userContext) - if err != nil { - return tpmodels.SignInUpPOSTResponse{}, err - } - - if result.OK != nil { - return tpmodels.SignInUpPOSTResponse{ - OK: &struct { - CreatedNewUser bool - User tpmodels.User - Session *sessmodels.TypeSessionContainer - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpmodels.User{ - ID: result.OK.User.ID, - TimeJoined: result.OK.User.TimeJoined, - Email: *result.OK.User.Email, - TenantIds: result.OK.User.TenantIds, - ThirdParty: *result.OK.User.ThirdParty, - }, - Session: result.OK.Session, - OAuthTokens: result.OK.OAuthTokens, - RawUserInfoFromProvider: result.OK.RawUserInfoFromProvider, - }, - }, nil - } else if result.NoEmailGivenByProviderError != nil { - return tpmodels.SignInUpPOSTResponse{ - NoEmailGivenByProviderError: &struct{}{}, - }, nil - } else { - return tpmodels.SignInUpPOSTResponse{ - GeneralError: result.GeneralError, - }, nil - } - } - - return tpmodels.APIInterface{ - AuthorisationUrlGET: apiImplmentation.AuthorisationUrlGET, - AppleRedirectHandlerPOST: apiImplmentation.AppleRedirectHandlerPOST, - SignInUpPOST: &signInUpPOST, - } -} diff --git a/recipe/thirdpartypasswordless/api_test.go b/recipe/thirdpartypasswordless/api_test.go deleted file mode 100644 index a2192828..00000000 --- a/recipe/thirdpartypasswordless/api_test.go +++ /dev/null @@ -1,1461 +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" - "io" - "net/http" - "net/http/httptest" - "net/url" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "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 TestForThirdPartyPasswordlessSignInUpFlowWithEmailUsingTheEmailOrPhoneContactMethod(t *testing.T) { - var userInputCodeRef string - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - userInputCodeRef = *input.PasswordlessLogin.UserInputCode - return nil - } - 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", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailDataInBytes, err := io.ReadAll(emailResp.Body) - if err != nil { - t.Error(err.Error()) - } - emailResp.Body.Close() - - var validCreateCodeResponse map[string]interface{} - err = json.Unmarshal(emailDataInBytes, &validCreateCodeResponse) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", validCreateCodeResponse["status"]) - assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", validCreateCodeResponse["flowType"]) - - data := map[string]interface{}{ - "preAuthSessionId": validCreateCodeResponse["preAuthSessionId"], - "userInputCode": userInputCodeRef, - "deviceId": validCreateCodeResponse["deviceId"], - } - - condeConsumePostBody, err := json.Marshal(data) - if err != nil { - t.Error(err.Error()) - } - - validUserInputCodeResponse, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(condeConsumePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, validUserInputCodeResponse.StatusCode) - - validUserInputCodeDataInBytes, err := io.ReadAll(validUserInputCodeResponse.Body) - if err != nil { - t.Error(err.Error()) - } - validUserInputCodeResponse.Body.Close() - - var validUserInputCodeDataResponse map[string]interface{} - err = json.Unmarshal(validUserInputCodeDataInBytes, &validUserInputCodeDataResponse) - if err != nil { - t.Error(err.Error()) - } - - user := validUserInputCodeDataResponse["user"].(map[string]interface{}) - assert.Equal(t, "OK", validUserInputCodeDataResponse["status"]) - assert.True(t, validUserInputCodeDataResponse["createdNewUser"].(bool)) - assert.NotNil(t, user) - assert.NotNil(t, user["email"]) - assert.NotNil(t, user["id"]) - assert.NotNil(t, user["timeJoined"]) - assert.Nil(t, user["phoneNumber"]) -} - -func TestForThirdPartyPasswordlessSignUpSignInFlowWithPhoneNumberUsingEmailOrPhoneContactMethod(t *testing.T) { - var userInputCodeRef string - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - userInputCodeRef = *input.PasswordlessLogin.UserInputCode - return nil - } - 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", - SmsDelivery: &smsdelivery.TypeInput{ - Service: &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, - }, - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - phone := map[string]interface{}{ - "phoneNumber": "+12345678901", - } - - phoneBody, err := json.Marshal(phone) - if err != nil { - t.Error(err.Error()) - } - - phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, phoneResp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(phoneResp.Body) - - assert.Equal(t, "OK", result["status"]) - assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", result["flowType"]) - - consumeCodePostData := map[string]interface{}{ - "preAuthSessionId": result["preAuthSessionId"], - "userInputCode": userInputCodeRef, - "deviceId": result["deviceId"], - } - - consumeCodePostBody, err := json.Marshal(consumeCodePostData) - if err != nil { - t.Error(err.Error()) - } - - consumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, consumeCodeResp.StatusCode) - - codeConsumeResult := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp.Body) - - user := codeConsumeResult["user"].(map[string]interface{}) - assert.Equal(t, "OK", codeConsumeResult["status"]) - assert.True(t, codeConsumeResult["createdNewUser"].(bool)) - assert.NotNil(t, user) - assert.Nil(t, user["email"]) - assert.NotNil(t, user["id"]) - assert.NotNil(t, user["timeJoined"]) - assert.NotNil(t, user["phoneNumber"]) -} - -func TestForThirdPartyPasswordlessCreatingACodeWithEmailAndThenResendingTheCodeAndCheckThatTheSendingCustomEmailFunctionIsCalledWhileUsingTheEmailOrPhoneContactMethod(t *testing.T) { - isCreateAndSendCustomEmailCalled := false - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - isCreateAndSendCustomEmailCalled = true - return nil - } - 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", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(emailResp.Body) - - assert.Equal(t, "OK", result["status"]) - assert.True(t, isCreateAndSendCustomEmailCalled) - - isCreateAndSendCustomEmailCalled = false - - codeResendPostData := map[string]interface{}{ - "deviceId": result["deviceId"], - "preAuthSessionId": result["preAuthSessionId"], - } - - codeResendPostBody, err := json.Marshal(codeResendPostData) - if err != nil { - t.Error(err.Error()) - } - - codeResendPostResp, err := http.Post(testServer.URL+"/auth/signinup/code/resend", "application/json", bytes.NewBuffer(codeResendPostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, codeResendPostResp.StatusCode) - - codeResendResult := *unittesting.HttpResponseToConsumableInformation(codeResendPostResp.Body) - assert.Equal(t, "OK", codeResendResult["status"]) - assert.True(t, isCreateAndSendCustomEmailCalled) -} - -func TestWithThirdPartyPasswordlessInvalidInputToCreateCodeAPIWhileUsingTheEmailOrPhoneContactMethod(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", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - postData := map[string]interface{}{ - "email": "test@example.com", - "phoneNumber": "+12345678901", - } - - postBody, err := json.Marshal(postData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(postBody)) - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - assert.NoError(t, err) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - - assert.Equal(t, "Please provide exactly one of email or phoneNumber", result["message"]) - - postData = map[string]interface{}{} - - postBody, err = json.Marshal(postData) - if err != nil { - t.Error(err.Error()) - } - - resp1, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(postBody)) - assert.Equal(t, http.StatusBadRequest, resp1.StatusCode) - assert.NoError(t, err) - - result1 := *unittesting.HttpResponseToConsumableInformation(resp1.Body) - - assert.Equal(t, "Please provide exactly one of email or phoneNumber", result1["message"]) -} - -func TestWithThirdPartyPasswordLessAddingPhoneNumberToAUsersInfoAndSigningInWillSignInTheSameUserUsingTheEmailOrPhoneContactMethod(t *testing.T) { - var userInputCodeRef string - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - userInputCodeRef = *input.PasswordlessLogin.UserInputCode - return nil - } - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - userInputCodeRef = *input.PasswordlessLogin.UserInputCode - return nil - } - - 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", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - SmsDelivery: &smsdelivery.TypeInput{ - Service: &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, - }, - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailCreateCodeResult := *unittesting.HttpResponseToConsumableInformation(emailResp.Body) - - assert.Equal(t, "OK", emailCreateCodeResult["status"]) - - consumeCodePostData := map[string]interface{}{ - "preAuthSessionId": emailCreateCodeResult["preAuthSessionId"], - "userInputCode": userInputCodeRef, - "deviceId": emailCreateCodeResult["deviceId"], - } - - consumeCodePostBody, err := json.Marshal(consumeCodePostData) - if err != nil { - t.Error(err.Error()) - } - - consumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, consumeCodeResp.StatusCode) - - emailUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp.Body) - - assert.Equal(t, "OK", emailUserInputCodeResponse["status"]) - user := emailUserInputCodeResponse["user"].(map[string]interface{}) - - phoneNumber := "+12345678901" - UpdatePasswordlessUser(user["id"].(string), nil, &phoneNumber) - - phoneNumberPostData := map[string]interface{}{ - "phoneNumber": "+12345678901", - } - - phoneNumberPostBody, err := json.Marshal(phoneNumberPostData) - if err != nil { - t.Error(err.Error()) - } - - phoneNumberPostResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneNumberPostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, phoneNumberPostResp.StatusCode) - - phoneCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(phoneNumberPostResp.Body) - - assert.Equal(t, "OK", phoneCreateCodeResponse["status"]) - - consumeCodePostData1 := map[string]interface{}{ - "preAuthSessionId": phoneCreateCodeResponse["preAuthSessionId"], - "userInputCode": userInputCodeRef, - "deviceId": phoneCreateCodeResponse["deviceId"], - } - - consumeCodePostBody1, err := json.Marshal(consumeCodePostData1) - if err != nil { - t.Error(err.Error()) - } - - consumeCodeResp1, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody1)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, consumeCodeResp1.StatusCode) - - phoneUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp1.Body) - - assert.Equal(t, "OK", phoneUserInputCodeResponse["status"]) - user1 := phoneUserInputCodeResponse["user"].(map[string]interface{}) - - assert.Equal(t, user["id"], user1["id"]) -} - -func TestNotPassingAnyFieldsToConsumeCodeAPI(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, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - consumeCodePostData := map[string]interface{}{ - "preAuthSessionId": "preAuthSessionId", - } - - consumeCodePostBody, err := json.Marshal(consumeCodePostData) - if err != nil { - t.Error(err.Error()) - } - - consumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, consumeCodeResp.StatusCode) - - emailUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(consumeCodeResp.Body) - - assert.Equal(t, "Please provide one of (linkCode) or (deviceId+userInputCode) and not both", emailUserInputCodeResponse["message"]) -} - -func TestWithThirdPartyPasswordlessConsumeCodeAPIWithMagicLink(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, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - codeInfo, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - consumeCodePostData := map[string]interface{}{ - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - "linkCode": "invalidLinkCode", - } - - consumeCodePostBody, err := json.Marshal(consumeCodePostData) - if err != nil { - t.Error(t, err) - } - - invalidConsumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, invalidConsumeCodeResp.StatusCode) - - invalidLinkCodeResponse := *unittesting.HttpResponseToConsumableInformation(invalidConsumeCodeResp.Body) - assert.Equal(t, "RESTART_FLOW_ERROR", invalidLinkCodeResponse["status"]) - - consumeCodePostData = map[string]interface{}{ - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - "linkCode": codeInfo.OK.LinkCode, - } - - consumeCodePostBody, err = json.Marshal(consumeCodePostData) - if err != nil { - t.Error(t, err) - } - - validConsumeCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, validConsumeCodeResp.StatusCode) - - validLinkCodeResponse := *unittesting.HttpResponseToConsumableInformation(validConsumeCodeResp.Body) - assert.Equal(t, "OK", validLinkCodeResponse["status"]) - assert.True(t, validLinkCodeResponse["createdNewUser"].(bool)) - user := validLinkCodeResponse["user"].(map[string]interface{}) - assert.NotNil(t, user) - assert.NotNil(t, user["email"]) - assert.NotNil(t, user["id"]) - assert.NotNil(t, user["timeJoined"]) - assert.Nil(t, user["phoneNumber"]) -} - -func TestWithThirdPartyPasswordlessConsumeCodeAPIWithCode(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, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - codeInfo, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - consumeCodePostData := map[string]interface{}{ - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - "deviceId": codeInfo.OK.DeviceID, - "userInputCode": "invalidCode", - } - - consumeCodePostBody, err := json.Marshal(consumeCodePostData) - if err != nil { - t.Error(t, err) - } - - incorrectUserInputCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, incorrectUserInputCodeResp.StatusCode) - - incorrectUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(incorrectUserInputCodeResp.Body) - assert.Equal(t, "INCORRECT_USER_INPUT_CODE_ERROR", incorrectUserInputCodeResponse["status"]) - assert.Equal(t, float64(1), incorrectUserInputCodeResponse["failedCodeInputAttemptCount"]) - assert.Equal(t, float64(5), incorrectUserInputCodeResponse["maximumCodeInputAttempts"]) - - consumeCodePostData = map[string]interface{}{ - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - "deviceId": codeInfo.OK.DeviceID, - "userInputCode": codeInfo.OK.UserInputCode, - } - - consumeCodePostBody, err = json.Marshal(consumeCodePostData) - if err != nil { - t.Error(t, err) - } - - correctUserInputCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, correctUserInputCodeResp.StatusCode) - - correctUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(correctUserInputCodeResp.Body) - assert.Equal(t, "OK", correctUserInputCodeResponse["status"]) - assert.True(t, correctUserInputCodeResponse["createdNewUser"].(bool)) - - user := correctUserInputCodeResponse["user"].(map[string]interface{}) - assert.NotNil(t, user) - assert.NotNil(t, user["email"]) - assert.NotNil(t, user["id"]) - assert.NotNil(t, user["timeJoined"]) - assert.Nil(t, user["phoneNumber"]) - - consumeCodePostData = map[string]interface{}{ - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - "deviceId": codeInfo.OK.DeviceID, - "userInputCode": codeInfo.OK.UserInputCode, - } - - consumeCodePostBody, err = json.Marshal(consumeCodePostData) - if err != nil { - t.Error(t, err) - } - - usedUserInputCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(consumeCodePostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, usedUserInputCodeResp.StatusCode) - - usedUserInputCodeResponse := *unittesting.HttpResponseToConsumableInformation(usedUserInputCodeResp.Body) - assert.Equal(t, "RESTART_FLOW_ERROR", usedUserInputCodeResponse["status"]) -} - -func TestWithThirdPartyPasswordLessConsumeCodeAPIWithExpiredCode(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, - }, - }), - }, - } - BeforeEach() - unittesting.SetKeyValueInConfig("passwordless_code_lifetime", "1000") - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - codeInfo, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - time.Sleep(2 * time.Second) - - expiredCodeResendPostBody := map[string]interface{}{ - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - "userInputCode": codeInfo.OK.UserInputCode, - "deviceId": codeInfo.OK.DeviceID, - } - - expiredCodeResendPostBodyJson, err := json.Marshal(expiredCodeResendPostBody) - if err != nil { - t.Error(err.Error()) - } - - expiredCodeResendResp, err := http.Post(testServer.URL+"/auth/signinup/code/consume", "application/json", bytes.NewBuffer(expiredCodeResendPostBodyJson)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, expiredCodeResendResp.StatusCode) - - expiredCodeResendResponse := *unittesting.HttpResponseToConsumableInformation(expiredCodeResendResp.Body) - assert.Equal(t, "EXPIRED_USER_INPUT_CODE_ERROR", expiredCodeResendResponse["status"]) - assert.Equal(t, float64(1), expiredCodeResendResponse["failedCodeInputAttemptCount"]) - assert.Equal(t, float64(5), expiredCodeResendResponse["maximumCodeInputAttempts"]) -} - -func TestWithThirdPartyPasswordlessCreateCodeAPIWithEmail(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, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - validCreateCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, validCreateCodeResp.StatusCode) - - validCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(validCreateCodeResp.Body) - - assert.Equal(t, "OK", validCreateCodeResponse["status"]) - assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", validCreateCodeResponse["flowType"]) - - email = map[string]interface{}{ - "email": "testmpeom", - } - - emailBody, err = json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - inValidCreateCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, inValidCreateCodeResp.StatusCode) - - inValidCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(inValidCreateCodeResp.Body) - - assert.Equal(t, "GENERAL_ERROR", inValidCreateCodeResponse["status"]) - assert.Equal(t, "Email is invalid", inValidCreateCodeResponse["message"]) -} - -func TestWithThirdPartyPasswordlessCreateCodeAPIWithPhoneNumber(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - phoneNumber := map[string]interface{}{ - "phoneNumber": "+12345678901", - } - - phoneNumberBody, err := json.Marshal(phoneNumber) - if err != nil { - t.Error(err.Error()) - } - - validCreateCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneNumberBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, validCreateCodeResp.StatusCode) - - validCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(validCreateCodeResp.Body) - - assert.Equal(t, "OK", validCreateCodeResponse["status"]) - assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", validCreateCodeResponse["flowType"]) - - phoneNumber = map[string]interface{}{ - "phoneNumber": "231", - } - - phoneNumberBody, err = json.Marshal(phoneNumber) - if err != nil { - t.Error(err.Error()) - } - - inValidCreateCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneNumberBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, inValidCreateCodeResp.StatusCode) - - inValidCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(inValidCreateCodeResp.Body) - - assert.Equal(t, "GENERAL_ERROR", inValidCreateCodeResponse["status"]) - assert.Equal(t, "Phone number is invalid", inValidCreateCodeResponse["message"]) -} - -func TestWithThirdPartyPasswordlessMagicLinkFormatInCreateCodeAPI(t *testing.T) { - var magicLinkURL *url.URL - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - magicLinkURL, _ = url.Parse(*input.PasswordlessLogin.UrlWithLinkCode) - return nil - } - 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", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - validCreateCodeResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, validCreateCodeResp.StatusCode) - - validCreateCodeResponse := *unittesting.HttpResponseToConsumableInformation(validCreateCodeResp.Body) - - assert.Equal(t, "OK", validCreateCodeResponse["status"]) - assert.Equal(t, "supertokens.io", magicLinkURL.Hostname()) - assert.Equal(t, "/auth/verify", magicLinkURL.Path) - assert.Equal(t, "thirdpartypasswordless", magicLinkURL.Query().Get("rid")) - assert.Equal(t, validCreateCodeResponse["preAuthSessionId"], magicLinkURL.Query().Get("preAuthSessionId")) -} - -func TestWithThirdPartyPasswordlessEmailExistAPI(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, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) - query := req.URL.Query() - query.Add("email", "test@example.com") - req.URL.RawQuery = query.Encode() - assert.NoError(t, err) - emailDoesNotExistResp, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailDoesNotExistResp.StatusCode) - - emailDoesNotExistResponse := *unittesting.HttpResponseToConsumableInformation(emailDoesNotExistResp.Body) - - assert.Equal(t, "OK", emailDoesNotExistResponse["status"]) - assert.False(t, emailDoesNotExistResponse["exists"].(bool)) - - codeInfo, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - ConsumeCodeWithLinkCode("public", codeInfo.OK.LinkCode, codeInfo.OK.PreAuthSessionID) - - req, err = http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/email/exists", nil) - query = req.URL.Query() - query.Add("email", "test@example.com") - req.URL.RawQuery = query.Encode() - assert.NoError(t, err) - emailExistsResp, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailExistsResp.StatusCode) - - emailExistsResponse := *unittesting.HttpResponseToConsumableInformation(emailExistsResp.Body) - - assert.Equal(t, "OK", emailExistsResponse["status"]) - assert.True(t, emailExistsResponse["exists"].(bool)) -} - -func TestWithThirdPartyPasswordlessPhoneNumberExistsAPI(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/phonenumber/exists", nil) - query := req.URL.Query() - query.Add("phoneNumber", "+1234567890") - req.URL.RawQuery = query.Encode() - assert.NoError(t, err) - phoneNumberDoesNotExistResp, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, phoneNumberDoesNotExistResp.StatusCode) - - phoneNumberDoesNotExistResponse := *unittesting.HttpResponseToConsumableInformation(phoneNumberDoesNotExistResp.Body) - - assert.Equal(t, "OK", phoneNumberDoesNotExistResponse["status"]) - assert.False(t, phoneNumberDoesNotExistResponse["exists"].(bool)) - - codeInfo, err := CreateCodeWithPhoneNumber("public", "+1234567890", nil) - assert.NoError(t, err) - - ConsumeCodeWithLinkCode("public", codeInfo.OK.LinkCode, codeInfo.OK.PreAuthSessionID) - - req, err = http.NewRequest(http.MethodGet, testServer.URL+"/auth/signup/phonenumber/exists", nil) - query = req.URL.Query() - query.Add("phoneNumber", "+1234567890") - req.URL.RawQuery = query.Encode() - assert.NoError(t, err) - phoneNumberExistsResp, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, phoneNumberExistsResp.StatusCode) - - phoneNumberExistsResponse := *unittesting.HttpResponseToConsumableInformation(phoneNumberExistsResp.Body) - - assert.Equal(t, "OK", phoneNumberExistsResponse["status"]) - assert.True(t, phoneNumberExistsResponse["exists"].(bool)) -} - -func TestWithThirdPartyPasswordlessResendCodeAPI(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - codeInfo, err := CreateCodeWithPhoneNumber("public", "+1234567890", nil) - assert.NoError(t, err) - - codeResendPostData := map[string]interface{}{ - "deviceId": codeInfo.OK.DeviceID, - "preAuthSessionId": codeInfo.OK.PreAuthSessionID, - } - - codeResendPostBody, err := json.Marshal(codeResendPostData) - if err != nil { - t.Error(err.Error()) - } - - codeResendPostResp, err := http.Post(testServer.URL+"/auth/signinup/code/resend", "application/json", bytes.NewBuffer(codeResendPostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, codeResendPostResp.StatusCode) - - codeResendResult := *unittesting.HttpResponseToConsumableInformation(codeResendPostResp.Body) - assert.Equal(t, "OK", codeResendResult["status"]) - - codeResendPostData = map[string]interface{}{ - "deviceId": "codeInfo", - "preAuthSessionId": "PreAuthSessionID", - } - - codeResendPostBody, err = json.Marshal(codeResendPostData) - if err != nil { - t.Error(err.Error()) - } - - codeResendPostResp, err = http.Post(testServer.URL+"/auth/signinup/code/resend", "application/json", bytes.NewBuffer(codeResendPostBody)) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, codeResendPostResp.StatusCode) - - codeResendResult = *unittesting.HttpResponseToConsumableInformation(codeResendPostResp.Body) - assert.Equal(t, "RESTART_FLOW_ERROR", codeResendResult["status"]) -} diff --git a/recipe/thirdpartypasswordless/authorizationUrlFeature_test.go b/recipe/thirdpartypasswordless/authorizationUrlFeature_test.go deleted file mode 100644 index 95d55450..00000000 --- a/recipe/thirdpartypasswordless/authorizationUrlFeature_test.go +++ /dev/null @@ -1,163 +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" - "net/http/httptest" - "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" -) - -func TestWithThirdPartyPasswordlessMinimumConfigForThirdPartyModule(t *testing.T) { - antiCsrfVal := "VIA_TOKEN" - 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{ - AntiCsrf: &antiCsrfVal, - 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, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := http.Get(testServer.URL + "/auth/authorisationurl?thirdPartyId=custom") - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "OK", result["status"]) - - fetchedUrl := result["urlWithQueryParams"].(string) - fetchedParsedUrl, err := url.Parse(fetchedUrl) - - assert.NoError(t, err) - assert.Equal(t, "test.com", fetchedParsedUrl.Host) - assert.Equal(t, "/oauth/auth", fetchedParsedUrl.Path) - assert.Equal(t, "test", fetchedParsedUrl.Query().Get("scope")) - assert.Equal(t, "supertokens", fetchedParsedUrl.Query().Get("client_id")) -} - -func TestWithThirdPartyPasswordlessThirdPartyProviderDoesNotExist(t *testing.T) { - antiCsrfVal := "VIA_TOKEN" - 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{ - AntiCsrf: &antiCsrfVal, - 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, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := http.Get(testServer.URL + "/auth/authorisationurl?thirdPartyId=google") - assert.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "the provider google could not be found in the configuration", result["message"]) -} diff --git a/recipe/thirdpartypasswordless/config_test.go b/recipe/thirdpartypasswordless/config_test.go deleted file mode 100644 index 2e3a5af1..00000000 --- a/recipe/thirdpartypasswordless/config_test.go +++ /dev/null @@ -1,906 +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" - "errors" - "io" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "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 TestMinimumConfigForThirdPartyPasswordlessWithEmailOrPhoneContactMethod(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", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - thirdPartyPasswordlessRecipe, err := GetRecipeInstanceOrThrowError() - assert.NoError(t, err) - assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", thirdPartyPasswordlessRecipe.Config.FlowType) - assert.NotNil(t, thirdPartyPasswordlessRecipe.thirdPartyRecipe) // thirdPartyRecipe must be created always -} - -func TestForThirdPartyPasswordLessCreateAndSendCustomTextMessageWithFlowTypeMagicLinkAndPhoneContactMethod(t *testing.T) { - isUserInputCodeAndUrlWithLinkCodeValid := false - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin.UserInputCode == nil && input.PasswordlessLogin.UrlWithLinkCode != nil { - isUserInputCodeAndUrlWithLinkCodeValid = true - } - return nil - } - 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: "MAGIC_LINK", - SmsDelivery: &smsdelivery.TypeInput{ - Service: &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, - }, - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - phone := map[string]interface{}{ - "phoneNumber": "+12345678901", - } - - phoneBody, err := json.Marshal(phone) - if err != nil { - t.Error(err.Error()) - } - - phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, phoneResp.StatusCode) - - phoneDataInBytes, err := io.ReadAll(phoneResp.Body) - if err != nil { - t.Error(err.Error()) - } - phoneResp.Body.Close() - - var phoneResult map[string]interface{} - err = json.Unmarshal(phoneDataInBytes, &phoneResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", phoneResult["status"]) - assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) -} - -func TestForThirdPartyPasswordlessCreateAndSendCustomMessageWithFlowTypeUserInputCodeAndMagicLinkAndPhoneContactMethod(t *testing.T) { - isUserInputCodeAndUrlWithLinkCodeValid := false - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin.UserInputCode != nil && input.PasswordlessLogin.UrlWithLinkCode != nil { - isUserInputCodeAndUrlWithLinkCodeValid = true - } - return nil - } - - 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", - SmsDelivery: &smsdelivery.TypeInput{ - Service: &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, - }, - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - phone := map[string]interface{}{ - "phoneNumber": "+12345678901", - } - - phoneBody, err := json.Marshal(phone) - if err != nil { - t.Error(err.Error()) - } - - phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, phoneResp.StatusCode) - - phoneDataInBytes, err := io.ReadAll(phoneResp.Body) - if err != nil { - t.Error(err.Error()) - } - phoneResp.Body.Close() - - var phoneResult map[string]interface{} - err = json.Unmarshal(phoneDataInBytes, &phoneResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", phoneResult["status"]) - assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) -} - -func TestWithThirdPartyPasswordLessCreateAndSendCustomTextMessageIfErrorIsThrownItShouldReturnA500Error(t *testing.T) { - isUserInputCodeAndUrlWithLinkCodeValid := false - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - isUserInputCodeAndUrlWithLinkCodeValid = true - return errors.New("test message") - } - 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: "MAGIC_LINK", - SmsDelivery: &smsdelivery.TypeInput{ - Service: &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, - }, - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - phone := map[string]interface{}{ - "phoneNumber": "+12345678901", - } - - phoneBody, err := json.Marshal(phone) - if err != nil { - t.Error(err.Error()) - } - - phoneResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(phoneBody)) - - assert.NoError(t, err) - assert.Equal(t, 500, phoneResp.StatusCode) - assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) -} - -func TestWithThirdPartyPasswordLessMinimumConfigWithEmailContactMethod(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, - }, - }), - }, - } - - 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 - } - - thirdPartyPasswordlessRecipe, err := GetRecipeInstanceOrThrowError() - assert.NoError(t, err) - assert.Equal(t, "USER_INPUT_CODE_AND_MAGIC_LINK", thirdPartyPasswordlessRecipe.Config.FlowType) -} - -func TestWithThirdPartyPasswordlessIfValidateEmailAdressIsCalledWithContactMethod(t *testing.T) { - isValidateEmailAddressCalled := false - 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, - ValidateEmailAddress: func(email interface{}, tenantId string) *string { - isValidateEmailAddressCalled = true - return nil - }, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailDataInBytes, err := io.ReadAll(emailResp.Body) - if err != nil { - t.Error(err.Error()) - } - emailResp.Body.Close() - - var emailResult map[string]interface{} - err = json.Unmarshal(emailDataInBytes, &emailResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", emailResult["status"]) - assert.True(t, isValidateEmailAddressCalled) -} - -func TestWithThirdPartyPasswordlessIfValidateEmailAdressThrowsGenericErrorInCaseOfReturningAString(t *testing.T) { - isValidateEmailAddressCalled := false - 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, - ValidateEmailAddress: func(email interface{}, tenantId string) *string { - isValidateEmailAddressCalled = true - message := "test error" - return &message - }, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailDataInBytes, err := io.ReadAll(emailResp.Body) - if err != nil { - t.Error(err.Error()) - } - emailResp.Body.Close() - - var emailResult map[string]interface{} - err = json.Unmarshal(emailDataInBytes, &emailResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "GENERAL_ERROR", emailResult["status"]) - assert.Equal(t, "test error", emailResult["message"]) - assert.True(t, isValidateEmailAddressCalled) -} - -func TestForThirdPartyPasswordlessCreateAndSendCustomEmailWithFlowTypeUserInputCodeAndEmailContactMethod(t *testing.T) { - isUserInputCodeAndUrlWithLinkCodeValid := false - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin.UserInputCode != nil && input.PasswordlessLogin.UrlWithLinkCode == nil { - isUserInputCodeAndUrlWithLinkCodeValid = true - } - return nil - - } - 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", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailDataInBytes, err := io.ReadAll(emailResp.Body) - if err != nil { - t.Error(err.Error()) - } - emailResp.Body.Close() - - var emailResult map[string]interface{} - err = json.Unmarshal(emailDataInBytes, &emailResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", emailResult["status"]) - assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) -} - -func TestWithThirdPartyPasswordlessCreateAndSendCustomEmailWithFlowTypeMagicLinkAndEmailContactMethod(t *testing.T) { - isUserInputCodeAndUrlWithLinkCodeValid := false - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin.UserInputCode == nil && input.PasswordlessLogin.UrlWithLinkCode != nil { - isUserInputCodeAndUrlWithLinkCodeValid = true - } - return nil - } - 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: "MAGIC_LINK", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailDataInBytes, err := io.ReadAll(emailResp.Body) - if err != nil { - t.Error(err.Error()) - } - emailResp.Body.Close() - - var emailResult map[string]interface{} - err = json.Unmarshal(emailDataInBytes, &emailResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", emailResult["status"]) - assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) -} - -func TestWithThirdPartyPasswordlessCreateAndSendCustomEmailWithFlowTypeUserInputCodeAndMagicLinkAndEmailContactMethod(t *testing.T) { - isUserInputCodeAndUrlWithLinkCodeValid := false - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin.UserInputCode != nil && input.PasswordlessLogin.UrlWithLinkCode != nil { - isUserInputCodeAndUrlWithLinkCodeValid = true - } - return nil - } - 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", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, emailResp.StatusCode) - - emailDataInBytes, err := io.ReadAll(emailResp.Body) - if err != nil { - t.Error(err.Error()) - } - emailResp.Body.Close() - - var emailResult map[string]interface{} - err = json.Unmarshal(emailDataInBytes, &emailResult) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", emailResult["status"]) - assert.True(t, isUserInputCodeAndUrlWithLinkCodeValid) -} - -func TestForThirdPartyPasswordLessThatForCreateAndCustomEmailIfErrorIsThrownTheStatusInTheResponseShouldBeA500Error(t *testing.T) { - isCreateAndSendCustomEmailCalled := false - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - isCreateAndSendCustomEmailCalled = true - return errors.New("test message") - } - - 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: "MAGIC_LINK", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - }), - }, - } - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - email := map[string]interface{}{ - "email": "test@example.com", - } - - emailBody, err := json.Marshal(email) - if err != nil { - t.Error(err.Error()) - } - - emailResp, err := http.Post(testServer.URL+"/auth/signinup/code", "application/json", bytes.NewBuffer(emailBody)) - - assert.NoError(t, err) - assert.Equal(t, 500, emailResp.StatusCode) - assert.True(t, isCreateAndSendCustomEmailCalled) -} 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/emaildelivery/backwardCompatibilityService/main.go b/recipe/thirdpartypasswordless/emaildelivery/backwardCompatibilityService/main.go deleted file mode 100644 index 196904c5..00000000 --- a/recipe/thirdpartypasswordless/emaildelivery/backwardCompatibilityService/main.go +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, 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 backwardCompatibilityService - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - passwordlessBackwardsCompatibilityService "github.com/supertokens/supertokens-golang/recipe/passwordless/emaildelivery/backwardCompatibilityService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeBackwardCompatibilityService(appInfo supertokens.NormalisedAppinfo, sendPasswordlessLoginEmail func(email string, userInputCode *string, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error) emaildelivery.EmailDeliveryInterface { - passwordlessService := passwordlessBackwardsCompatibilityService.MakeBackwardCompatibilityService(appInfo, sendPasswordlessLoginEmail) - - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin != nil { - return (*passwordlessService.SendEmail)(input, userContext) - - } else { - return errors.New("should never come here") - } - } - - return emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - } -} diff --git a/recipe/thirdpartypasswordless/emaildelivery/smtpService/main.go b/recipe/thirdpartypasswordless/emaildelivery/smtpService/main.go deleted file mode 100644 index 88eee5d6..00000000 --- a/recipe/thirdpartypasswordless/emaildelivery/smtpService/main.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022, 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 smtpService - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - plesssmtpService "github.com/supertokens/supertokens-golang/recipe/passwordless/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeSMTPService(config emaildelivery.SMTPServiceConfig) *emaildelivery.EmailDeliveryInterface { - serviceImpl := makeServiceImplementation(config.Settings) - - if config.Override != nil { - serviceImpl = config.Override(serviceImpl) - } - - passwordlessServiceImpl := plesssmtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: config.Settings, - Override: makePasswordlessServiceImplementation(serviceImpl), - }) - - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin != nil { - return (*passwordlessServiceImpl.SendEmail)(input, userContext) - - } else { - return errors.New("should never come here") - } - } - - return &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - } -} diff --git a/recipe/thirdpartypasswordless/emaildelivery/smtpService/plessServiceImplementation.go b/recipe/thirdpartypasswordless/emaildelivery/smtpService/plessServiceImplementation.go deleted file mode 100644 index c11b4b65..00000000 --- a/recipe/thirdpartypasswordless/emaildelivery/smtpService/plessServiceImplementation.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, 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 smtpService - -import ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func makePasswordlessServiceImplementation(serviceImpl emaildelivery.SMTPInterface) func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - return func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - sendRawEmail := func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - return (*serviceImpl.SendRawEmail)(input, userContext) - } - - getContent := func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - return (*serviceImpl.GetContent)(input, userContext) - } - - return emaildelivery.SMTPInterface{ - SendRawEmail: &sendRawEmail, - GetContent: &getContent, - } - } -} diff --git a/recipe/thirdpartypasswordless/emaildelivery/smtpService/serviceImplementation.go b/recipe/thirdpartypasswordless/emaildelivery/smtpService/serviceImplementation.go deleted file mode 100644 index 90191e25..00000000 --- a/recipe/thirdpartypasswordless/emaildelivery/smtpService/serviceImplementation.go +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2022, 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 smtpService - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - plesssmtpService "github.com/supertokens/supertokens-golang/recipe/passwordless/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func makeServiceImplementation(settings emaildelivery.SMTPSettings) emaildelivery.SMTPInterface { - sendRawEmail := func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - return emaildelivery.SendSMTPEmail(settings, input) - } - - plessServiceImpl := plesssmtpService.MakeServiceImplementation(settings) - - getContent := func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.PasswordlessLogin != nil { - return (*plessServiceImpl.GetContent)(input, userContext) - - } else { - return emaildelivery.EmailContent{}, errors.New("should never come here") - } - } - - return emaildelivery.SMTPInterface{ - SendRawEmail: &sendRawEmail, - GetContent: &getContent, - } -} 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/main.go b/recipe/thirdpartypasswordless/main.go deleted file mode 100644 index ed7c6710..00000000 --- a/recipe/thirdpartypasswordless/main.go +++ /dev/null @@ -1,415 +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 ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/smsdelivery/supertokensService" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/smsdelivery/twilioService" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func Init(config tplmodels.TypeInput) supertokens.Recipe { - return recipeInit(config) -} - -func ThirdPartyManuallyCreateOrUpdateUser(tenantId string, thirdPartyID string, thirdPartyUserID string, email string, userContext ...supertokens.UserContext) (tplmodels.ManuallyCreateOrUpdateUserResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return tplmodels.ManuallyCreateOrUpdateUserResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ThirdPartyManuallyCreateOrUpdateUser)(thirdPartyID, thirdPartyUserID, email, tenantId, userContext[0]) -} - -func ThirdPartyGetProvider(tenantId string, thirdPartyID string, clientType *string, userContext ...supertokens.UserContext) (*tpmodels.TypeProvider, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ThirdPartyGetProvider)(thirdPartyID, clientType, tenantId, userContext[0]) -} - -func GetUserByThirdPartyInfo(tenantId string, thirdPartyID string, thirdPartyUserID string, userContext ...supertokens.UserContext) (*tplmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUserByThirdPartyInfo)(thirdPartyID, thirdPartyUserID, tenantId, userContext[0]) -} - -func GetUserById(userID string, userContext ...supertokens.UserContext) (*tplmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUserByID)(userID, userContext[0]) -} - -func GetUsersByEmail(tenantId string, email string, userContext ...supertokens.UserContext) ([]tplmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUsersByEmail)(email, tenantId, userContext[0]) -} - -func CreateCodeWithEmail(tenantId string, email string, userInputCode *string, userContext ...supertokens.UserContext) (plessmodels.CreateCodeResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return plessmodels.CreateCodeResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.CreateCode)(&email, nil, userInputCode, tenantId, userContext[0]) -} - -func CreateCodeWithPhoneNumber(tenantId string, phoneNumber string, userInputCode *string, userContext ...supertokens.UserContext) (plessmodels.CreateCodeResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return plessmodels.CreateCodeResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.CreateCode)(nil, &phoneNumber, userInputCode, tenantId, userContext[0]) -} - -func CreateNewCodeForDevice(tenantId string, deviceID string, userInputCode *string, userContext ...supertokens.UserContext) (plessmodels.ResendCodeResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return plessmodels.ResendCodeResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.CreateNewCodeForDevice)(deviceID, userInputCode, tenantId, userContext[0]) -} - -func ConsumeCodeWithUserInputCode(tenantId string, deviceID string, userInputCode string, preAuthSessionID string, userContext ...supertokens.UserContext) (tplmodels.ConsumeCodeResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return tplmodels.ConsumeCodeResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ConsumeCode)(&plessmodels.UserInputCodeWithDeviceID{ - Code: userInputCode, - DeviceID: deviceID, - }, nil, preAuthSessionID, tenantId, userContext[0]) -} - -func ConsumeCodeWithLinkCode(tenantId string, linkCode string, preAuthSessionID string, userContext ...supertokens.UserContext) (tplmodels.ConsumeCodeResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return tplmodels.ConsumeCodeResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ConsumeCode)(nil, &linkCode, preAuthSessionID, tenantId, userContext[0]) -} - -func GetUserByID(userID string, userContext ...supertokens.UserContext) (*tplmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUserByID)(userID, userContext[0]) -} - -func GetUserByPhoneNumber(tenantId string, phoneNumber string, userContext ...supertokens.UserContext) (*tplmodels.User, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.GetUserByPhoneNumber)(phoneNumber, tenantId, userContext[0]) -} - -func UpdatePasswordlessUser(userID string, email *string, phoneNumber *string, userContext ...supertokens.UserContext) (plessmodels.UpdateUserResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return plessmodels.UpdateUserResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.UpdatePasswordlessUser)(userID, email, phoneNumber, userContext[0]) -} - -func DeleteEmailForPasswordlessUser(userID string, userContext ...supertokens.UserContext) (plessmodels.DeleteUserResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return plessmodels.DeleteUserResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.DeleteEmailForPasswordlessUser)(userID, userContext[0]) -} - -func DeletePhoneNumberForUser(userID string, userContext ...supertokens.UserContext) (plessmodels.DeleteUserResponse, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return plessmodels.DeleteUserResponse{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.DeletePhoneNumberForUser)(userID, userContext[0]) -} - -func RevokeAllCodesByEmail(tenantId string, email string, userContext ...supertokens.UserContext) error { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.RevokeAllCodes)(&email, nil, tenantId, userContext[0]) -} - -func RevokeAllCodesByPhoneNumber(tenantId string, phoneNumber string, userContext ...supertokens.UserContext) error { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.RevokeAllCodes)(nil, &phoneNumber, tenantId, userContext[0]) -} - -func RevokeCode(tenantId string, codeID string, userContext ...supertokens.UserContext) error { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.RevokeCode)(codeID, tenantId, userContext[0]) -} - -func ListCodesByEmail(tenantId string, email string, userContext ...supertokens.UserContext) ([]plessmodels.DeviceType, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return []plessmodels.DeviceType{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ListCodesByEmail)(email, tenantId, userContext[0]) -} - -func ListCodesByPhoneNumber(tenantId string, phoneNumber string, userContext ...supertokens.UserContext) ([]plessmodels.DeviceType, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return []plessmodels.DeviceType{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ListCodesByPhoneNumber)(phoneNumber, tenantId, userContext[0]) -} - -func ListCodesByDeviceID(tenantId string, deviceID string, userContext ...supertokens.UserContext) (*plessmodels.DeviceType, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ListCodesByDeviceID)(deviceID, tenantId, userContext[0]) -} - -func ListCodesByPreAuthSessionID(tenantId string, preAuthSessionID string, userContext ...supertokens.UserContext) (*plessmodels.DeviceType, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return nil, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.RecipeImpl.ListCodesByPreAuthSessionID)(preAuthSessionID, tenantId, userContext[0]) -} - -func CreateMagicLinkByEmail(tenantId string, email string, userContext ...supertokens.UserContext) (string, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return "", err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return instance.passwordlessRecipe.CreateMagicLink(&email, nil, tenantId, userContext[0]) -} - -func CreateMagicLinkByPhoneNumber(tenantId string, phoneNumber string, userContext ...supertokens.UserContext) (string, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return "", err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return instance.passwordlessRecipe.CreateMagicLink(nil, &phoneNumber, tenantId, userContext[0]) -} - -func PasswordlessSignInUpByEmail(tenantId string, email string, userContext ...supertokens.UserContext) (struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User -}, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User - }{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - resp, err := instance.passwordlessRecipe.SignInUp(&email, nil, tenantId, userContext[0]) - if err != nil { - return struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User - }{}, err - } - return struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User - }{ - PreAuthSessionID: resp.PreAuthSessionID, - CreatedNewUser: resp.CreatedNewUser, - User: tplmodels.User{ - ID: resp.User.ID, - TimeJoined: resp.User.TimeJoined, - Email: resp.User.Email, - PhoneNumber: resp.User.PhoneNumber, - ThirdParty: nil, - }, - }, nil -} - -func PasswordlessSignInUpByPhoneNumber(tenantId string, phoneNumber string, userContext ...supertokens.UserContext) (struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User -}, error) { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User - }{}, err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - resp, err := instance.passwordlessRecipe.SignInUp(nil, &phoneNumber, tenantId, userContext[0]) - if err != nil { - return struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User - }{}, err - } - return struct { - PreAuthSessionID string - CreatedNewUser bool - User tplmodels.User - }{ - PreAuthSessionID: resp.PreAuthSessionID, - CreatedNewUser: resp.CreatedNewUser, - User: tplmodels.User{ - ID: resp.User.ID, - TimeJoined: resp.User.TimeJoined, - Email: resp.User.Email, - PhoneNumber: resp.User.PhoneNumber, - ThirdParty: nil, - }, - }, nil -} - -func SendEmail(input emaildelivery.EmailType, userContext ...supertokens.UserContext) error { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.EmailDelivery.IngredientInterfaceImpl.SendEmail)(input, userContext[0]) -} - -func SendSms(input smsdelivery.SmsType, userContext ...supertokens.UserContext) error { - instance, err := GetRecipeInstanceOrThrowError() - if err != nil { - return err - } - if len(userContext) == 0 { - userContext = append(userContext, &map[string]interface{}{}) - } - return (*instance.SmsDelivery.IngredientInterfaceImpl.SendSms)(input, userContext[0]) -} - -func MakeSMTPService(config emaildelivery.SMTPServiceConfig) *emaildelivery.EmailDeliveryInterface { - return smtpService.MakeSMTPService(config) -} - -func MakeTwilioService(config smsdelivery.TwilioServiceConfig) (*smsdelivery.SmsDeliveryInterface, error) { - return twilioService.MakeTwilioService(config) -} - -func MakeSupertokensSMSService(apiKey string) *smsdelivery.SmsDeliveryInterface { - return supertokensService.MakeSupertokensSMSService(apiKey) -} 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()) - } -} diff --git a/recipe/thirdpartypasswordless/recipe.go b/recipe/thirdpartypasswordless/recipe.go deleted file mode 100644 index e4566516..00000000 --- a/recipe/thirdpartypasswordless/recipe.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 ( - "errors" - "net/http" - - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/api" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/recipeimplementation" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -const RECIPE_ID = "thirdpartypasswordless" - -type Recipe struct { - RecipeModule supertokens.RecipeModule - Config tplmodels.TypeNormalisedInput - passwordlessRecipe *passwordless.Recipe - thirdPartyRecipe *thirdparty.Recipe - RecipeImpl tplmodels.RecipeInterface - APIImpl tplmodels.APIInterface - EmailDelivery emaildelivery.Ingredient - SmsDelivery smsdelivery.Ingredient -} - -var singletonInstance *Recipe - -func MakeRecipe(recipeId string, appInfo supertokens.NormalisedAppinfo, config tplmodels.TypeInput, thirdPartyInstance *thirdparty.Recipe, passwordlessInstance *passwordless.Recipe, emailDeliveryIngredient *emaildelivery.Ingredient, smsDeliveryIngredient *smsdelivery.Ingredient, onSuperTokensAPIError func(err error, req *http.Request, res http.ResponseWriter)) (Recipe, error) { - r := &Recipe{} - r.RecipeModule = supertokens.MakeRecipeModule(recipeId, appInfo, r.handleAPIRequest, r.getAllCORSHeaders, r.getAPIsHandled, nil, r.handleError, onSuperTokensAPIError) - - verifiedConfig, err := validateAndNormaliseUserInput(r, appInfo, config) - if err != nil { - return Recipe{}, err - } - r.Config = verifiedConfig - { - passwordlessquerierInstance, err := supertokens.GetNewQuerierInstanceOrThrowError(passwordless.RECIPE_ID) - if err != nil { - return Recipe{}, err - } - thirdpartyquerierInstance, err := supertokens.GetNewQuerierInstanceOrThrowError(thirdparty.RECIPE_ID) - if err != nil { - return Recipe{}, err - } - - r.RecipeImpl = verifiedConfig.Override.Functions(recipeimplementation.MakeRecipeImplementation(*passwordlessquerierInstance, thirdpartyquerierInstance, verifiedConfig.Providers)) - } - r.APIImpl = verifiedConfig.Override.APIs(api.MakeAPIImplementation()) - - if emailDeliveryIngredient != nil { - r.EmailDelivery = *emailDeliveryIngredient - } else { - r.EmailDelivery = emaildelivery.MakeIngredient(verifiedConfig.GetEmailDeliveryConfig()) - } - - if smsDeliveryIngredient != nil { - r.SmsDelivery = *smsDeliveryIngredient - } else { - r.SmsDelivery = smsdelivery.MakeIngredient(verifiedConfig.GetSmsDeliveryConfig()) - } - - var passwordlessRecipe passwordless.Recipe - if passwordlessInstance == nil { - passwordlessConfig := plessmodels.TypeInput{ - ContactMethodPhone: verifiedConfig.ContactMethodPhone, - ContactMethodEmail: verifiedConfig.ContactMethodEmail, - ContactMethodEmailOrPhone: verifiedConfig.ContactMethodEmailOrPhone, - FlowType: verifiedConfig.FlowType, - GetCustomUserInputCode: verifiedConfig.GetCustomUserInputCode, - Override: &plessmodels.OverrideStruct{ - Functions: func(originalImplementation plessmodels.RecipeInterface) plessmodels.RecipeInterface { - return recipeimplementation.MakePasswordlessRecipeImplementation(r.RecipeImpl) - }, - APIs: func(originalImplementation plessmodels.APIInterface) plessmodels.APIInterface { - return api.GetPasswordlessIterfaceImpl(r.APIImpl) - }, - }, - } - passwordlessRecipe, err = passwordless.MakeRecipe(recipeId, appInfo, passwordlessConfig, &r.EmailDelivery, &r.SmsDelivery, onSuperTokensAPIError) - if err != nil { - return Recipe{}, err - } - r.passwordlessRecipe = &passwordlessRecipe - } else { - r.passwordlessRecipe = passwordlessInstance - } - - if thirdPartyInstance == nil { - thirdPartyConfig := &tpmodels.TypeInput{ - SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{ - Providers: verifiedConfig.Providers, - }, - Override: &tpmodels.OverrideStruct{ - Functions: func(_ tpmodels.RecipeInterface) tpmodels.RecipeInterface { - return recipeimplementation.MakeThirdPartyRecipeImplementation(r.RecipeImpl) - }, - APIs: func(_ tpmodels.APIInterface) tpmodels.APIInterface { - return api.GetThirdPartyIterfaceImpl(r.APIImpl) - }, - }, - } - thirdPartyRecipeinstance, err := thirdparty.MakeRecipe(recipeId, appInfo, thirdPartyConfig, &r.EmailDelivery, onSuperTokensAPIError) - if err != nil { - return Recipe{}, err - } - r.thirdPartyRecipe = &thirdPartyRecipeinstance - } else { - r.thirdPartyRecipe = thirdPartyInstance - } - - return *r, nil -} - -func recipeInit(config tplmodels.TypeInput) supertokens.Recipe { - return func(appInfo supertokens.NormalisedAppinfo, onSuperTokensAPIError func(err error, req *http.Request, res http.ResponseWriter)) (*supertokens.RecipeModule, error) { - if singletonInstance == nil { - recipe, err := MakeRecipe(RECIPE_ID, appInfo, config, nil, nil, nil, nil, onSuperTokensAPIError) - if err != nil { - return nil, err - } - singletonInstance = &recipe - return &singletonInstance.RecipeModule, nil - } - return nil, errors.New("ThirdPartyPasswordless recipe has already been initialised. Please check your code for bugs.") - } -} - -func GetRecipeInstanceOrThrowError() (*Recipe, error) { - if singletonInstance != nil { - return singletonInstance, nil - } - return nil, errors.New("Initialisation not done. Did you forget to call the init function?") -} - -func GetRecipeInstance() *Recipe { - return singletonInstance -} - -// implement RecipeModule - -func (r *Recipe) getAPIsHandled() ([]supertokens.APIHandled, error) { - passwordlessAPIhandled, err := r.passwordlessRecipe.RecipeModule.GetAPIsHandled() - if err != nil { - return nil, err - } - apisHandled := passwordlessAPIhandled - if r.thirdPartyRecipe != nil { - thirdpartyAPIhandled, err := r.thirdPartyRecipe.RecipeModule.GetAPIsHandled() - if err != nil { - return nil, err - } - apisHandled = append(apisHandled, thirdpartyAPIhandled...) - } - return apisHandled, nil -} - -func (r *Recipe) handleAPIRequest(id string, tenantId string, req *http.Request, res http.ResponseWriter, theirHandler http.HandlerFunc, path supertokens.NormalisedURLPath, method string, userContext supertokens.UserContext) error { - ok, _, err := r.passwordlessRecipe.RecipeModule.ReturnAPIIdIfCanHandleRequest(path, method, userContext) - if err != nil { - return err - } - if ok != nil { - return r.passwordlessRecipe.RecipeModule.HandleAPIRequest(id, tenantId, req, res, theirHandler, path, method, userContext) - } - if r.thirdPartyRecipe != nil { - ok, _, err := r.thirdPartyRecipe.RecipeModule.ReturnAPIIdIfCanHandleRequest(path, method, userContext) - if err != nil { - return err - } - if ok != nil { - return r.thirdPartyRecipe.RecipeModule.HandleAPIRequest(id, tenantId, req, res, theirHandler, path, method, userContext) - } - } - return errors.New("should not come here") -} - -func (r *Recipe) getAllCORSHeaders() []string { - corsHeaders := r.passwordlessRecipe.RecipeModule.GetAllCORSHeaders() - if r.thirdPartyRecipe != nil { - corsHeaders = append(corsHeaders, r.thirdPartyRecipe.RecipeModule.GetAllCORSHeaders()...) - } - return corsHeaders -} - -func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) { - handleError, err := r.passwordlessRecipe.RecipeModule.HandleError(err, req, res, userContext) - if err != nil || handleError { - return handleError, err - } - if r.thirdPartyRecipe != nil { - handleError, err = r.thirdPartyRecipe.RecipeModule.HandleError(err, req, res, userContext) - if err != nil || handleError { - return handleError, err - } - } - return false, nil -} - -func ResetForTest() { - singletonInstance = nil -} diff --git a/recipe/thirdpartypasswordless/recipeFunctions_test.go b/recipe/thirdpartypasswordless/recipeFunctions_test.go deleted file mode 100644 index 34e68d40..00000000 --- a/recipe/thirdpartypasswordless/recipeFunctions_test.go +++ /dev/null @@ -1,1150 +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" - "net/url" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" - "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" -) - -func TestWithThirdPartyPasswordlessForThirdPartyUserThatIsEmailVerifiedReturnsTheCorrectEmailVerificationStatus(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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - 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", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - customProvider1, - }, - }), - }, - } - - 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 - } - - resp, err := ThirdPartyManuallyCreateOrUpdateUser("public", "custom", "verifiedUser", "test@example.com") - assert.NoError(t, err) - - emailVerificationToken, err := emailverification.CreateEmailVerificationToken("public", resp.OK.User.ID, nil) - assert.NoError(t, err) - - emailverification.VerifyEmailUsingToken("public", emailVerificationToken.OK.Token) - - isVerfied, err := emailverification.IsEmailVerified(resp.OK.User.ID, nil) - assert.NoError(t, err) - assert.True(t, isVerfied) - - resp1, err := ThirdPartyManuallyCreateOrUpdateUser("public", "custom2", "NotVerifiedUser", "test@example.com") - assert.NoError(t, err) - - isVerfied1, err := emailverification.IsEmailVerified(resp1.OK.User.ID, nil) - assert.NoError(t, err) - assert.False(t, isVerfied1) -} - -func TestWithThirdPartyPasswordlessForPasswordlessUserThatIsEmailVerifiedReturnsTrueForPhoneAndFalseForEmail(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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - 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", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - response, err := PasswordlessSignInUpByEmail("public", "test@example.com") - assert.NoError(t, err) - - isVerified, err := emailverification.IsEmailVerified(response.User.ID, nil) - assert.NoError(t, err) - assert.False(t, isVerified) // this is a change in behavior - - emailVerificationResp, err := emailverification.CreateEmailVerificationToken("public", response.User.ID, nil) - assert.NoError(t, err) - assert.Nil(t, emailVerificationResp.EmailAlreadyVerifiedError) - assert.NotNil(t, emailVerificationResp.OK) - - response, err = PasswordlessSignInUpByPhoneNumber("public", "+123456789012") - assert.NoError(t, err) - - isVerified, err = emailverification.IsEmailVerified(response.User.ID, nil) - assert.NoError(t, err) - assert.True(t, isVerified) - - emailVerificationResp, err = emailverification.CreateEmailVerificationToken("public", response.User.ID, nil) - assert.NoError(t, err) - assert.NotNil(t, emailVerificationResp.EmailAlreadyVerifiedError) - assert.Nil(t, emailVerificationResp.OK) -} - -func TestWithThirdPartyPasswordlessGetUserFunctionality(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", - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - user, err := GetUserByID("random") - assert.NoError(t, err) - assert.Nil(t, user) - - resp, err := PasswordlessSignInUpByEmail("public", "test@example.com") - assert.NoError(t, err) - userId := resp.User.ID - - user, err = GetUserByID(userId) - assert.NoError(t, err) - assert.NotNil(t, user) - - assert.Equal(t, userId, user.ID) - assert.Equal(t, resp.User.Email, user.Email) - assert.Nil(t, user.PhoneNumber) - - users, err := GetUsersByEmail("public", "random") - assert.NoError(t, err) - assert.Equal(t, 0, len(users)) - - users, err = GetUsersByEmail("public", "test@example.com") - assert.NoError(t, err) - assert.Equal(t, 1, len(users)) - - userInfo := users[0] - - assert.Equal(t, user.Email, userInfo.Email) - assert.Equal(t, user.ID, userInfo.ID) - assert.Equal(t, user.PhoneNumber, userInfo.PhoneNumber) - assert.Nil(t, userInfo.PhoneNumber) - assert.Nil(t, userInfo.ThirdParty) - assert.Equal(t, user.TimeJoined, userInfo.TimeJoined) - - user, err = GetUserByPhoneNumber("public", "random") - assert.NoError(t, err) - assert.Nil(t, user) - - resp, err = PasswordlessSignInUpByPhoneNumber("public", "+1234567890") - assert.NoError(t, err) - - user, err = GetUserByPhoneNumber("public", *resp.User.PhoneNumber) - assert.NoError(t, err) - assert.NotNil(t, user) - - assert.Equal(t, user.Email, resp.User.Email) - assert.Equal(t, user.ID, resp.User.ID) - assert.Equal(t, user.PhoneNumber, resp.User.PhoneNumber) - assert.Equal(t, user.ThirdParty, resp.User.ThirdParty) - assert.Equal(t, user.ThirdParty, resp.User.ThirdParty) - assert.Nil(t, user.Email) - assert.Nil(t, user.ThirdParty) -} - -func TestWithThirdPartyPasswordlessCreateCodeTest(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, - }, - }), - }, - } - - 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 - } - - resp, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - assert.NotNil(t, resp.OK) - assert.NotNil(t, resp.OK.CodeID) - assert.NotNil(t, resp.OK.PreAuthSessionID) - assert.NotNil(t, resp.OK.CodeLifetime) - assert.NotNil(t, resp.OK.DeviceID) - assert.NotNil(t, resp.OK.LinkCode) - assert.NotNil(t, resp.OK.TimeCreated) - assert.NotNil(t, resp.OK.UserInputCode) - - userInputCode := "23123" - resp1, err := CreateCodeWithEmail("public", "test@example.com", &userInputCode) - assert.NoError(t, err) - assert.NotNil(t, resp1.OK) - assert.NotNil(t, resp1.OK.CodeID) - assert.NotNil(t, resp1.OK.PreAuthSessionID) - assert.NotNil(t, resp1.OK.CodeLifetime) - assert.NotNil(t, resp1.OK.DeviceID) - assert.NotNil(t, resp1.OK.LinkCode) - assert.NotNil(t, resp1.OK.TimeCreated) - assert.NotNil(t, resp1.OK.UserInputCode) -} - -func TestThirdPartyPasswordlessCreateNewCodeFromDevice(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, - }, - }), - }, - } - - 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 - } - - resp, err := CreateCodeWithEmail("public", "test@example.com", nil) - - assert.NoError(t, err) - - resp1, err := CreateNewCodeForDevice("public", resp.OK.DeviceID, nil) - assert.NoError(t, err) - - assert.NoError(t, err) - assert.NotNil(t, resp1.OK) - assert.NotNil(t, resp1.OK.CodeID) - assert.NotNil(t, resp1.OK.PreAuthSessionID) - assert.NotNil(t, resp1.OK.CodeLifetime) - assert.NotNil(t, resp1.OK.DeviceID) - assert.NotNil(t, resp1.OK.LinkCode) - assert.NotNil(t, resp1.OK.TimeCreated) - assert.NotNil(t, resp1.OK.UserInputCode) - - resp, err = CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - userInputCode := "2314" - resp1, err = CreateNewCodeForDevice("public", resp.OK.DeviceID, &userInputCode) - assert.NoError(t, err) - - assert.NoError(t, err) - assert.NotNil(t, resp1.OK) - assert.NotNil(t, resp1.OK.CodeID) - assert.NotNil(t, resp1.OK.PreAuthSessionID) - assert.NotNil(t, resp1.OK.CodeLifetime) - assert.NotNil(t, resp1.OK.DeviceID) - assert.NotNil(t, resp1.OK.LinkCode) - assert.NotNil(t, resp1.OK.TimeCreated) - assert.NotNil(t, resp1.OK.UserInputCode) - - resp, err = CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - resp1, err = CreateNewCodeForDevice("public", "random", nil) - assert.NoError(t, err) - assert.NotNil(t, resp1.RestartFlowError) - assert.Nil(t, resp1.OK) - - resp, err = CreateCodeWithEmail("public", "test@example.com", &userInputCode) - assert.NoError(t, err) - - resp1, err = CreateNewCodeForDevice("public", resp.OK.DeviceID, &userInputCode) - assert.NoError(t, err) - assert.NotNil(t, resp1.UserInputCodeAlreadyUsedError) - assert.Nil(t, resp1.OK) -} - -func TestThirdPartyPasswordlessConsumeCoed(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, - }, - }), - }, - } - - 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 - } - - resp, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - result, err := ConsumeCodeWithUserInputCode("public", resp.OK.DeviceID, resp.OK.UserInputCode, resp.OK.PreAuthSessionID) - assert.NoError(t, err) - - assert.NotNil(t, result.OK) - assert.True(t, result.OK.CreatedNewUser) - assert.Equal(t, "test@example.com", *result.OK.User.Email) - assert.NotNil(t, result.OK) - assert.NotNil(t, result.OK.User) - assert.NotNil(t, result.OK.User.ID) - assert.NotNil(t, result.OK.User.TimeJoined) - assert.Nil(t, result.OK.User.PhoneNumber) - assert.Nil(t, result.OK.User.ThirdParty) - - resp, err = CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - result, err = ConsumeCodeWithUserInputCode("public", resp.OK.DeviceID, "random", resp.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, result.IncorrectUserInputCodeError) - assert.Equal(t, 1, result.IncorrectUserInputCodeError.FailedCodeInputAttemptCount) - assert.Equal(t, 5, result.IncorrectUserInputCodeError.MaximumCodeInputAttempts) - - resp, err = CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - _, err = ConsumeCodeWithUserInputCode("public", resp.OK.DeviceID, resp.OK.UserInputCode, "random") - assert.Contains(t, err.Error(), "preAuthSessionId and deviceId doesn't match") -} - -func TestThirdPartyPasswordlessConsumeCodeTestWithExpiredUserInputCodeError(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, - }, - }), - }, - } - - BeforeEach() - unittesting.SetKeyValueInConfig("passwordless_code_lifetime", "1000") - 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 - } - - resp, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - time.Sleep(2 * time.Second) - - result, err := ConsumeCodeWithUserInputCode("public", resp.OK.DeviceID, resp.OK.UserInputCode, resp.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, result.ExpiredUserInputCodeError) - assert.Equal(t, 1, result.ExpiredUserInputCodeError.FailedCodeInputAttemptCount) - assert.Equal(t, 5, result.ExpiredUserInputCodeError.MaximumCodeInputAttempts) - assert.Nil(t, result.OK) -} - -func TestThirdPartyPasswordlessUpdateUserContactMethodEmailTest(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, - }, - }), - }, - } - - 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 - } - - userInfo, err := PasswordlessSignInUpByEmail("public", "test@example.com") - assert.NoError(t, err) - - updatedEmail := "test2@example.com" - resp, err := UpdatePasswordlessUser(userInfo.User.ID, &updatedEmail, nil) - assert.NoError(t, err) - assert.NotNil(t, resp.OK) - - user, err := GetUserByID(userInfo.User.ID) - assert.NoError(t, err) - assert.Equal(t, updatedEmail, *user.Email) - - resp, err = UpdatePasswordlessUser("random", &updatedEmail, nil) - assert.NoError(t, err) - assert.Nil(t, resp.OK) - assert.NotNil(t, resp.UnknownUserIdError) - - userInfo2, err := PasswordlessSignInUpByEmail("public", "test3@example.com") - assert.NoError(t, err) - - resp, err = UpdatePasswordlessUser(userInfo2.User.ID, &updatedEmail, nil) - assert.NoError(t, err) - assert.Nil(t, resp.OK) - assert.NotNil(t, resp.EmailAlreadyExistsError) -} - -func TestThirdPartyPasswordlessUpdateUserContactPhone(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - phoneNumber_1 := "+1234567891" - phoneNumber_2 := "+1234567892" - phoneNumber_3 := "+1234567893" - - userInfo, err := PasswordlessSignInUpByPhoneNumber("public", phoneNumber_1) - assert.NoError(t, err) - - res1, err := UpdatePasswordlessUser(userInfo.User.ID, nil, &phoneNumber_2) - assert.NoError(t, err) - - assert.NotNil(t, res1.OK) - - result, err := GetUserByID(userInfo.User.ID) - assert.NoError(t, err) - - assert.Equal(t, phoneNumber_2, *result.PhoneNumber) - - userInfo1, err := PasswordlessSignInUpByPhoneNumber("public", phoneNumber_3) - assert.NoError(t, err) - - res1, err = UpdatePasswordlessUser(userInfo1.User.ID, nil, &phoneNumber_2) - assert.NoError(t, err) - - assert.Nil(t, res1.OK) - assert.NotNil(t, res1.PhoneNumberAlreadyExistsError) -} - -func TestThirdPartyPasswordlessRevokeAllCodesTest(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, - }, - }), - }, - } - - 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 - } - - codeInfo1, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - codeInfo2, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - err = RevokeAllCodesByEmail("public", "test@example.com") - assert.NoError(t, err) - - result1, err := ConsumeCodeWithUserInputCode("public", codeInfo1.OK.DeviceID, codeInfo1.OK.UserInputCode, codeInfo1.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, result1.RestartFlowError) - assert.Nil(t, result1.OK) - - result2, err := ConsumeCodeWithUserInputCode("public", codeInfo2.OK.DeviceID, codeInfo2.OK.UserInputCode, codeInfo2.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, result2.RestartFlowError) - assert.Nil(t, result2.OK) -} - -func TestThirdPartyPasswordlessRevokeCode(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, - }, - }), - }, - } - - 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 - } - - codeInfo1, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - codeInfo2, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - err = RevokeCode("public", codeInfo1.OK.CodeID) - assert.NoError(t, err) - - result1, err := ConsumeCodeWithUserInputCode("public", codeInfo1.OK.DeviceID, codeInfo1.OK.UserInputCode, codeInfo1.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, result1.RestartFlowError) - assert.Nil(t, result1.OK) - - result2, err := ConsumeCodeWithUserInputCode("public", codeInfo2.OK.DeviceID, codeInfo2.OK.UserInputCode, codeInfo2.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.Nil(t, result2.RestartFlowError) - assert.NotNil(t, result2.OK) -} - -func TestThirdPartyPasswordlessListCodesByEmail(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, - }, - }), - }, - } - - 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 - } - - codeInfo1, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - codeInfo2, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - - result, err := ListCodesByEmail("public", "test@example.com") - assert.NoError(t, err) - - assert.Equal(t, 2, len(result)) - for _, dt := range result { - for _, c := range dt.Codes { - if !(c.CodeID == codeInfo1.OK.CodeID || c.CodeID == codeInfo2.OK.CodeID) { - t.Fail() - } - } - } -} - -func TestListCodesByPhoneNumber(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - codeInfo1, err := CreateCodeWithEmail("public", "+1234567890", nil) - assert.NoError(t, err) - - codeInfo2, err := CreateCodeWithEmail("public", "+1234567890", nil) - assert.NoError(t, err) - - result, err := ListCodesByEmail("public", "+1234567890") - assert.NoError(t, err) - - assert.Equal(t, 2, len(result)) - for _, dt := range result { - for _, c := range dt.Codes { - if !(c.CodeID == codeInfo1.OK.CodeID || c.CodeID == codeInfo2.OK.CodeID) { - t.Fail() - } - } - } -} - -func TestThirdPartyPasswordlessListCodesByDeviceIdAndListCodesByPreAuthSessionId(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - codeInfo1, err := CreateCodeWithEmail("public", "+1234567890", nil) - assert.NoError(t, err) - - result, err := ListCodesByDeviceID("public", codeInfo1.OK.DeviceID) - assert.NoError(t, err) - - assert.Equal(t, codeInfo1.OK.CodeID, result.Codes[0].CodeID) - - result, err = ListCodesByPreAuthSessionID("public", codeInfo1.OK.PreAuthSessionID) - assert.NoError(t, err) - - assert.Equal(t, codeInfo1.OK.CodeID, result.Codes[0].CodeID) -} - -func TestCreateMagicLinkTest(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - result, err := CreateMagicLinkByPhoneNumber("public", "+1234567890") - assert.NoError(t, err) - - magicLinkURL, err := url.Parse(result) - assert.NoError(t, err) - - assert.Equal(t, "supertokens.io", magicLinkURL.Host) - assert.Equal(t, "/auth/verify", magicLinkURL.Path) - assert.Equal(t, "thirdpartypasswordless", magicLinkURL.Query().Get("rid")) - assert.NotNil(t, magicLinkURL.Query().Get("preAuthSessionId")) -} - -func TestThirdPartyPasswordlessSignInUp(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", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - }), - }, - } - - 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 - } - - result, err := PasswordlessSignInUpByPhoneNumber("public", "+12345678901") - assert.NoError(t, err) - assert.NotNil(t, result.User) - assert.True(t, result.CreatedNewUser) - assert.Equal(t, "+12345678901", *result.User.PhoneNumber) - assert.NotNil(t, result.User.TimeJoined) - assert.NotNil(t, result.User.ID) - assert.Nil(t, result.User.ThirdParty) -} diff --git a/recipe/thirdpartypasswordless/recipeimplementation/main.go b/recipe/thirdpartypasswordless/recipeimplementation/main.go deleted file mode 100644 index b1cb8f0e..00000000 --- a/recipe/thirdpartypasswordless/recipeimplementation/main.go +++ /dev/null @@ -1,420 +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 recipeimplementation - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/recipe/passwordless" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeRecipeImplementation(passwordlessQuerier supertokens.Querier, thirdPartyQuerier *supertokens.Querier, providers []tpmodels.ProviderInput) tplmodels.RecipeInterface { - result := tplmodels.RecipeInterface{} - - passwordlessImplementation := passwordless.MakeRecipeImplementation(passwordlessQuerier) - var thirdPartyImplementation *tpmodels.RecipeInterface - if thirdPartyQuerier != nil { - thirdPartyImplementationTemp := thirdparty.MakeRecipeImplementation(*thirdPartyQuerier, providers) - thirdPartyImplementation = &thirdPartyImplementationTemp - } - - var ogSignInUp func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) = nil - if thirdPartyImplementation != nil { - ogSignInUp = *thirdPartyImplementation.SignInUp - } - thirPartySignInUp := func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tplmodels.ThirdPartySignInUp, error) { - if ogSignInUp == nil { - return tplmodels.ThirdPartySignInUp{}, errors.New("no thirdparty provider configured") - } - result, err := ogSignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - if err != nil { - return tplmodels.ThirdPartySignInUp{}, err - } - return tplmodels.ThirdPartySignInUp{ - OK: &struct { - CreatedNewUser bool - User tplmodels.User - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tplmodels.User{ - ID: result.OK.User.ID, - TimeJoined: result.OK.User.TimeJoined, - Email: &result.OK.User.Email, - ThirdParty: &result.OK.User.ThirdParty, - }, - }, - }, nil - } - - var ogManuallyCreateOrUpdateUser func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (tpmodels.ManuallyCreateOrUpdateUserResponse, error) = nil - if thirdPartyImplementation != nil { - ogManuallyCreateOrUpdateUser = *thirdPartyImplementation.ManuallyCreateOrUpdateUser - } - - thirdPartyManuallyCreateOrUpdateUser := func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (tplmodels.ManuallyCreateOrUpdateUserResponse, error) { - if ogManuallyCreateOrUpdateUser == nil { - return tplmodels.ManuallyCreateOrUpdateUserResponse{}, errors.New("no thirdparty provider configured") - } - result, err := ogManuallyCreateOrUpdateUser(thirdPartyID, thirdPartyUserID, email, tenantId, userContext) - if err != nil { - return tplmodels.ManuallyCreateOrUpdateUserResponse{}, err - } - return tplmodels.ManuallyCreateOrUpdateUserResponse{ - OK: &struct { - CreatedNewUser bool - User tplmodels.User - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tplmodels.User{ - ID: result.OK.User.ID, - TimeJoined: result.OK.User.TimeJoined, - Email: &result.OK.User.Email, - ThirdParty: &result.OK.User.ThirdParty, - }, - }, - }, nil - } - - var ogGetProvider func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) = nil - if thirdPartyImplementation != nil { - ogGetProvider = *thirdPartyImplementation.GetProvider - } - - thirdPartyGetProvider := func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) { - if ogGetProvider == nil { - return nil, errors.New("no thirdparty provider configured") - } - return ogGetProvider(thirdPartyID, clientType, tenantId, userContext) - } - - ogPlessGetUserByID := *passwordlessImplementation.GetUserByID - var ogTPGetUserById func(userID string, userContext supertokens.UserContext) (*tpmodels.User, error) = nil - if thirdPartyImplementation != nil { - ogTPGetUserById = *thirdPartyImplementation.GetUserByID - } - getUserByID := func(userID string, userContext supertokens.UserContext) (*tplmodels.User, error) { - user, err := ogPlessGetUserByID(userID, userContext) - if err != nil { - return nil, err - } - if user != nil { - return &tplmodels.User{ - ID: user.ID, - Email: user.Email, - PhoneNumber: user.PhoneNumber, - TimeJoined: user.TimeJoined, - TenantIds: user.TenantIds, - ThirdParty: nil, - }, nil - } - if ogTPGetUserById == nil { - return nil, nil - } - - userinfo, err := ogTPGetUserById(userID, userContext) - if err != nil { - return nil, err - } - - if userinfo != nil { - return &tplmodels.User{ - ID: userinfo.ID, - Email: &userinfo.Email, - PhoneNumber: nil, - TimeJoined: userinfo.TimeJoined, - TenantIds: userinfo.TenantIds, - ThirdParty: &userinfo.ThirdParty, - }, nil - } - return nil, nil - } - - ogPlessGetUserByEmail := *passwordlessImplementation.GetUserByEmail - var ogTPGetUsersByEmail func(email string, tenantId string, userContext supertokens.UserContext) ([]tpmodels.User, error) = nil - if thirdPartyImplementation != nil { - ogTPGetUsersByEmail = *thirdPartyImplementation.GetUsersByEmail - } - getUsersByEmail := func(email string, tenantId string, userContext supertokens.UserContext) ([]tplmodels.User, error) { - fromPless, err := ogPlessGetUserByEmail(email, tenantId, userContext) - if err != nil { - return []tplmodels.User{}, err - } - - fromTP := []tpmodels.User{} - if ogTPGetUsersByEmail != nil { - fromTP, err = ogTPGetUsersByEmail(email, tenantId, userContext) - if err != nil { - return []tplmodels.User{}, err - } - } - finalResult := []tplmodels.User{} - - if fromPless != nil { - finalResult = append(finalResult, tplmodels.User{ - ID: fromPless.ID, - TimeJoined: fromPless.TimeJoined, - Email: fromPless.Email, - PhoneNumber: fromPless.PhoneNumber, - ThirdParty: nil, - }) - } - - for _, tpUser := range fromTP { - finalResult = append(finalResult, tplmodels.User{ - ID: tpUser.ID, - TimeJoined: tpUser.TimeJoined, - Email: &tpUser.Email, - PhoneNumber: nil, - ThirdParty: &tpUser.ThirdParty, - }) - } - - return finalResult, nil - } - - var ogGetUserByThirdPartyInfo func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*tpmodels.User, error) = nil - if thirdPartyImplementation != nil { - ogGetUserByThirdPartyInfo = *thirdPartyImplementation.GetUserByThirdPartyInfo - } - getUserByThirdPartyInfo := func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*tplmodels.User, error) { - if ogGetUserByThirdPartyInfo == nil { - return nil, nil - } - - userinfo, err := ogGetUserByThirdPartyInfo(thirdPartyID, thirdPartyUserID, tenantId, userContext) - if err != nil { - return nil, err - } - - if userinfo != nil { - return &tplmodels.User{ - ID: userinfo.ID, - Email: &userinfo.Email, - PhoneNumber: nil, - TimeJoined: userinfo.TimeJoined, - TenantIds: userinfo.TenantIds, - ThirdParty: &userinfo.ThirdParty, - }, nil - } - return nil, nil - } - - ogCreateCode := *passwordlessImplementation.CreateCode - createCode := func(email *string, phoneNumber *string, userInputCode *string, tenantId string, userContext supertokens.UserContext) (plessmodels.CreateCodeResponse, error) { - return ogCreateCode(email, phoneNumber, userInputCode, tenantId, userContext) - } - - ogConsumeCode := *passwordlessImplementation.ConsumeCode - consumeCode := func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (tplmodels.ConsumeCodeResponse, error) { - response, err := ogConsumeCode(userInput, linkCode, preAuthSessionID, tenantId, userContext) - if err != nil { - return tplmodels.ConsumeCodeResponse{}, err - } - - if response.ExpiredUserInputCodeError != nil { - return tplmodels.ConsumeCodeResponse{ - ExpiredUserInputCodeError: response.ExpiredUserInputCodeError, - }, nil - } else if response.IncorrectUserInputCodeError != nil { - return tplmodels.ConsumeCodeResponse{ - IncorrectUserInputCodeError: response.IncorrectUserInputCodeError, - }, nil - } else if response.RestartFlowError != nil { - return tplmodels.ConsumeCodeResponse{ - RestartFlowError: &struct{}{}, - }, nil - } - - return tplmodels.ConsumeCodeResponse{ - OK: &struct { - CreatedNewUser bool - User tplmodels.User - }{ - CreatedNewUser: response.OK.CreatedNewUser, - User: tplmodels.User{ - ID: response.OK.User.ID, - TimeJoined: response.OK.User.TimeJoined, - Email: response.OK.User.Email, - PhoneNumber: response.OK.User.PhoneNumber, - ThirdParty: nil, - }, - }, - }, nil - } - - ogCreateNewCodeForDevice := *passwordlessImplementation.CreateNewCodeForDevice - createNewCodeForDevice := func(deviceID string, userInputCode *string, tenantId string, userContext supertokens.UserContext) (plessmodels.ResendCodeResponse, error) { - return ogCreateNewCodeForDevice(deviceID, userInputCode, tenantId, userContext) - } - - ogGetUserByPhoneNumber := *passwordlessImplementation.GetUserByPhoneNumber - getUserByPhoneNumber := func(phoneNumber string, tenantId string, userContext supertokens.UserContext) (*tplmodels.User, error) { - resp, err := ogGetUserByPhoneNumber(phoneNumber, tenantId, userContext) - if err != nil { - return &tplmodels.User{}, err - } - - if resp == nil { - return nil, nil - } - - return &tplmodels.User{ - ID: resp.ID, - TimeJoined: resp.TimeJoined, - Email: resp.Email, - PhoneNumber: resp.PhoneNumber, - ThirdParty: nil, - }, nil - } - - ogListCodesByDeviceID := *passwordlessImplementation.ListCodesByDeviceID - listCodesByDeviceID := func(deviceID string, tenantId string, userContext supertokens.UserContext) (*plessmodels.DeviceType, error) { - return ogListCodesByDeviceID(deviceID, tenantId, userContext) - } - - ogListCodesByEmail := *passwordlessImplementation.ListCodesByEmail - listCodesByEmail := func(email string, tenantId string, userContext supertokens.UserContext) ([]plessmodels.DeviceType, error) { - return ogListCodesByEmail(email, tenantId, userContext) - } - - ogListCodesByPhoneNumber := *passwordlessImplementation.ListCodesByPhoneNumber - listCodesByPhoneNumber := func(phoneNumber string, tenantId string, userContext supertokens.UserContext) ([]plessmodels.DeviceType, error) { - return ogListCodesByPhoneNumber(phoneNumber, tenantId, userContext) - } - - ogListCodesByPreAuthSessionID := *passwordlessImplementation.ListCodesByPreAuthSessionID - listCodesByPreAuthSessionID := func(preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (*plessmodels.DeviceType, error) { - return ogListCodesByPreAuthSessionID(preAuthSessionID, tenantId, userContext) - } - - ogRevokeAllCodes := *passwordlessImplementation.RevokeAllCodes - revokeAllCodes := func(email *string, phoneNumber *string, tenantId string, userContext supertokens.UserContext) error { - return ogRevokeAllCodes(email, phoneNumber, tenantId, userContext) - } - - ogRevokeCode := *passwordlessImplementation.RevokeCode - revokeCode := func(codeID string, tenantId string, userContext supertokens.UserContext) error { - return ogRevokeCode(codeID, tenantId, userContext) - } - - ogUpdateUser := *passwordlessImplementation.UpdateUser - updatePasswordlessUser := func(userID string, email *string, phoneNumber *string, userContext supertokens.UserContext) (plessmodels.UpdateUserResponse, error) { - user, err := (*result.GetUserByID)(userID, userContext) - if err != nil { - return plessmodels.UpdateUserResponse{}, err - } - - if user == nil { - return plessmodels.UpdateUserResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } else if user.ThirdParty != nil { - return plessmodels.UpdateUserResponse{}, errors.New("cannot update passwordless user info for those who signed up using third party login") - } - return ogUpdateUser(userID, email, phoneNumber, userContext) - } - - ogDeleteEmailForUser := *passwordlessImplementation.DeleteEmailForUser - deleteEmailForPasswordlessUser := func(userID string, userContext supertokens.UserContext) (plessmodels.DeleteUserResponse, error) { - user, err := (*result.GetUserByID)(userID, userContext) - if err != nil { - return plessmodels.DeleteUserResponse{}, err - } - - if user == nil { - return plessmodels.DeleteUserResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } else if user.ThirdParty != nil { - return plessmodels.DeleteUserResponse{}, errors.New("cannot update passwordless user info for those who signed up using third party login") - } - return ogDeleteEmailForUser(userID, userContext) - } - - ogDeletePhoneNumberForUser := *passwordlessImplementation.DeletePhoneNumberForUser - deletePhoneNumberForPasswordlessUser := func(userID string, userContext supertokens.UserContext) (plessmodels.DeleteUserResponse, error) { - user, err := (*result.GetUserByID)(userID, userContext) - if err != nil { - return plessmodels.DeleteUserResponse{}, err - } - - if user == nil { - return plessmodels.DeleteUserResponse{ - UnknownUserIdError: &struct{}{}, - }, nil - } else if user.ThirdParty != nil { - return plessmodels.DeleteUserResponse{}, errors.New("cannot update passwordless user info for those who signed up using third party login") - } - return ogDeletePhoneNumberForUser(userID, userContext) - } - - result.GetUserByID = &getUserByID - result.GetUsersByEmail = &getUsersByEmail - result.GetUserByThirdPartyInfo = &getUserByThirdPartyInfo - result.ThirdPartySignInUp = &thirPartySignInUp - result.ThirdPartyManuallyCreateOrUpdateUser = &thirdPartyManuallyCreateOrUpdateUser - result.ThirdPartyGetProvider = &thirdPartyGetProvider - result.ConsumeCode = &consumeCode - result.CreateCode = &createCode - result.CreateNewCodeForDevice = &createNewCodeForDevice - result.GetUserByPhoneNumber = &getUserByPhoneNumber - result.ListCodesByDeviceID = &listCodesByDeviceID - result.ListCodesByEmail = &listCodesByEmail - result.ListCodesByPhoneNumber = &listCodesByPhoneNumber - result.ListCodesByPreAuthSessionID = &listCodesByPreAuthSessionID - result.RevokeAllCodes = &revokeAllCodes - result.RevokeCode = &revokeCode - result.UpdatePasswordlessUser = &updatePasswordlessUser - result.DeleteEmailForPasswordlessUser = &deleteEmailForPasswordlessUser - result.DeletePhoneNumberForUser = &deletePhoneNumberForPasswordlessUser - - modifiedPwdless := MakePasswordlessRecipeImplementation(result) - (*passwordlessImplementation.ConsumeCode) = *modifiedPwdless.ConsumeCode - (*passwordlessImplementation.CreateCode) = *modifiedPwdless.CreateCode - (*passwordlessImplementation.CreateNewCodeForDevice) = *modifiedPwdless.CreateNewCodeForDevice - (*passwordlessImplementation.GetUserByEmail) = *modifiedPwdless.GetUserByEmail - (*passwordlessImplementation.GetUserByID) = *modifiedPwdless.GetUserByID - (*passwordlessImplementation.GetUserByPhoneNumber) = *modifiedPwdless.GetUserByPhoneNumber - (*passwordlessImplementation.ListCodesByDeviceID) = *modifiedPwdless.ListCodesByDeviceID - (*passwordlessImplementation.ListCodesByEmail) = *modifiedPwdless.ListCodesByEmail - (*passwordlessImplementation.ListCodesByPhoneNumber) = *modifiedPwdless.ListCodesByPhoneNumber - (*passwordlessImplementation.ListCodesByPreAuthSessionID) = *modifiedPwdless.ListCodesByPreAuthSessionID - (*passwordlessImplementation.RevokeAllCodes) = *modifiedPwdless.RevokeAllCodes - (*passwordlessImplementation.RevokeCode) = *modifiedPwdless.RevokeCode - (*passwordlessImplementation.UpdateUser) = *modifiedPwdless.UpdateUser - (*passwordlessImplementation.DeleteEmailForUser) = *modifiedPwdless.DeleteEmailForUser - (*passwordlessImplementation.DeletePhoneNumberForUser) = *modifiedPwdless.DeletePhoneNumberForUser - - if thirdPartyImplementation != nil { - modifiedTp := MakeThirdPartyRecipeImplementation(result) - (*thirdPartyImplementation.GetUserByID) = *modifiedTp.GetUserByID - (*thirdPartyImplementation.GetUserByThirdPartyInfo) = *modifiedTp.GetUserByThirdPartyInfo - (*thirdPartyImplementation.GetUsersByEmail) = *modifiedTp.GetUsersByEmail - (*thirdPartyImplementation.GetProvider) = *modifiedTp.GetProvider - (*thirdPartyImplementation.SignInUp) = *modifiedTp.SignInUp - (*thirdPartyImplementation.ManuallyCreateOrUpdateUser) = *modifiedTp.ManuallyCreateOrUpdateUser - (*thirdPartyImplementation.GetProvider) = *modifiedTp.GetProvider - } - - return result -} diff --git a/recipe/thirdpartypasswordless/recipeimplementation/passwordlessRecipeImplementation.go b/recipe/thirdpartypasswordless/recipeimplementation/passwordlessRecipeImplementation.go deleted file mode 100644 index 0fa14bd0..00000000 --- a/recipe/thirdpartypasswordless/recipeimplementation/passwordlessRecipeImplementation.go +++ /dev/null @@ -1,191 +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 recipeimplementation - -import ( - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakePasswordlessRecipeImplementation(recipeImplementation tplmodels.RecipeInterface) plessmodels.RecipeInterface { - createCode := func(email *string, phoneNumber *string, userInputCode *string, tenantId string, userContext supertokens.UserContext) (plessmodels.CreateCodeResponse, error) { - return (*recipeImplementation.CreateCode)(email, phoneNumber, userInputCode, tenantId, userContext) - } - - consumeCode := func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (plessmodels.ConsumeCodeResponse, error) { - resp, err := (*recipeImplementation.ConsumeCode)(userInput, linkCode, preAuthSessionID, tenantId, userContext) - - if err != nil { - return plessmodels.ConsumeCodeResponse{}, err - } - - if resp.ExpiredUserInputCodeError != nil { - return plessmodels.ConsumeCodeResponse{ - ExpiredUserInputCodeError: resp.ExpiredUserInputCodeError, - }, nil - } else if resp.IncorrectUserInputCodeError != nil { - return plessmodels.ConsumeCodeResponse{ - IncorrectUserInputCodeError: resp.IncorrectUserInputCodeError, - }, nil - } else if resp.RestartFlowError != nil { - return plessmodels.ConsumeCodeResponse{ - RestartFlowError: &struct{}{}, - }, nil - } else { - return plessmodels.ConsumeCodeResponse{ - OK: &struct { - CreatedNewUser bool - User plessmodels.User - }{ - CreatedNewUser: resp.OK.CreatedNewUser, - User: plessmodels.User{ - ID: resp.OK.User.ID, - Email: resp.OK.User.Email, - PhoneNumber: resp.OK.User.PhoneNumber, - TimeJoined: resp.OK.User.TimeJoined, - }, - }, - }, nil - } - } - - createNewCodeForDevice := func(deviceID string, userInputCode *string, tenantId string, userContext supertokens.UserContext) (plessmodels.ResendCodeResponse, error) { - return (*recipeImplementation.CreateNewCodeForDevice)(deviceID, userInputCode, tenantId, userContext) - } - - getUserByEmail := func(email string, tenantId string, userContext supertokens.UserContext) (*plessmodels.User, error) { - resp, err := (*recipeImplementation.GetUsersByEmail)(email, tenantId, userContext) - if err != nil { - return nil, err - } - - for _, user := range resp { - if user.ThirdParty == nil { - return &plessmodels.User{ - ID: user.ID, - Email: user.Email, - PhoneNumber: user.PhoneNumber, - TimeJoined: user.TimeJoined, - }, nil - } - } - - return nil, nil - } - - getUserByID := func(userID string, userContext supertokens.UserContext) (*plessmodels.User, error) { - resp, err := (*recipeImplementation.GetUserByID)(userID, userContext) - - if err != nil { - return nil, err - } - - if resp == nil { - return nil, nil - } - - if resp.ThirdParty != nil { - // this is a thirdparty user - return nil, nil - } - - return &plessmodels.User{ - ID: resp.ID, - Email: resp.Email, - PhoneNumber: resp.PhoneNumber, - TimeJoined: resp.TimeJoined, - }, nil - } - - getUserByPhoneNumber := func(phoneNumber string, tenantId string, userContext supertokens.UserContext) (*plessmodels.User, error) { - resp, err := (*recipeImplementation.GetUserByPhoneNumber)(phoneNumber, tenantId, userContext) - - if err != nil { - return nil, err - } - - if resp == nil { - return nil, nil - } - - if resp.ThirdParty != nil { - // this is a thirdparty user - return nil, nil - } - - return &plessmodels.User{ - ID: resp.ID, - Email: resp.Email, - PhoneNumber: resp.PhoneNumber, - TimeJoined: resp.TimeJoined, - }, nil - } - - listCodesByDeviceID := func(deviceID string, tenantId string, userContext supertokens.UserContext) (*plessmodels.DeviceType, error) { - return (*recipeImplementation.ListCodesByDeviceID)(deviceID, tenantId, userContext) - } - - listCodesByEmail := func(email string, tenantId string, userContext supertokens.UserContext) ([]plessmodels.DeviceType, error) { - return (*recipeImplementation.ListCodesByEmail)(email, tenantId, userContext) - } - - listCodesByPhoneNumber := func(phoneNumber string, tenantId string, userContext supertokens.UserContext) ([]plessmodels.DeviceType, error) { - return (*recipeImplementation.ListCodesByPhoneNumber)(phoneNumber, tenantId, userContext) - } - - listCodesByPreAuthSessionID := func(preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (*plessmodels.DeviceType, error) { - return (*recipeImplementation.ListCodesByPreAuthSessionID)(preAuthSessionID, tenantId, userContext) - } - - revokeAllCodes := func(email *string, phoneNumber *string, tenantId string, userContext supertokens.UserContext) error { - return (*recipeImplementation.RevokeAllCodes)(email, phoneNumber, tenantId, userContext) - } - - revokeCode := func(codeID string, tenantId string, userContext supertokens.UserContext) error { - return (*recipeImplementation.RevokeCode)(codeID, tenantId, userContext) - } - - updateUser := func(userID string, email *string, phoneNumber *string, userContext supertokens.UserContext) (plessmodels.UpdateUserResponse, error) { - return (*recipeImplementation.UpdatePasswordlessUser)(userID, email, phoneNumber, userContext) - } - - deleteEmailForUser := func(userID string, userContext supertokens.UserContext) (plessmodels.DeleteUserResponse, error) { - return (*recipeImplementation.DeleteEmailForPasswordlessUser)(userID, userContext) - } - - deletePhoneNumberForUser := func(userID string, userContext supertokens.UserContext) (plessmodels.DeleteUserResponse, error) { - return (*recipeImplementation.DeletePhoneNumberForUser)(userID, userContext) - } - - return plessmodels.RecipeInterface{ - CreateCode: &createCode, - ConsumeCode: &consumeCode, - CreateNewCodeForDevice: &createNewCodeForDevice, - GetUserByEmail: &getUserByEmail, - GetUserByID: &getUserByID, - GetUserByPhoneNumber: &getUserByPhoneNumber, - ListCodesByDeviceID: &listCodesByDeviceID, - ListCodesByEmail: &listCodesByEmail, - ListCodesByPhoneNumber: &listCodesByPhoneNumber, - ListCodesByPreAuthSessionID: &listCodesByPreAuthSessionID, - RevokeAllCodes: &revokeAllCodes, - RevokeCode: &revokeCode, - UpdateUser: &updateUser, - DeleteEmailForUser: &deleteEmailForUser, - DeletePhoneNumberForUser: &deletePhoneNumberForUser, - } -} diff --git a/recipe/thirdpartypasswordless/recipeimplementation/thirdPartyRecipeImplementation.go b/recipe/thirdpartypasswordless/recipeimplementation/thirdPartyRecipeImplementation.go deleted file mode 100644 index 18f02919..00000000 --- a/recipe/thirdpartypasswordless/recipeimplementation/thirdPartyRecipeImplementation.go +++ /dev/null @@ -1,140 +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 recipeimplementation - -import ( - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeThirdPartyRecipeImplementation(recipeImplementation tplmodels.RecipeInterface) tpmodels.RecipeInterface { - - getUserByThirdPartyInfo := func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*tpmodels.User, error) { - user, err := (*recipeImplementation.GetUserByThirdPartyInfo)(thirdPartyID, thirdPartyUserID, tenantId, userContext) - if err != nil { - return nil, err - } - if user == nil || user.ThirdParty == nil { - return nil, nil - } - return &tpmodels.User{ - ID: user.ID, - Email: *user.Email, - TimeJoined: user.TimeJoined, - TenantIds: user.TenantIds, - ThirdParty: *user.ThirdParty, - }, nil - } - - signInUp := func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) { - result, err := (*recipeImplementation.ThirdPartySignInUp)(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) - if err != nil { - return tpmodels.SignInUpResponse{}, err - } - - return tpmodels.SignInUpResponse{ - OK: &struct { - CreatedNewUser bool - User tpmodels.User - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpmodels.User{ - ID: result.OK.User.ID, - Email: *result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - ThirdParty: *result.OK.User.ThirdParty, - }, - OAuthTokens: result.OK.OAuthTokens, - RawUserInfoFromProvider: result.OK.RawUserInfoFromProvider, - }, - }, nil - } - - manuallyCreateOrUpdateUser := func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (tpmodels.ManuallyCreateOrUpdateUserResponse, error) { - result, err := (*recipeImplementation.ThirdPartyManuallyCreateOrUpdateUser)(thirdPartyID, thirdPartyUserID, email, tenantId, userContext) - if err != nil { - return tpmodels.ManuallyCreateOrUpdateUserResponse{}, err - } - - return tpmodels.ManuallyCreateOrUpdateUserResponse{ - OK: &struct { - CreatedNewUser bool - User tpmodels.User - }{ - CreatedNewUser: result.OK.CreatedNewUser, - User: tpmodels.User{ - ID: result.OK.User.ID, - Email: *result.OK.User.Email, - TimeJoined: result.OK.User.TimeJoined, - ThirdParty: *result.OK.User.ThirdParty, - }, - }, - }, nil - } - - getUserByID := func(userID string, userContext supertokens.UserContext) (*tpmodels.User, error) { - user, err := (*recipeImplementation.GetUserByID)(userID, userContext) - if err != nil { - return nil, err - } - if user == nil || user.ThirdParty == nil { - return nil, nil - } - return &tpmodels.User{ - ID: user.ID, - Email: *user.Email, - TimeJoined: user.TimeJoined, - ThirdParty: *user.ThirdParty, - }, nil - } - - getUserByEmail := func(email string, tenantId string, userContext supertokens.UserContext) ([]tpmodels.User, error) { - users, err := (*recipeImplementation.GetUsersByEmail)(email, tenantId, userContext) - if err != nil { - return nil, err - } - - finalResult := []tpmodels.User{} - - for _, tpepUser := range users { - if tpepUser.ThirdParty != nil { - finalResult = append(finalResult, tpmodels.User{ - ID: tpepUser.ID, - TimeJoined: tpepUser.TimeJoined, - Email: *tpepUser.Email, - ThirdParty: *tpepUser.ThirdParty, - }) - } - } - return finalResult, nil - } - - getProvider := func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) { - return (*recipeImplementation.ThirdPartyGetProvider)(thirdPartyID, clientType, tenantId, userContext) - } - - return tpmodels.RecipeInterface{ - GetUserByID: &getUserByID, - GetUsersByEmail: &getUserByEmail, - GetUserByThirdPartyInfo: &getUserByThirdPartyInfo, - SignInUp: &signInUp, - ManuallyCreateOrUpdateUser: &manuallyCreateOrUpdateUser, - GetProvider: &getProvider, - } -} diff --git a/recipe/thirdpartypasswordless/signinupFeature_test.go b/recipe/thirdpartypasswordless/signinupFeature_test.go deleted file mode 100644 index 2555213b..00000000 --- a/recipe/thirdpartypasswordless/signinupFeature_test.go +++ /dev/null @@ -1,1084 +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" - "io" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" - "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" - "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 TestThirdPartyPasswordlessThatIfYouDisableTheSignInUpAPIItDoesNotWork(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, - }, - Override: &tplmodels.OverrideStruct{ - APIs: func(originalImplementation tplmodels.APIInterface) tplmodels.APIInterface { - *originalImplementation.ThirdPartySignInUpPOST = nil - return originalImplementation - }, - }, - 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 - } - - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - signinupPostData := map[string]interface{}{ - "thirdPartyId": "google", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(signinupPostData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestWithThirdPartyPasswordlessMinimumConfigWithoutCodeForThirdPartyModyule(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - AntiCsrf: &customAntiCsrfValue, - 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{ - signinupCustomProvider6, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - signinupPostData := thirdparty.PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - cookieData := unittesting.ExtractInfoFromResponse(resp) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - - assert.Equal(t, true, result["createdNewUser"]) - assert.Equal(t, "OK", result["status"]) - - user := result["user"].(map[string]interface{}) - - assert.Equal(t, "email@test.com", user["email"]) - assert.Equal(t, "custom", user["thirdParty"].(map[string]interface{})["id"]) - assert.Equal(t, "user", user["thirdParty"].(map[string]interface{})["userId"]) - - assert.NotNil(t, cookieData["antiCsrf"]) - assert.NotNil(t, cookieData["sAccessToken"]) - assert.NotNil(t, cookieData["sRefreshToken"]) - assert.NotNil(t, cookieData["refreshTokenExpiry"]) - assert.NotNil(t, cookieData["refreshTokenHttpOnly"]) - assert.NotNil(t, cookieData["accessTokenExpiry"]) - assert.NotNil(t, cookieData["accessTokenHttpOnly"]) -} - -func TestWithThirdPartyPasswordlessMissingCodeAndAuthCodeResponse(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - AntiCsrf: &customAntiCsrfValue, - 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{ - signinupCustomProvider6, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - signinupPostData := thirdparty.PostDataForCustomProvider{ - ThirdPartyId: "custom", - RedirectURIInfo: &struct { - RedirectURIOnProviderDashboard string "json:\"redirectURIOnProviderDashboard\"" - RedirectURIQueryParams map[string]interface{} "json:\"redirectURIQueryParams\"" - }{ - RedirectURIOnProviderDashboard: "http://127.0.0.1/callback", - }, - } - - postBody, err := json.Marshal(signinupPostData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestWithThirdPartyPasswordlessMinimumConfigForThirdpartyModule(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - AntiCsrf: &customAntiCsrfValue, - 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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{"access_token": "abcdefghj"}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - cookieData := unittesting.ExtractInfoFromResponse(resp) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - - assert.Equal(t, true, result["createdNewUser"]) - assert.Equal(t, "OK", result["status"]) - - user := result["user"].(map[string]interface{}) - - assert.Equal(t, "email@test.com", user["email"]) - assert.Equal(t, "custom", user["thirdParty"].(map[string]interface{})["id"]) - assert.Equal(t, "user", user["thirdParty"].(map[string]interface{})["userId"]) - - assert.NotNil(t, cookieData["antiCsrf"]) - assert.NotNil(t, cookieData["sAccessToken"]) - assert.NotNil(t, cookieData["sRefreshToken"]) - assert.NotNil(t, cookieData["refreshTokenExpiry"]) - assert.NotNil(t, cookieData["refreshTokenHttpOnly"]) - assert.NotNil(t, cookieData["accessTokenExpiry"]) - assert.NotNil(t, cookieData["accessTokenHttpOnly"]) -} - -func TestWithThirdPartyPasswordlessWithMinimumConfigForThirdPartyModuleEmailUnverified(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - }), - session.Init(&sessmodels.TypeInput{ - AntiCsrf: &customAntiCsrfValue, - 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{ - signinupCustomProvider5, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{"access_token": "abcdefghj"}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - cookieData := unittesting.ExtractInfoFromResponse(resp) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - - assert.Equal(t, true, result["createdNewUser"]) - assert.Equal(t, "OK", result["status"]) - - user := result["user"].(map[string]interface{}) - - isVerified, err := emailverification.IsEmailVerified(user["id"].(string), nil) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, false, isVerified) - - assert.Equal(t, "email@test.com", user["email"]) - assert.Equal(t, "custom", user["thirdParty"].(map[string]interface{})["id"]) - assert.Equal(t, "user", user["thirdParty"].(map[string]interface{})["userId"]) - - assert.NotNil(t, cookieData["antiCsrf"]) - assert.NotNil(t, cookieData["sAccessToken"]) - assert.NotNil(t, cookieData["sRefreshToken"]) - assert.NotNil(t, cookieData["refreshTokenExpiry"]) - assert.NotNil(t, cookieData["refreshTokenHttpOnly"]) - assert.NotNil(t, cookieData["accessTokenExpiry"]) - assert.NotNil(t, cookieData["accessTokenHttpOnly"]) -} - -func TestWithThirdPartyPasswordlessThirdPartyProviderDoesNotExistInConfig(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - AntiCsrf: &customAntiCsrfValue, - 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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - postData := map[string]interface{}{ - "thirdPartyId": "google", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - if err != nil { - t.Error(err.Error()) - } - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - - response := *unittesting.HttpResponseToConsumableInformation(resp.Body) - - assert.Equal(t, "the provider google could not be found in the configuration", response["message"]) -} - -func TestWithThirdPartyPasswordlessEmailNotReturnedInGetProfileInfoFunction(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{ - signinupCustomProvider3, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{"access_token": "abcdefghj"}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "NO_EMAIL_GIVEN_BY_PROVIDER", result["status"]) -} - -func TestWithThirdPartyPasswordlessErrorThrownFromGetProfileInfoFunction(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{ - signinupCustomProvider4, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{"access_token": "abcdefghj"}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "abcdefghj", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, 500, resp.StatusCode) -} - -func TestWithThirdPartyPasswordlessInvalidPostParamsForThirdPartyModule(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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - //request where the postData was empty - postData := map[string]string{} - postBody, err := json.Marshal(postData) - if err != nil { - t.Error(err.Error()) - } - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - response := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "Please provide the thirdPartyId in request body", response["message"]) - - //request where the post data just had the thirdpartyid - postData1 := map[string]string{ - "thirdPartyId": "custom", - } - postBody1, err := json.Marshal(postData1) - if err != nil { - t.Error(err.Error()) - } - resp1, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody1)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusBadRequest, resp1.StatusCode) - response1 := *unittesting.HttpResponseToConsumableInformation(resp1.Body) - assert.Equal(t, "Please provide one of redirectURIInfo or oAuthTokens in the request body", response1["message"]) - - //request where the post data without redirect-uri - postData2 := map[string]interface{}{ - "thirdPartyId": "custom", - "code": "32432432", - } - postBody2, err := json.Marshal(postData2) - if err != nil { - t.Error(err.Error()) - } - resp2, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody2)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusBadRequest, resp2.StatusCode) - response2 := *unittesting.HttpResponseToConsumableInformation(resp2.Body) - assert.Equal(t, "Please provide one of redirectURIInfo or oAuthTokens in the request body", response2["message"]) -} - -func TestWithThirdPartyPasswordlessGetUserByIdWhenUserDoesNotExist(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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://localhost.org", - "redirectURIQueryParams": map[string]interface{}{ - "code": "32432432", - }, - }, - } - - postBody, err := json.Marshal(postData) - if err != nil { - t.Error(err.Error()) - } - - gock.New(testServer.URL).EnableNetworking().Persist() - gock.New("http://localhost:8080/").EnableNetworking().Persist() - - userDataBeforeSignup, err := GetUserByID("randomId") - - if err != nil { - t.Error(err.Error()) - } - - assert.Nil(t, userDataBeforeSignup) - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - - assert.Equal(t, "OK", result["status"]) - - user := result["user"].(map[string]interface{}) - userInfoAfterSignup, err := GetUserByID(user["id"].(string)) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, userInfoAfterSignup.ID, user["id"].(string)) - assert.Equal(t, *userInfoAfterSignup.Email, user["email"].(string)) -} - -func TestGetUserByThirdPartyInfoWhenUserDoesNotExist(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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://localhost.org", - "redirectURIQueryParams": map[string]interface{}{ - "code": "32432432", - }, - }, - } - - postBody, err := json.Marshal(postData) - if err != nil { - t.Error(err.Error()) - } - - gock.New(testServer.URL).EnableNetworking().Persist() - gock.New("http://localhost:8080/").EnableNetworking().Persist() - - userBegoreSignup, err := GetUserByThirdPartyInfo("public", "custom", "user") - if err != nil { - t.Error(err.Error()) - } - assert.Nil(t, userBegoreSignup) - - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - - dataInBytes, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err.Error()) - } - resp.Body.Close() - - var result map[string]interface{} - - err = json.Unmarshal(dataInBytes, &result) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, "OK", result["status"]) - - user := result["user"].(map[string]interface{}) - userInfoAfterSignup, err := GetUserByThirdPartyInfo("public", "custom", "user") - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, userInfoAfterSignup.ID, user["id"].(string)) - assert.Equal(t, *userInfoAfterSignup.Email, user["email"].(string)) -} diff --git a/recipe/thirdpartypasswordless/signoutFeature_test.go b/recipe/thirdpartypasswordless/signoutFeature_test.go deleted file mode 100644 index da141742..00000000 --- a/recipe/thirdpartypasswordless/signoutFeature_test.go +++ /dev/null @@ -1,431 +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" - "time" - - "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 TestTheDefaultRouteAndItShouldRevokeTheSession(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - AntiCsrf: &customAntiCsrfValue, - 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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "32432432", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "OK", result["status"]) - - cookieData := unittesting.ExtractInfoFromResponse(resp) - - req, err := http.NewRequest(http.MethodPost, testServer.URL+"/auth/signout", nil) - if err != nil { - t.Error(err.Error()) - } - - req.Header.Add("Cookie", "sAccessToken="+cookieData["sAccessToken"]) - req.Header.Add("anti-csrf", cookieData["antiCsrf"]) - - res, err := http.DefaultClient.Do(req) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusOK, res.StatusCode) - - cookieData1 := unittesting.ExtractInfoFromResponseWhenAntiCSRFisNone(res) - - assert.Equal(t, "", cookieData1["sAccessToken"]) - assert.Equal(t, "", cookieData1["sRefreshToken"]) - - assert.Equal(t, "Thu, 01 Jan 1970 00:00:00 GMT", cookieData1["refreshTokenExpiry"]) - assert.Equal(t, "Thu, 01 Jan 1970 00:00:00 GMT", cookieData1["accessTokenExpiry"]) - - assert.Equal(t, "", cookieData1["accessTokenDomain"]) - assert.Equal(t, "", cookieData1["refreshTokenDomain"]) -} - -func TestDisablingDefaultRouteAndCallingTheAPIReturns404(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, - }, - Override: &tplmodels.OverrideStruct{ - APIs: func(originalImplementation tplmodels.APIInterface) tplmodels.APIInterface { - *originalImplementation.ThirdPartySignInUpPOST = nil - return originalImplementation - }, - }, - Providers: []tpmodels.ProviderInput{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - req, err := http.NewRequest(http.MethodPost, testServer.URL+"/auth/signout", nil) - if err != nil { - t.Error(err.Error()) - } - - req.Header.Add("rid", "thirdparty") - - res, err := http.DefaultClient.Do(req) - if err != nil { - t.Error(err.Error()) - } - - assert.Equal(t, http.StatusNotFound, res.StatusCode) -} - -func TestCallingAPIWithoutSessionShouldReturnOk(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{ - SigninupCustomProvider1, - }, - }), - }, - } - - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - resp, err := http.Post(testServer.URL+"/auth/signout", "application/json", nil) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, 401, resp.StatusCode) -} - -func TestThatSignoutAPIreturnsTryRefreshTokenRefreshSessionAndSignoutShouldReturnOk(t *testing.T) { - customAntiCsrfValue := "VIA_TOKEN" - 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{ - AntiCsrf: &customAntiCsrfValue, - 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{ - SigninupCustomProvider1, - }, - }), - }, - } - - BeforeEach() - unittesting.SetKeyValueInConfig("access_token_validity", "2") - 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() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - defer testServer.Close() - - defer gock.OffAll() - gock.New("https://test.com/"). - Post("oauth/token"). - Reply(200). - JSON(map[string]string{}) - - postData := map[string]interface{}{ - "thirdPartyId": "custom", - "redirectURIInfo": map[string]interface{}{ - "redirectURIOnProviderDashboard": "http://127.0.0.1/callback", - "redirectURIQueryParams": map[string]interface{}{ - "code": "32432432", - }, - }, - } - - postBody, err := json.Marshal(postData) - 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)) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, resp.StatusCode) - cookieData := unittesting.ExtractInfoFromResponse(resp) - - result := *unittesting.HttpResponseToConsumableInformation(resp.Body) - assert.Equal(t, "OK", result["status"]) - - time.Sleep(5 * time.Second) - - req, err := http.NewRequest(http.MethodPost, testServer.URL+"/auth/signout", nil) - if err != nil { - t.Error(err.Error()) - } - - req.Header.Add("Cookie", "sAccessToken="+cookieData["sAccessToken"]) - req.Header.Add("anti-csrf", cookieData["antiCsrf"]) - - res, err := http.DefaultClient.Do(req) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusUnauthorized, res.StatusCode) - - result1 := *unittesting.HttpResponseToConsumableInformation(res.Body) - assert.Equal(t, "try refresh token", result1["message"]) - - req1, err := http.NewRequest(http.MethodPost, testServer.URL+"/auth/session/refresh", nil) - if err != nil { - t.Error(err.Error()) - } - req1.Header.Add("Cookie", "sRefreshToken="+cookieData["sRefreshToken"]) - req1.Header.Add("anti-csrf", cookieData["antiCsrf"]) - - res1, err := http.DefaultClient.Do(req1) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, res1.StatusCode) - cookieData1 := unittesting.ExtractInfoFromResponse(res1) - - req2, err := http.NewRequest(http.MethodPost, testServer.URL+"/auth/signout", nil) - if err != nil { - t.Error(err.Error()) - } - req2.Header.Add("Cookie", "sAccessToken="+cookieData1["sAccessToken"]) - req2.Header.Add("anti-csrf", cookieData1["antiCsrf"]) - - res2, err := http.DefaultClient.Do(req2) - if err != nil { - t.Error(err.Error()) - } - assert.Equal(t, http.StatusOK, res2.StatusCode) - - cookieData2 := unittesting.ExtractInfoFromResponseWhenAntiCSRFisNone(res2) - - assert.Equal(t, "", cookieData2["sAccessToken"]) - assert.Equal(t, "", cookieData2["sRefreshToken"]) - - assert.Equal(t, "Thu, 01 Jan 1970 00:00:00 GMT", cookieData2["refreshTokenExpiry"]) - assert.Equal(t, "Thu, 01 Jan 1970 00:00:00 GMT", cookieData2["accessTokenExpiry"]) - - assert.Equal(t, "", cookieData2["accessTokenDomain"]) - assert.Equal(t, "", cookieData2["refreshTokenDomain"]) -} diff --git a/recipe/thirdpartypasswordless/smsdelivery/backwardCompatibilityService/main.go b/recipe/thirdpartypasswordless/smsdelivery/backwardCompatibilityService/main.go deleted file mode 100644 index f198ff40..00000000 --- a/recipe/thirdpartypasswordless/smsdelivery/backwardCompatibilityService/main.go +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022, 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 backwardCompatibilityService - -import ( - "errors" - - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless/smsdelivery/backwardCompatibilityService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeBackwardCompatibilityService(createAndSendCustomSms func(phoneNumber string, userInputCode *string, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error) smsdelivery.SmsDeliveryInterface { - plessBackwardCompatibilityService := backwardCompatibilityService.MakeBackwardCompatibilityService(createAndSendCustomSms) - - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin != nil { - return (*plessBackwardCompatibilityService.SendSms)(input, userContext) - - } else { - return errors.New("should never come here") - } - } - - return smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - } -} diff --git a/recipe/thirdpartypasswordless/smsdelivery/supertokensService/main.go b/recipe/thirdpartypasswordless/smsdelivery/supertokensService/main.go deleted file mode 100644 index e9e14c36..00000000 --- a/recipe/thirdpartypasswordless/smsdelivery/supertokensService/main.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022, 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 supertokensService - -import ( - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless/smsdelivery/supertokensService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeSupertokensSMSService(apiKey string) *smsdelivery.SmsDeliveryInterface { - plessService := supertokensService.MakeSupertokensSMSService(apiKey) - - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - return (*plessService.SendSms)(input, userContext) - } - - return &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - } -} diff --git a/recipe/thirdpartypasswordless/smsdelivery/twilioService/main.go b/recipe/thirdpartypasswordless/smsdelivery/twilioService/main.go deleted file mode 100644 index 941760c7..00000000 --- a/recipe/thirdpartypasswordless/smsdelivery/twilioService/main.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, 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 twilioService - -import ( - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless/smsdelivery/twilioService" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func MakeTwilioService(config smsdelivery.TwilioServiceConfig) (*smsdelivery.SmsDeliveryInterface, error) { - plessServiceImpl, err := twilioService.MakeTwilioService(config) - - if err != nil { - return nil, err - } - - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - return (*plessServiceImpl.SendSms)(input, userContext) - } - - return &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, nil -} diff --git a/recipe/thirdpartypasswordless/testingUtils.go b/recipe/thirdpartypasswordless/testingUtils.go deleted file mode 100644 index 80a87d2f..00000000 --- a/recipe/thirdpartypasswordless/testingUtils.go +++ /dev/null @@ -1,295 +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 ( - "errors" - "github.com/supertokens/supertokens-golang/recipe/session" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/multitenancy" - "github.com/supertokens/supertokens-golang/recipe/passwordless" - "github.com/supertokens/supertokens-golang/recipe/thirdparty" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func resetAll() { - supertokens.ResetForTest() - ResetForTest() - emailverification.ResetForTest() - thirdparty.ResetForTest() - passwordless.ResetForTest() - session.ResetForTest() - multitenancy.ResetForTest() -} - -func BeforeEach() { - unittesting.KillAllST() - resetAll() - unittesting.SetUpST() -} - -func AfterEach() { - unittesting.KillAllST() - resetAll() - unittesting.CleanST() -} - -var customProvider1 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - AuthorizationEndpointQueryParams: map[string]interface{}{ - "scope": "test", - "client_id": "supertokens", - }, - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "email@test.com", - IsVerified: true, - }, - }, nil - - } - return originalImplementation - }, -} - -var mockThirdPartyProvider1 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "mock1", - }, -} - -var mockThirdPartyProvider2 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "mock2", - }, -} - -var SigninupCustomProvider1 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "email@test.com", - IsVerified: true, - }, - }, nil - } - return originalImplementation - }, -} - -var signinupCustomProvider3 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - }, nil - } - return originalImplementation - }, -} - -var signinupCustomProvider4 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{}, errors.New("error from getUserInfo") - } - return originalImplementation - }, -} - -var signinupCustomProvider5 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "email@test.com", - IsVerified: false, - }, - }, nil - } - return originalImplementation - }, -} - -var signinupCustomProvider6 = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - if oAuthTokens["access_token"] == nil { - return tpmodels.TypeUserInfo{}, nil - } - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "email@test.com", - IsVerified: true, - }, - }, nil - } - return originalImplementation - }, -} - -var userTestCustomProvider = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: oAuthTokens["id"].(string), - Email: &tpmodels.EmailStruct{ - ID: oAuthTokens["email"].(string), - IsVerified: true, - }, - }, nil - } - return originalImplementation - }, -} - -type PostDataForCustomProvider struct { - ThirdPartyId string `json:"thirdPartyId"` - OAuthTokens map[string]interface{} `json:"oAuthTokens"` - // RedirectUri string `json:"redirectURI"` -} - -var customProviderForEmailVerification = tpmodels.ProviderInput{ - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "custom", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "supertokens", - }, - }, - AuthorizationEndpoint: "https://test.com/oauth/auth", - TokenEndpoint: "https://test.com/oauth/token", - }, - Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider { - originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { - if oAuthTokens["access_token"] == nil { - return tpmodels.TypeUserInfo{}, nil - } - return tpmodels.TypeUserInfo{ - ThirdPartyUserId: "user", - Email: &tpmodels.EmailStruct{ - ID: "test@example.com", - IsVerified: false, - }, - }, nil - } - return originalImplementation - }, -} - -func supertokensInitForTest(t *testing.T, recipes ...supertokens.Recipe) *httptest.Server { - config := supertokens.TypeInput{ - Supertokens: &supertokens.ConnectionInfo{ - ConnectionURI: "http://localhost:8080", - }, - AppInfo: supertokens.AppInfo{ - APIDomain: "api.supertokens.io", - AppName: "SuperTokens", - WebsiteDomain: "supertokens.io", - }, - RecipeList: recipes, - } - - err := supertokens.Init(config) - assert.NoError(t, err) - - mux := http.NewServeMux() - testServer := httptest.NewServer(supertokens.Middleware(mux)) - return testServer -} diff --git a/recipe/thirdpartypasswordless/tpl_email_test.go b/recipe/thirdpartypasswordless/tpl_email_test.go deleted file mode 100644 index 69314107..00000000 --- a/recipe/thirdpartypasswordless/tpl_email_test.go +++ /dev/null @@ -1,1046 +0,0 @@ -/* - * Copyright (c) 2022, 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" - "io/ioutil" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/recipe/emailverification" - "github.com/supertokens/supertokens-golang/recipe/emailverification/emaildelivery/smtpService" - "github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels" - "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" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestDefaultBackwardCompatibilityPasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - assert.True(t, passwordless.PasswordlessLoginEmailSentForTest) - assert.Equal(t, passwordless.PasswordlessLoginEmailDataForTest.Email, "test@example.com") - assert.NotNil(t, passwordless.PasswordlessLoginEmailDataForTest.UrlWithLinkCode) - assert.NotNil(t, passwordless.PasswordlessLoginEmailDataForTest.UserInputCode) - - // Test resend - ResetForTest() - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - assert.True(t, passwordless.PasswordlessLoginEmailSentForTest) - assert.Equal(t, passwordless.PasswordlessLoginEmailDataForTest.Email, "test@example.com") - assert.NotNil(t, passwordless.PasswordlessLoginEmailDataForTest.UrlWithLinkCode) - assert.NotNil(t, passwordless.PasswordlessLoginEmailDataForTest.UserInputCode) -} - -func TestBackwardCompatibilityPasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - plessEmail := "" - var code, urlWithCode *string - var codeLife uint64 - - sendEmail := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - plessEmail = input.PasswordlessLogin.Email - code = input.PasswordlessLogin.UserInputCode - urlWithCode = input.PasswordlessLogin.UrlWithLinkCode - codeLife = input.PasswordlessLogin.CodeLifetime - customCalled = true - return nil - } - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendEmail, - }, - }, - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - // Default handler not called - assert.False(t, passwordless.PasswordlessLoginEmailSentForTest) - assert.Empty(t, passwordless.PasswordlessLoginEmailDataForTest.Email) - assert.Nil(t, passwordless.PasswordlessLoginEmailDataForTest.UserInputCode) - assert.Nil(t, passwordless.PasswordlessLoginEmailDataForTest.UrlWithLinkCode) - - // Custom handler called - assert.Equal(t, plessEmail, "test@example.com") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) - - // Test resend - customCalled = false - plessEmail = "" - code = nil - urlWithCode = nil - codeLife = 0 - - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - assert.Equal(t, plessEmail, "test@example.com") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) -} - -func TestCustomOverridePasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - plessEmail := "" - var code, urlWithCode *string - var codeLife uint64 - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin != nil { - customCalled = true - plessEmail = input.PasswordlessLogin.Email - code = input.PasswordlessLogin.UserInputCode - urlWithCode = input.PasswordlessLogin.UrlWithLinkCode - codeLife = input.PasswordlessLogin.CodeLifetime - } - return nil - } - return originalImplementation - }, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - // Default handler not called - assert.False(t, passwordless.PasswordlessLoginEmailSentForTest) - assert.Empty(t, passwordless.PasswordlessLoginEmailDataForTest.Email) - assert.Nil(t, passwordless.PasswordlessLoginEmailDataForTest.UserInputCode) - assert.Nil(t, passwordless.PasswordlessLoginEmailDataForTest.UrlWithLinkCode) - - // Custom handler called - assert.Equal(t, plessEmail, "test@example.com") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) - - // Test resend - customCalled = false - plessEmail = "" - code = nil - urlWithCode = nil - codeLife = 0 - - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - assert.Equal(t, plessEmail, "test@example.com") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) -} - -func TestSMTPOverridePasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - plessEmail := "" - var code, urlWithCode *string - var codeLife uint64 - - smtpService := MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.PasswordlessLogin != nil { - plessEmail = input.PasswordlessLogin.Email - code = input.PasswordlessLogin.UserInputCode - urlWithCode = input.PasswordlessLogin.UrlWithLinkCode - codeLife = input.PasswordlessLogin.CodeLifetime - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - // Default handler not called - assert.False(t, passwordless.PasswordlessLoginEmailSentForTest) - assert.Empty(t, passwordless.PasswordlessLoginEmailDataForTest.Email) - assert.Nil(t, passwordless.PasswordlessLoginEmailDataForTest.UserInputCode) - assert.Nil(t, passwordless.PasswordlessLoginEmailDataForTest.UrlWithLinkCode) - - assert.Equal(t, plessEmail, "test@example.com") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawEmailCalled, true) - - // Test resend - getContentCalled = false - sendRawEmailCalled = false - plessEmail = "" - code = nil - urlWithCode = nil - codeLife = 0 - - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - assert.Equal(t, plessEmail, "test@example.com") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawEmailCalled, true) -} - -func TestDefaultBackwardCompatibilityEmailVerifyForPasswordlessUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{Mode: evmodels.ModeOptional}), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - - var response map[string]interface{} - json.Unmarshal(bodyBytes, &response) - - resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *passwordless.PasswordlessLoginEmailDataForTest.UserInputCode, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - - bodyBytes, err = ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - - json.Unmarshal(bodyBytes, &response) - assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") - - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.False(t, emailverification.EmailVerificationEmailSentForTest) - assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) - assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) -} - -func TestDefaultBackwardCompatibilityEmailVerifyForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{ - customProviderForEmailVerification, - }, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{Mode: evmodels.ModeOptional}), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - signinupPostData := PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - - cookies := resp.Cookies() - - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.True(t, emailverification.EmailVerificationEmailSentForTest) - assert.Equal(t, emailverification.EmailVerificationDataForTest.User.Email, "test@example.com") - assert.NotEmpty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) -} - -// func TestBackwardCompatibilityEmailVerifyForPasswordlessUser(t *testing.T) { -// BeforeEach() -// unittesting.StartUpST("localhost", "8080") -// defer AfterEach() - -// customCalled := false -// email := "" -// emailVerifyLink := "" - -// tplConfig := tplmodels.TypeInput{ -// FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", -// ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ -// Enabled: true, -// }, -// EmailVerificationFeature: &tplmodels.TypeInputEmailVerificationFeature{ -// CreateAndSendCustomEmail: func(user tplmodels.User, emailVerificationURLWithToken string, userContext supertokens.UserContext) { -// email = *user.Email -// emailVerifyLink = emailVerificationURLWithToken -// customCalled = true -// }, -// }, -// } -// testServer := supertokensInitForTest(t, session.Init(nil), Init(tplConfig)) -// defer testServer.Close() - -// 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.10", cdiVersion) == "2.10" { -// return -// } - -// resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// bodyBytes, err := ioutil.ReadAll(resp.Body) -// assert.NoError(t, err) - -// var response map[string]interface{} -// json.Unmarshal(bodyBytes, &response) - -// resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *passwordless.PasswordlessLoginEmailDataForTest.UserInputCode, testServer.URL) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, resp.StatusCode) - -// cookies := resp.Cookies() -// resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, resp.StatusCode) - -// bodyBytes, err = ioutil.ReadAll(resp.Body) -// assert.NoError(t, err) - -// json.Unmarshal(bodyBytes, &response) -// assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") - -// // Default handler not called -// assert.False(t, emailverification.EmailVerificationEmailSentForTest) -// assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) -// assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) - -// // Custom handler called -// assert.Empty(t, email) -// assert.Empty(t, emailVerifyLink) -// assert.False(t, customCalled) -// } - -// func TestBackwardCompatibilityEmailVerifyForThirdpartyUser(t *testing.T) { -// BeforeEach() -// unittesting.StartUpST("localhost", "8080") -// defer AfterEach() - -// customCalled := false -// email := "" -// emailVerifyLink := "" -// var thirdparty *struct { -// ID string `json:"id"` -// UserID string `json:"userId"` -// } - -// tplConfig := tplmodels.TypeInput{ -// FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", -// ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ -// Enabled: true, -// }, -// EmailVerificationFeature: &tplmodels.TypeInputEmailVerificationFeature{ -// CreateAndSendCustomEmail: func(user tplmodels.User, emailVerificationURLWithToken string, userContext supertokens.UserContext) { -// email = *user.Email -// emailVerifyLink = emailVerificationURLWithToken -// thirdparty = user.ThirdParty -// customCalled = true -// }, -// }, -// Providers: []tpmodels.TypeProvider{customProviderForEmailVerification}, -// } -// testServer := supertokensInitForTest(t, session.Init(nil), Init(tplConfig)) -// defer testServer.Close() - -// signinupPostData := PostDataForCustomProvider{ -// ThirdPartyId: "custom", -// AuthCodeResponse: map[string]string{ -// "access_token": "saodiasjodai", -// }, -// RedirectUri: "http://127.0.0.1/callback", -// } - -// postBody, err := json.Marshal(signinupPostData) -// resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) -// assert.NoError(t, err) - -// cookies := resp.Cookies() -// resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, resp.StatusCode) - -// // Default handler not called -// assert.False(t, emailverification.EmailVerificationEmailSentForTest) -// assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) -// assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) - -// // Custom handler called -// assert.Equal(t, email, "test@example.com") -// assert.NotEmpty(t, emailVerifyLink) -// assert.NotNil(t, thirdparty) -// assert.True(t, customCalled) -// } - -func TestCustomOverrideEmailVerifyForPasswordlessUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - emailVerifyLink := "" - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - sendEmail := *originalImplementation.SendEmail - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.EmailVerification != nil { - customCalled = true - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - return nil - } - return sendEmail(input, userContext) - } - return originalImplementation - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - - var response map[string]interface{} - json.Unmarshal(bodyBytes, &response) - - resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *passwordless.PasswordlessLoginEmailDataForTest.UserInputCode, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err = ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - - json.Unmarshal(bodyBytes, &response) - assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") - - // Default handler not called - assert.False(t, emailverification.EmailVerificationEmailSentForTest) - assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) - assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) - - // Custom handler not called - assert.Empty(t, email) - assert.Empty(t, emailVerifyLink) - assert.False(t, customCalled) -} - -func TestCustomOverrideEmailVerifyForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - email := "" - emailVerifyLink := "" - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - - Providers: []tpmodels.ProviderInput{customProviderForEmailVerification}, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface { - sendEmail := *originalImplementation.SendEmail - *originalImplementation.SendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error { - if input.EmailVerification != nil { - customCalled = true - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - return nil - } - return sendEmail(input, userContext) - } - return originalImplementation - }, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - signinupPostData := PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailverification.EmailVerificationEmailSentForTest) - assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) - assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) - - // Custom handler called - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, emailVerifyLink) - assert.True(t, customCalled) -} - -func TestSMTPOverrideEmailVerifyForPasswordlessUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - emailVerifyLink := "" - var userInputCode *string - - evSmtpService := smtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.EmailVerification != nil { - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tplSmtpService := MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.PasswordlessLogin != nil { - userInputCode = input.PasswordlessLogin.UserInputCode - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - EmailDelivery: &emaildelivery.TypeInput{ - Service: tplSmtpService, - }, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Service: evSmtpService, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessEmailLoginRequest("test@example.com", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - - var response map[string]interface{} - json.Unmarshal(bodyBytes, &response) - - resp, err = unittesting.PasswordlessLoginWithCodeRequest(response["deviceId"].(string), response["preAuthSessionId"].(string), *userInputCode, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - sendRawEmailCalled = false // it would be true for the passwordless login, so reset it - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err = ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - - json.Unmarshal(bodyBytes, &response) - assert.Equal(t, response["status"], "EMAIL_ALREADY_VERIFIED_ERROR") - - // Default handler not called - assert.False(t, emailverification.EmailVerificationEmailSentForTest) - assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) - assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) - - // Custom handler not called - assert.Empty(t, email) - assert.Empty(t, emailVerifyLink) - assert.False(t, getContentCalled) - assert.False(t, sendRawEmailCalled) -} - -func TestSMTPOverrideEmailVerifyForThirdpartyUser(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawEmailCalled := false - email := "" - emailVerifyLink := "" - - smtpService := smtpService.MakeSMTPService(emaildelivery.SMTPServiceConfig{ - Settings: emaildelivery.SMTPSettings{ - Host: "", - From: emaildelivery.SMTPFrom{ - Name: "Test User", - Email: "", - }, - Port: 123, - Password: "", - }, - Override: func(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface { - (*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) { - if input.EmailVerification != nil { - email = input.EmailVerification.User.Email - emailVerifyLink = input.EmailVerification.EmailVerifyLink - getContentCalled = true - } - return emaildelivery.EmailContent{}, nil - } - - (*originalImplementation.SendRawEmail) = func(input emaildelivery.EmailContent, userContext supertokens.UserContext) error { - sendRawEmailCalled = true - return nil - } - - return originalImplementation - }, - }) - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ - Enabled: true, - }, - Providers: []tpmodels.ProviderInput{customProviderForEmailVerification}, - } - testServer := supertokensInitForTest( - t, - emailverification.Init(evmodels.TypeInput{ - Mode: evmodels.ModeOptional, - EmailDelivery: &emaildelivery.TypeInput{ - Service: smtpService, - }, - }), - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - signinupPostData := PostDataForCustomProvider{ - ThirdPartyId: "custom", - OAuthTokens: map[string]interface{}{ - "access_token": "saodiasjodai", - }, - } - - postBody, err := json.Marshal(signinupPostData) - resp, err := http.Post(testServer.URL+"/auth/signinup", "application/json", bytes.NewBuffer(postBody)) - assert.NoError(t, err) - - cookies := resp.Cookies() - resp, err = unittesting.EmailVerificationTokenRequest(cookies, testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Default handler not called - assert.False(t, emailverification.EmailVerificationEmailSentForTest) - assert.Empty(t, emailverification.EmailVerificationDataForTest.User.Email) - assert.Empty(t, emailverification.EmailVerificationDataForTest.EmailVerifyURLWithToken) - - assert.Equal(t, email, "test@example.com") - assert.NotEmpty(t, emailVerifyLink) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawEmailCalled, true) -} diff --git a/recipe/thirdpartypasswordless/tpl_sms_test.go b/recipe/thirdpartypasswordless/tpl_sms_test.go deleted file mode 100644 index 4073c82d..00000000 --- a/recipe/thirdpartypasswordless/tpl_sms_test.go +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (c) 2022, 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 ( - "encoding/json" - "io/ioutil" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "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/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" - "github.com/supertokens/supertokens-golang/test/unittesting" -) - -func TestSmsDefaultBackwardCompatibilityPasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessPhoneLoginRequest("+919876543210", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - assert.True(t, passwordless.PasswordlessLoginSmsSentForTest) - assert.Equal(t, passwordless.PasswordlessLoginSmsDataForTest.Phone, "+919876543210") - assert.NotNil(t, passwordless.PasswordlessLoginSmsDataForTest.UrlWithLinkCode) - assert.NotNil(t, passwordless.PasswordlessLoginSmsDataForTest.UserInputCode) - - // Test resend - ResetForTest() - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - assert.True(t, passwordless.PasswordlessLoginSmsSentForTest) - assert.Equal(t, passwordless.PasswordlessLoginSmsDataForTest.Phone, "+919876543210") - assert.NotNil(t, passwordless.PasswordlessLoginSmsDataForTest.UrlWithLinkCode) - assert.NotNil(t, passwordless.PasswordlessLoginSmsDataForTest.UserInputCode) -} - -func TestSmsBackwardCompatibilityPasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - plessPhone := "" - var code, urlWithCode *string - var codeLife uint64 - - sendSms := func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - plessPhone = input.PasswordlessLogin.PhoneNumber - code = input.PasswordlessLogin.UserInputCode - urlWithCode = input.PasswordlessLogin.UrlWithLinkCode - codeLife = input.PasswordlessLogin.CodeLifetime - customCalled = true - return nil - } - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - SmsDelivery: &smsdelivery.TypeInput{ - Service: &smsdelivery.SmsDeliveryInterface{ - SendSms: &sendSms, - }, - }, - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessPhoneLoginRequest("+919876543210", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - // Default handler not called - assert.False(t, passwordless.PasswordlessLoginSmsSentForTest) - assert.Empty(t, passwordless.PasswordlessLoginSmsDataForTest.Phone) - assert.Nil(t, passwordless.PasswordlessLoginSmsDataForTest.UserInputCode) - assert.Nil(t, passwordless.PasswordlessLoginSmsDataForTest.UrlWithLinkCode) - - // Custom handler called - assert.Equal(t, plessPhone, "+919876543210") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) - - // Test resend - customCalled = false - plessPhone = "" - code = nil - urlWithCode = nil - codeLife = 0 - - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - assert.Equal(t, plessPhone, "+919876543210") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) -} - -func TestSmsCustomOverridePasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - customCalled := false - plessPhone := "" - var code, urlWithCode *string - var codeLife uint64 - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - SmsDelivery: &smsdelivery.TypeInput{ - Override: func(originalImplementation smsdelivery.SmsDeliveryInterface) smsdelivery.SmsDeliveryInterface { - *originalImplementation.SendSms = func(input smsdelivery.SmsType, userContext supertokens.UserContext) error { - if input.PasswordlessLogin != nil { - customCalled = true - plessPhone = input.PasswordlessLogin.PhoneNumber - code = input.PasswordlessLogin.UserInputCode - urlWithCode = input.PasswordlessLogin.UrlWithLinkCode - codeLife = input.PasswordlessLogin.CodeLifetime - } - return nil - } - return originalImplementation - }, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessPhoneLoginRequest("+919876543210", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - // Default handler not called - assert.False(t, passwordless.PasswordlessLoginSmsSentForTest) - assert.Empty(t, passwordless.PasswordlessLoginSmsDataForTest.Phone) - assert.Nil(t, passwordless.PasswordlessLoginSmsDataForTest.UserInputCode) - assert.Nil(t, passwordless.PasswordlessLoginSmsDataForTest.UrlWithLinkCode) - - // Custom handler called - assert.Equal(t, plessPhone, "+919876543210") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) - - // Test resend - customCalled = false - plessPhone = "" - code = nil - urlWithCode = nil - codeLife = 0 - - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, resp.StatusCode, http.StatusOK) - - assert.Equal(t, plessPhone, "+919876543210") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.True(t, customCalled) -} - -func TestSmsTwilioOverridePasswordlessLogin(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - getContentCalled := false - sendRawSmsCalled := false - plessPhone := "" - var code, urlWithCode *string - var codeLife uint64 - - twilioService, err := MakeTwilioService(smsdelivery.TwilioServiceConfig{ - Settings: smsdelivery.TwilioSettings{ - AccountSid: "AC123", - AuthToken: "123", - MessagingServiceSid: "MS123", - }, - Override: func(originalImplementation smsdelivery.TwilioInterface) smsdelivery.TwilioInterface { - *originalImplementation.GetContent = func(input smsdelivery.SmsType, userContext supertokens.UserContext) (smsdelivery.SMSContent, error) { - if input.PasswordlessLogin != nil { - plessPhone = input.PasswordlessLogin.PhoneNumber - code = input.PasswordlessLogin.UserInputCode - urlWithCode = input.PasswordlessLogin.UrlWithLinkCode - codeLife = input.PasswordlessLogin.CodeLifetime - getContentCalled = true - } - return smsdelivery.SMSContent{}, nil - } - - *originalImplementation.SendRawSms = func(input smsdelivery.SMSContent, userContext supertokens.UserContext) error { - sendRawSmsCalled = true - return nil - } - - return originalImplementation - }, - }) - assert.NoError(t, err) - - tplConfig := tplmodels.TypeInput{ - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ - Enabled: true, - }, - SmsDelivery: &smsdelivery.TypeInput{ - Service: twilioService, - }, - } - testServer := supertokensInitForTest( - t, - session.Init(&sessmodels.TypeInput{ - GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod { - return sessmodels.CookieTransferMethod - }, - }), - Init(tplConfig), - ) - defer testServer.Close() - - 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.10", cdiVersion) == "2.10" { - return - } - - resp, err := unittesting.PasswordlessPhoneLoginRequest("+919876543210", testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - bodyBytes, err := ioutil.ReadAll(resp.Body) - assert.NoError(t, err) - body := map[string]string{} - - err = json.Unmarshal(bodyBytes, &body) - assert.NoError(t, err) - - // Default handler not called - assert.False(t, passwordless.PasswordlessLoginSmsSentForTest) - assert.Empty(t, passwordless.PasswordlessLoginSmsDataForTest.Phone) - assert.Nil(t, passwordless.PasswordlessLoginSmsDataForTest.UserInputCode) - assert.Nil(t, passwordless.PasswordlessLoginSmsDataForTest.UrlWithLinkCode) - - assert.Equal(t, plessPhone, "+919876543210") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawSmsCalled, true) - - // Test resend - getContentCalled = false - sendRawSmsCalled = false - plessPhone = "" - code = nil - urlWithCode = nil - codeLife = 0 - - resp, err = unittesting.PasswordlessLoginResendRequest(body["deviceId"], body["preAuthSessionId"], testServer.URL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - - assert.Equal(t, plessPhone, "+919876543210") - assert.NotNil(t, code) - assert.NotNil(t, urlWithCode) - assert.NotZero(t, codeLife) - assert.Equal(t, getContentCalled, true) - assert.Equal(t, sendRawSmsCalled, true) -} diff --git a/recipe/thirdpartypasswordless/tpl_userIdMapping_test.go b/recipe/thirdpartypasswordless/tpl_userIdMapping_test.go deleted file mode 100644 index 3843483e..00000000 --- a/recipe/thirdpartypasswordless/tpl_userIdMapping_test.go +++ /dev/null @@ -1,211 +0,0 @@ -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 initForUserIdMappingTest(t *testing.T) { - - config := 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{ - ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{ - Enabled: true, - }, - FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: "clientID", - ClientSecret: "clientSecret", - }, - }, - }, - }, - }, - })}, - } - - err := supertokens.Init(config) - assert.NoError(t, err) -} - -func TestCreateUserIdMappingUsingEmail(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - initForUserIdMappingTest(t) - - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - assert.NoError(t, err) - - cdiVersion, err := querier.GetQuerierAPIVersion() - assert.NoError(t, err) - - if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { - return - } - - signUpResponse, err := ThirdPartyManuallyCreateOrUpdateUser("public", "google", "googleID", "test@example.com") - assert.NoError(t, err) - - externalUserId := "externalId" - externalUserIdInfo := "externalIdInfo" - createResp, err := supertokens.CreateUserIdMapping(signUpResponse.OK.User.ID, externalUserId, &externalUserIdInfo, nil) - assert.NoError(t, err) - assert.NotNil(t, createResp.OK) - - { // Using supertokens ID - userResp, err := GetUserByID(signUpResponse.OK.User.ID) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using external ID - userResp, err := GetUserByID(externalUserId) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using thirdparty info - userResp, err := GetUserByThirdPartyInfo("public", "google", "googleID") - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } -} - -func TestPlessCreateUserIdMappingUsingEmail(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - initForUserIdMappingTest(t) - - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - assert.NoError(t, err) - - cdiVersion, err := querier.GetQuerierAPIVersion() - assert.NoError(t, err) - - if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { - return - } - - signUpResponse, err := PasswordlessSignInUpByEmail("public", "test@example.com") - assert.NoError(t, err) - - externalUserId := "externalId" - externalUserIdInfo := "externalIdInfo" - createResp, err := supertokens.CreateUserIdMapping(signUpResponse.User.ID, externalUserId, &externalUserIdInfo, nil) - assert.NoError(t, err) - assert.NotNil(t, createResp.OK) - - { // Using supertokens ID - userResp, err := GetUserByID(signUpResponse.User.ID) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using external ID - userResp, err := GetUserByID(externalUserId) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using email - userResp, err := GetUsersByEmail("public", "test@example.com") - assert.NoError(t, err) - assert.NotNil(t, userResp) - assert.Equal(t, 1, len(userResp)) - for _, user := range userResp { - assert.Equal(t, externalUserId, user.ID) - } - } - - { // Using sign in - codeResp, err := CreateCodeWithEmail("public", "test@example.com", nil) - assert.NoError(t, err) - assert.NotNil(t, codeResp.OK) - - resp, err := ConsumeCodeWithUserInputCode("public", codeResp.OK.DeviceID, codeResp.OK.UserInputCode, codeResp.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, resp.OK) - - assert.Equal(t, externalUserId, resp.OK.User.ID) - } -} - -func TestPlessCreateUserIdMappingUsingPhone(t *testing.T) { - BeforeEach() - unittesting.StartUpST("localhost", "8080") - defer AfterEach() - - initForUserIdMappingTest(t) - - querier, err := supertokens.GetNewQuerierInstanceOrThrowError("") - assert.NoError(t, err) - - cdiVersion, err := querier.GetQuerierAPIVersion() - assert.NoError(t, err) - - if unittesting.MaxVersion(cdiVersion, "2.14") == "2.14" { - return - } - - signUpResponse, err := PasswordlessSignInUpByPhoneNumber("public", "+919876543210") - assert.NoError(t, err) - - externalUserId := "externalId" - externalUserIdInfo := "externalIdInfo" - createResp, err := supertokens.CreateUserIdMapping(signUpResponse.User.ID, externalUserId, &externalUserIdInfo, nil) - assert.NoError(t, err) - assert.NotNil(t, createResp.OK) - - { // Using supertokens ID - userResp, err := GetUserByID(signUpResponse.User.ID) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using external ID - userResp, err := GetUserByID(externalUserId) - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using email - userResp, err := GetUserByPhoneNumber("public", "+919876543210") - assert.NoError(t, err) - assert.Equal(t, externalUserId, userResp.ID) - } - - { // Using sign in - codeResp, err := CreateCodeWithPhoneNumber("public", "+919876543210", nil) - assert.NoError(t, err) - assert.NotNil(t, codeResp.OK) - - resp, err := ConsumeCodeWithUserInputCode("public", codeResp.OK.DeviceID, codeResp.OK.UserInputCode, codeResp.OK.PreAuthSessionID) - assert.NoError(t, err) - assert.NotNil(t, resp.OK) - - assert.Equal(t, externalUserId, resp.OK.User.ID) - } -} diff --git a/recipe/thirdpartypasswordless/tplmodels/apiInterface.go b/recipe/thirdpartypasswordless/tplmodels/apiInterface.go deleted file mode 100644 index 21545554..00000000 --- a/recipe/thirdpartypasswordless/tplmodels/apiInterface.go +++ /dev/null @@ -1,65 +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 tplmodels - -import ( - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -type APIInterface struct { - AuthorisationUrlGET *func(provider *tpmodels.TypeProvider, redirectURIOnProviderDashboard string, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.AuthorisationUrlGETResponse, error) - AppleRedirectHandlerPOST *func(formPostInfoFromProvider map[string]interface{}, options tpmodels.APIOptions, userContext supertokens.UserContext) error - ThirdPartySignInUpPOST *func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (ThirdPartySignInUpPOSTResponse, error) - - CreateCodePOST *func(email *string, phoneNumber *string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.CreateCodePOSTResponse, error) - ResendCodePOST *func(deviceID string, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.ResendCodePOSTResponse, error) - ConsumeCodePOST *func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (ConsumeCodePOSTResponse, error) - PasswordlessEmailExistsGET *func(email string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.EmailExistsGETResponse, error) - PasswordlessPhoneNumberExistsGET *func(email string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.PhoneNumberExistsGETResponse, error) -} - -type ConsumeCodePOSTResponse struct { - OK *struct { - CreatedNewUser bool - User User - Session sessmodels.SessionContainer - } - IncorrectUserInputCodeError *struct { - FailedCodeInputAttemptCount int - MaximumCodeInputAttempts int - } - ExpiredUserInputCodeError *struct { - FailedCodeInputAttemptCount int - MaximumCodeInputAttempts int - } - RestartFlowError *struct{} - GeneralError *supertokens.GeneralErrorResponse -} - -type ThirdPartySignInUpPOSTResponse struct { - OK *struct { - CreatedNewUser bool - User User - Session sessmodels.SessionContainer - OAuthTokens tpmodels.TypeOAuthTokens - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - } - NoEmailGivenByProviderError *struct{} - GeneralError *supertokens.GeneralErrorResponse -} diff --git a/recipe/thirdpartypasswordless/tplmodels/models.go b/recipe/thirdpartypasswordless/tplmodels/models.go deleted file mode 100644 index 30e157fe..00000000 --- a/recipe/thirdpartypasswordless/tplmodels/models.go +++ /dev/null @@ -1,70 +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 tplmodels - -import ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -type User struct { - ID string `json:"id"` - TimeJoined uint64 `json:"timeJoined"` - Email *string `json:"email"` - PhoneNumber *string `json:"phoneNumber"` - ThirdParty *struct { - ID string `json:"id"` - UserID string `json:"userId"` - } `json:"thirdParty"` - TenantIds []string `json:"tenantIds"` -} - -type TypeInput struct { - ContactMethodPhone plessmodels.ContactMethodPhoneConfig - ContactMethodEmail plessmodels.ContactMethodEmailConfig - ContactMethodEmailOrPhone plessmodels.ContactMethodEmailOrPhoneConfig - FlowType string - GetCustomUserInputCode func(tenantId string, userContext supertokens.UserContext) (string, error) - Providers []tpmodels.ProviderInput - Override *OverrideStruct - EmailDelivery *emaildelivery.TypeInput - SmsDelivery *smsdelivery.TypeInput -} - -type TypeNormalisedInput struct { - ContactMethodPhone plessmodels.ContactMethodPhoneConfig - ContactMethodEmail plessmodels.ContactMethodEmailConfig - ContactMethodEmailOrPhone plessmodels.ContactMethodEmailOrPhoneConfig - FlowType string - GetCustomUserInputCode func(tenantId string, userContext supertokens.UserContext) (string, error) - Providers []tpmodels.ProviderInput - Override OverrideStruct - GetEmailDeliveryConfig func() emaildelivery.TypeInputWithService - GetSmsDeliveryConfig func() smsdelivery.TypeInputWithService -} - -type OverrideStruct struct { - Functions func(originalImplementation RecipeInterface) RecipeInterface - APIs func(originalImplementation APIInterface) APIInterface -} - -type EmailStruct struct { - ID string `json:"id"` - IsVerified bool `json:"isVerified"` -} diff --git a/recipe/thirdpartypasswordless/tplmodels/recipeInterface.go b/recipe/thirdpartypasswordless/tplmodels/recipeInterface.go deleted file mode 100644 index 8f306b56..00000000 --- a/recipe/thirdpartypasswordless/tplmodels/recipeInterface.go +++ /dev/null @@ -1,92 +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 tplmodels - -import ( - "github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -type RecipeInterface struct { - GetUserByID *func(userID string, userContext supertokens.UserContext) (*User, error) - GetUsersByEmail *func(email string, tenantId string, userContext supertokens.UserContext) ([]User, error) - GetUserByPhoneNumber *func(phoneNumber string, tenantId string, userContext supertokens.UserContext) (*User, error) - GetUserByThirdPartyInfo *func(thirdPartyID string, thirdPartyUserID string, tenantId string, userContext supertokens.UserContext) (*User, error) - - ThirdPartySignInUp *func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (ThirdPartySignInUp, error) - ThirdPartyManuallyCreateOrUpdateUser *func(thirdPartyID string, thirdPartyUserID string, email string, tenantId string, userContext supertokens.UserContext) (ManuallyCreateOrUpdateUserResponse, error) - ThirdPartyGetProvider *func(thirdPartyID string, clientType *string, tenantId string, userContext supertokens.UserContext) (*tpmodels.TypeProvider, error) - - CreateCode *func(email *string, phoneNumber *string, userInputCode *string, tenantId string, userContext supertokens.UserContext) (plessmodels.CreateCodeResponse, error) - CreateNewCodeForDevice *func(deviceID string, userInputCode *string, tenantId string, userContext supertokens.UserContext) (plessmodels.ResendCodeResponse, error) - ConsumeCode *func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (ConsumeCodeResponse, error) - UpdatePasswordlessUser *func(userID string, email *string, phoneNumber *string, userContext supertokens.UserContext) (plessmodels.UpdateUserResponse, error) - DeleteEmailForPasswordlessUser *func(userID string, userContext supertokens.UserContext) (plessmodels.DeleteUserResponse, error) - DeletePhoneNumberForUser *func(userID string, userContext supertokens.UserContext) (plessmodels.DeleteUserResponse, error) - RevokeAllCodes *func(email *string, phoneNumber *string, tenantId string, userContext supertokens.UserContext) error - RevokeCode *func(codeID string, tenantId string, userContext supertokens.UserContext) error - ListCodesByEmail *func(email string, tenantId string, userContext supertokens.UserContext) ([]plessmodels.DeviceType, error) - ListCodesByPhoneNumber *func(phoneNumber string, tenantId string, userContext supertokens.UserContext) ([]plessmodels.DeviceType, error) - ListCodesByDeviceID *func(deviceID string, tenantId string, userContext supertokens.UserContext) (*plessmodels.DeviceType, error) - ListCodesByPreAuthSessionID *func(preAuthSessionID string, tenantId string, userContext supertokens.UserContext) (*plessmodels.DeviceType, error) -} - -type ConsumeCodeResponse struct { - OK *struct { - CreatedNewUser bool - User User - } - IncorrectUserInputCodeError *struct { - FailedCodeInputAttemptCount int - MaximumCodeInputAttempts int - } - ExpiredUserInputCodeError *struct { - FailedCodeInputAttemptCount int - MaximumCodeInputAttempts int - } - RestartFlowError *struct{} -} - -type ThirdPartySignInUp struct { - OK *struct { - CreatedNewUser bool - User User - OAuthTokens map[string]interface{} - RawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider - } -} - -type ManuallyCreateOrUpdateUserResponse struct { - OK *struct { - CreatedNewUser bool - User User - } -} - -type SignUpResponse struct { - OK *struct { - User User - } - EmailAlreadyExistsError *struct{} -} - -type SignInResponse struct { - OK *struct { - User User - } - WrongCredentialsError *struct{} -} diff --git a/recipe/thirdpartypasswordless/users_test.go b/recipe/thirdpartypasswordless/users_test.go deleted file mode 100644 index 64f72331..00000000 --- a/recipe/thirdpartypasswordless/users_test.go +++ /dev/null @@ -1,422 +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" -// "net/http/httptest" -// "reflect" -// "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" -// ) - -// func TestGetUsersOldesFirst(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, -// CreateAndSendCustomEmail: func(email string, userInputCode, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error { -// return nil -// }, -// }, -// Providers: []tpmodels.TypeProvider{ -// userTestCustomProvider, -// }, -// }), -// }, -// } -// "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/thirdparty/tpmodels" -// "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" -// "github.com/supertokens/supertokens-golang/supertokens" -// "github.com/supertokens/supertokens-golang/test/unittesting" -// ) - -// func TestGetUsersOldesFirst(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(nil), -// Init(tplmodels.TypeInput{ -// FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", -// ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ -// Enabled: true, -// CreateAndSendCustomEmail: func(email string, userInputCode, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error { -// return nil -// }, -// }, -// Providers: []tpmodels.ProviderInput{ -// userTestCustomProvider, -// }, -// }), -// }, -// } - -// 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() -// testServer := httptest.NewServer(supertokens.Middleware(mux)) -// defer testServer.Close() - -// unittesting.SigninupCustomRequest(testServer.URL, "test@gmail.com", "testPass0") -// unittesting.SigninupCustomRequest(testServer.URL, "test1@gmail.com", "testPass1") -// unittesting.SigninupCustomRequest(testServer.URL, "test2@gmail.com", "testPass2") -// unittesting.SigninupCustomRequest(testServer.URL, "test3@gmail.com", "testPass3") -// unittesting.SigninupCustomRequest(testServer.URL, "test4@gmail.com", "testPass4") - -// userPaginationResult, err := supertokens.GetUsersOldestFirst(nil, nil, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 5, len(userPaginationResult.Users)) -// assert.Nil(t, userPaginationResult.NextPaginationToken) - -// customLimit := 1 -// userPaginationResult, err = supertokens.GetUsersOldestFirst(nil, &customLimit, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 1, len(userPaginationResult.Users)) -// assert.Equal(t, "test@gmail.com", userPaginationResult.Users[0].User["email"]) -// assert.Equal(t, "*string", reflect.TypeOf(userPaginationResult.NextPaginationToken).String()) - -// userPaginationResult, err = supertokens.GetUsersOldestFirst(userPaginationResult.NextPaginationToken, &customLimit, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 1, len(userPaginationResult.Users)) -// assert.Equal(t, "test1@gmail.com", userPaginationResult.Users[0].User["email"]) -// assert.Equal(t, "*string", reflect.TypeOf(userPaginationResult.NextPaginationToken).String()) - -// customLimit = 5 -// userPaginationResult, err = supertokens.GetUsersOldestFirst(userPaginationResult.NextPaginationToken, &customLimit, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 3, len(userPaginationResult.Users)) - -// customInvalidPaginationToken := "invalid-pagination-token" -// userPaginationResult, err = supertokens.GetUsersOldestFirst(&customInvalidPaginationToken, &customLimit, nil, nil) -// if err != nil { -// assert.Contains(t, err.Error(), "invalid pagination token") -// } else { -// t.Fail() -// } - -// customLimit = -1 -// userPaginationResult, err = supertokens.GetUsersOldestFirst(nil, &customLimit, nil, nil) -// if err != nil { -// assert.Contains(t, err.Error(), "limit must a positive integer with min value 1") -// } else { -// t.Fail() -// } -// } - -// func TestGetUsersNewestFirst(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, -// CreateAndSendCustomEmail: func(email string, userInputCode, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error { -// return nil -// }, -// }, -// Providers: []tpmodels.TypeProvider{ -// userTestCustomProvider, -// }, -// }), -// }, -// } -// func TestGetUsersNewestFirst(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(nil), -// Init(tplmodels.TypeInput{ -// FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", -// ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ -// Enabled: true, -// CreateAndSendCustomEmail: func(email string, userInputCode, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error { -// return nil -// }, -// }, -// Providers: []tpmodels.ProviderInput{ -// userTestCustomProvider, -// }, -// }), -// }, -// } - -// 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() -// testServer := httptest.NewServer(supertokens.Middleware(mux)) -// defer testServer.Close() - -// unittesting.SigninupCustomRequest(testServer.URL, "test@gmail.com", "testPass0") -// unittesting.SigninupCustomRequest(testServer.URL, "test1@gmail.com", "testPass1") -// unittesting.SigninupCustomRequest(testServer.URL, "test2@gmail.com", "testPass2") -// unittesting.SigninupCustomRequest(testServer.URL, "test3@gmail.com", "testPass3") -// unittesting.SigninupCustomRequest(testServer.URL, "test4@gmail.com", "testPass4") - -// userPaginationResult, err := supertokens.GetUsersNewestFirst(nil, nil, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 5, len(userPaginationResult.Users)) -// assert.Nil(t, userPaginationResult.NextPaginationToken) - -// customLimit := 1 -// userPaginationResult, err = supertokens.GetUsersNewestFirst(nil, &customLimit, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 1, len(userPaginationResult.Users)) -// assert.Equal(t, "test4@gmail.com", userPaginationResult.Users[0].User["email"]) -// assert.Equal(t, "*string", reflect.TypeOf(userPaginationResult.NextPaginationToken).String()) - -// userPaginationResult, err = supertokens.GetUsersNewestFirst(userPaginationResult.NextPaginationToken, &customLimit, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 1, len(userPaginationResult.Users)) -// assert.Equal(t, "test3@gmail.com", userPaginationResult.Users[0].User["email"]) -// assert.Equal(t, "*string", reflect.TypeOf(userPaginationResult.NextPaginationToken).String()) - -// customLimit = 5 -// userPaginationResult, err = supertokens.GetUsersNewestFirst(userPaginationResult.NextPaginationToken, &customLimit, nil, nil) -// if err != nil { -// t.Error(err.Error()) -// } -// assert.Equal(t, 3, len(userPaginationResult.Users)) - -// customInvalidPaginationToken := "invalid-pagination-token" -// customLimit = 10 -// userPaginationResult, err = supertokens.GetUsersNewestFirst(&customInvalidPaginationToken, &customLimit, nil, nil) -// if err != nil { -// assert.Contains(t, err.Error(), "invalid pagination token") -// } else { -// t.Fail() -// } - -// customLimit = -1 -// userPaginationResult, err = supertokens.GetUsersNewestFirst(nil, &customLimit, nil, nil) -// if err != nil { -// assert.Contains(t, err.Error(), "limit must a positive integer with min value 1") -// } else { -// t.Fail() -// } -// } - -// func TestGetUserCount(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, -// CreateAndSendCustomEmail: func(email string, userInputCode, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error { -// return nil -// }, -// }, -// Providers: []tpmodels.TypeProvider{ -// userTestCustomProvider, -// }, -// }), -// }, -// } -// func TestGetUserCount(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(nil), -// Init(tplmodels.TypeInput{ -// FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK", -// ContactMethodEmail: plessmodels.ContactMethodEmailConfig{ -// Enabled: true, -// CreateAndSendCustomEmail: func(email string, userInputCode, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error { -// return nil -// }, -// }, -// Providers: []tpmodels.ProviderInput{ -// userTestCustomProvider, -// }, -// }), -// }, -// } - -// 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() -// testServer := httptest.NewServer(supertokens.Middleware(mux)) -// defer testServer.Close() - -// userCount, err := supertokens.GetUserCount(nil) -// if err != nil { -// t.Error(err.Error()) -// } - -// assert.Equal(t, 0.0, userCount) - -// unittesting.SigninupCustomRequest(testServer.URL, "test@gmail.com", "testPass0") - -// userCount, err = supertokens.GetUserCount(nil) -// if err != nil { -// t.Error(err.Error()) -// } - -// assert.Equal(t, 1.0, userCount) - -// unittesting.SigninupCustomRequest(testServer.URL, "test1@gmail.com", "testPass1") -// unittesting.SigninupCustomRequest(testServer.URL, "test2@gmail.com", "testPass2") -// unittesting.SigninupCustomRequest(testServer.URL, "test3@gmail.com", "testPass3") -// unittesting.SigninupCustomRequest(testServer.URL, "test4@gmail.com", "testPass4") - -// userCount, err = supertokens.GetUserCount(nil) -// if err != nil { -// t.Error(err.Error()) -// } - -// assert.Equal(t, 5.0, userCount) -// } diff --git a/recipe/thirdpartypasswordless/utils.go b/recipe/thirdpartypasswordless/utils.go deleted file mode 100644 index c0233af8..00000000 --- a/recipe/thirdpartypasswordless/utils.go +++ /dev/null @@ -1,93 +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 ( - "github.com/supertokens/supertokens-golang/ingredients/emaildelivery" - "github.com/supertokens/supertokens-golang/ingredients/smsdelivery" - "github.com/supertokens/supertokens-golang/recipe/passwordless" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/emaildelivery/backwardCompatibilityService" - smsBackwardCompatibilityService "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/smsdelivery/backwardCompatibilityService" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" - "github.com/supertokens/supertokens-golang/supertokens" -) - -func validateAndNormaliseUserInput(recipeInstance *Recipe, appInfo supertokens.NormalisedAppinfo, config tplmodels.TypeInput) (tplmodels.TypeNormalisedInput, error) { - typeNormalisedInput := makeTypeNormalisedInput(recipeInstance, config) - - typeNormalisedInput.GetEmailDeliveryConfig = func() emaildelivery.TypeInputWithService { - sendPasswordlessLoginEmail := passwordless.DefaultCreateAndSendCustomEmail(appInfo) - - emailService := backwardCompatibilityService.MakeBackwardCompatibilityService(appInfo, sendPasswordlessLoginEmail) - if config.EmailDelivery != nil && config.EmailDelivery.Service != nil { - emailService = *config.EmailDelivery.Service - } - result := emaildelivery.TypeInputWithService{ - Service: emailService, - } - if config.EmailDelivery != nil && config.EmailDelivery.Override != nil { - result.Override = config.EmailDelivery.Override - } - - return result - } - - typeNormalisedInput.GetSmsDeliveryConfig = func() smsdelivery.TypeInputWithService { - sendPasswordlessLoginSms := passwordless.DefaultCreateAndSendCustomTextMessage(appInfo) - - smsService := smsBackwardCompatibilityService.MakeBackwardCompatibilityService(sendPasswordlessLoginSms) - if config.SmsDelivery != nil && config.SmsDelivery.Service != nil { - smsService = *config.SmsDelivery.Service - } - result := smsdelivery.TypeInputWithService{ - Service: smsService, - } - if config.SmsDelivery != nil && config.SmsDelivery.Override != nil { - result.Override = config.SmsDelivery.Override - } - return result - } - - if config.Override != nil { - if config.Override.Functions != nil { - typeNormalisedInput.Override.Functions = config.Override.Functions - } - if config.Override.APIs != nil { - typeNormalisedInput.Override.APIs = config.Override.APIs - } - } - - return typeNormalisedInput, nil -} - -func makeTypeNormalisedInput(recipeInstance *Recipe, inputConfig tplmodels.TypeInput) tplmodels.TypeNormalisedInput { - return tplmodels.TypeNormalisedInput{ - Providers: inputConfig.Providers, - ContactMethodPhone: inputConfig.ContactMethodPhone, - ContactMethodEmail: inputConfig.ContactMethodEmail, - ContactMethodEmailOrPhone: inputConfig.ContactMethodEmailOrPhone, - FlowType: inputConfig.FlowType, - GetCustomUserInputCode: inputConfig.GetCustomUserInputCode, - Override: tplmodels.OverrideStruct{ - Functions: func(originalImplementation tplmodels.RecipeInterface) tplmodels.RecipeInterface { - return originalImplementation - }, - APIs: func(originalImplementation tplmodels.APIInterface) tplmodels.APIInterface { - return originalImplementation - }, - }, - } -} diff --git a/supertokens/constants.go b/supertokens/constants.go index a62dfa7f..879aa905 100644 --- a/supertokens/constants.go +++ b/supertokens/constants.go @@ -21,7 +21,7 @@ const ( ) // VERSION current version of the lib -const VERSION = "0.19.0" +const VERSION = "0.20.0" var ( cdiSupported = []string{"3.0"} diff --git a/supertokens/supertokens.go b/supertokens/supertokens.go index b3bc166e..630d782f 100644 --- a/supertokens/supertokens.go +++ b/supertokens/supertokens.go @@ -181,41 +181,69 @@ func (s *superTokens) middleware(theirHandler http.Handler) http.Handler { requestRID = "" } if requestRID != "" { - var matchedRecipe *RecipeModule + var matchedRecipes []RecipeModule = []RecipeModule{} for _, recipeModule := range s.RecipeModules { LogDebugMessage("middleware: Checking recipe ID for match: " + recipeModule.GetRecipeID()) if recipeModule.GetRecipeID() == requestRID { - matchedRecipe = &recipeModule - break + matchedRecipes = append(matchedRecipes, recipeModule) + } else if requestRID == "thirdpartyemailpassword" { + if recipeModule.GetRecipeID() == "thirdparty" || + recipeModule.GetRecipeID() == "emailpassword" { + matchedRecipes = append(matchedRecipes, recipeModule) + } + } else if requestRID == "thirdpartypasswordless" { + if recipeModule.GetRecipeID() == "thirdparty" || + recipeModule.GetRecipeID() == "passwordless" { + matchedRecipes = append(matchedRecipes, recipeModule) + } } } - if matchedRecipe == nil { - LogDebugMessage("middleware: Not handling because no recipe matched") - theirHandler.ServeHTTP(dw, r) + if len(matchedRecipes) == 0 { + LogDebugMessage("middleware: Not handling because no recipe matched. Trying without rid") + s.middlewareHelperHandleWithoutRid(path, method, userContext, theirHandler, dw, r) return } - LogDebugMessage("middleware: Matched with recipe ID: " + matchedRecipe.GetRecipeID()) + for _, matchedRecipe := range matchedRecipes { + LogDebugMessage("middleware: Matched with recipe IDs: " + matchedRecipe.GetRecipeID()) + } - id, tenantId, err := matchedRecipe.ReturnAPIIdIfCanHandleRequest(path, method, userContext) + var id *string = nil + var finalTenantId *string = nil + var finalMatchedRecipe RecipeModule = RecipeModule{} - if err != nil { - err = s.errorHandler(err, r, dw, userContext) - if err != nil && !dw.IsDone() { - s.OnSuperTokensAPIError(err, r, dw) + for _, matchedRecipe := range matchedRecipes { + currId, currTenantId, err := matchedRecipe.ReturnAPIIdIfCanHandleRequest(path, method, userContext) + if err != nil { + err = s.errorHandler(err, r, dw, userContext) + if err != nil && !dw.IsDone() { + s.OnSuperTokensAPIError(err, r, dw) + } + return + } + + if currId != nil { + if id != nil { + if !dw.IsDone() { + s.OnSuperTokensAPIError(errors.New("Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."), r, dw) + } + return + } else { + id = currId + finalTenantId = &currTenantId + finalMatchedRecipe = matchedRecipe + } } - return } - if id == nil { - LogDebugMessage("middleware: Not handling because recipe doesn't handle request path or method. Request path: " + path.GetAsStringDangerous() + ", request method: " + method) - theirHandler.ServeHTTP(dw, r) + if id == nil || finalTenantId == nil { + s.middlewareHelperHandleWithoutRid(path, method, userContext, theirHandler, dw, r) return } LogDebugMessage("middleware: Request being handled by recipe. ID is: " + *id) - tenantId, err = GetTenantIdFuncFromUsingMultitenancyRecipe(tenantId, userContext) + var tenantId, err = GetTenantIdFuncFromUsingMultitenancyRecipe(*finalTenantId, userContext) if err != nil { err = s.errorHandler(err, r, dw, userContext) if err != nil && !dw.IsDone() { @@ -224,7 +252,7 @@ func (s *superTokens) middleware(theirHandler http.Handler) http.Handler { return } - apiErr := matchedRecipe.HandleAPIRequest(*id, tenantId, r, dw, theirHandler.ServeHTTP, path, method, userContext) + apiErr := finalMatchedRecipe.HandleAPIRequest(*id, tenantId, r, dw, theirHandler.ServeHTTP, path, method, userContext) if apiErr != nil { apiErr = s.errorHandler(apiErr, r, dw, userContext) if apiErr != nil && !dw.IsDone() { @@ -234,36 +262,40 @@ func (s *superTokens) middleware(theirHandler http.Handler) http.Handler { } LogDebugMessage("middleware: Ended") } else { - for _, recipeModule := range s.RecipeModules { - id, tenantId, err := recipeModule.ReturnAPIIdIfCanHandleRequest(path, method, userContext) - LogDebugMessage("middleware: Checking recipe ID for match: " + recipeModule.GetRecipeID()) - if err != nil { - err = s.errorHandler(err, r, dw, userContext) - if err != nil && !dw.IsDone() { - s.OnSuperTokensAPIError(err, r, dw) - } - return - } + s.middlewareHelperHandleWithoutRid(path, method, userContext, theirHandler, dw, r) + } + }) +} - if id != nil { - LogDebugMessage("middleware: Request being handled by recipe. ID is: " + *id) - err := recipeModule.HandleAPIRequest(*id, tenantId, r, dw, theirHandler.ServeHTTP, path, method, userContext) - if err != nil { - err = s.errorHandler(err, r, dw, userContext) - if err != nil && !dw.IsDone() { - s.OnSuperTokensAPIError(err, r, dw) - } - } else { - LogDebugMessage("middleware: Ended") - } - return - } +func (s *superTokens) middlewareHelperHandleWithoutRid(path NormalisedURLPath, method string, userContext *map[string]interface{}, theirHandler http.Handler, dw DoneWriter, r *http.Request) { + for _, recipeModule := range s.RecipeModules { + id, tenantId, err := recipeModule.ReturnAPIIdIfCanHandleRequest(path, method, userContext) + LogDebugMessage("middleware: Checking recipe ID for match: " + recipeModule.GetRecipeID()) + if err != nil { + err = s.errorHandler(err, r, dw, userContext) + if err != nil && !dw.IsDone() { + s.OnSuperTokensAPIError(err, r, dw) } + return + } - LogDebugMessage("middleware: Not handling because no recipe matched") - theirHandler.ServeHTTP(dw, r) + if id != nil { + LogDebugMessage("middleware: Request being handled by recipe. ID is: " + *id) + err := recipeModule.HandleAPIRequest(*id, tenantId, r, dw, theirHandler.ServeHTTP, path, method, userContext) + if err != nil { + err = s.errorHandler(err, r, dw, userContext) + if err != nil && !dw.IsDone() { + s.OnSuperTokensAPIError(err, r, dw) + } + } else { + LogDebugMessage("middleware: Ended") + } + return } - }) + } + + LogDebugMessage("middleware: Not handling because no recipe matched") + theirHandler.ServeHTTP(dw, r) } func (s *superTokens) getAllCORSHeaders() []string { diff --git a/test/auth-react-server/main.go b/test/auth-react-server/main.go index 7f79e2e8..531ae598 100644 --- a/test/auth-react-server/main.go +++ b/test/auth-react-server/main.go @@ -44,10 +44,6 @@ import ( "github.com/supertokens/supertokens-golang/recipe/session/sessmodels" "github.com/supertokens/supertokens-golang/recipe/thirdparty" "github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword" - "github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless" - "github.com/supertokens/supertokens-golang/recipe/thirdpartypasswordless/tplmodels" "github.com/supertokens/supertokens-golang/recipe/userroles" "github.com/supertokens/supertokens-golang/recipe/userroles/userrolesclaims" "github.com/supertokens/supertokens-golang/supertokens" @@ -94,8 +90,6 @@ func callSTInit(passwordlessConfig *plessmodels.TypeInput) { passwordless.ResetForTest() session.ResetForTest() thirdparty.ResetForTest() - thirdpartyemailpassword.ResetForTest() - thirdpartypasswordless.ResetForTest() userroles.ResetForTest() multitenancy.ResetForTest() @@ -332,134 +326,6 @@ func callSTInit(passwordlessConfig *plessmodels.TypeInput) { }, }, }), - thirdpartyemailpassword.Init(&tpepmodels.TypeInput{ - EmailDelivery: &emaildelivery.TypeInput{ - Service: &emaildelivery.EmailDeliveryInterface{ - SendEmail: &sendPasswordResetEmail, - }, - }, - Override: &tpepmodels.OverrideStruct{ - APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface { - ogPasswordResetPOST := *originalImplementation.PasswordResetPOST - ogGeneratePasswordResetTokenPOST := *originalImplementation.GeneratePasswordResetTokenPOST - ogEmailExistsGET := *originalImplementation.EmailPasswordEmailExistsGET - ogSignUpPOST := *originalImplementation.EmailPasswordSignUpPOST - ogSignInPOST := *originalImplementation.EmailPasswordSignInPOST - ogAuthorisationUrlGET := *originalImplementation.AuthorisationUrlGET - ogSignInUpPOST := *originalImplementation.ThirdPartySignInUpPOST - - (*originalImplementation.AuthorisationUrlGET) = func(provider *tpmodels.TypeProvider, redirectURIOnProviderDashboard string, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.AuthorisationUrlGETResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API authorisation url get", true) - if gr != nil { - return tpmodels.AuthorisationUrlGETResponse{ - GeneralError: gr, - }, nil - } - return ogAuthorisationUrlGET(provider, redirectURIOnProviderDashboard, tenantId, options, userContext) - } - - (*originalImplementation.ThirdPartySignInUpPOST) = func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.ThirdPartySignInUpPOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API sign in up", false) - if gr != nil { - return tpepmodels.ThirdPartySignInUpPOSTResponse{ - GeneralError: gr, - }, nil - } - return ogSignInUpPOST(provider, input, tenantId, options, userContext) - } - - (*originalImplementation.PasswordResetPOST) = func(formFields []epmodels.TypeFormField, token string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.ResetPasswordPOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API reset password consume", false) - if gr != nil { - return epmodels.ResetPasswordPOSTResponse{ - GeneralError: gr, - }, nil - } - return ogPasswordResetPOST(formFields, token, tenantId, options, userContext) - } - - (*originalImplementation.GeneratePasswordResetTokenPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.GeneratePasswordResetTokenPOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API reset password", false) - if gr != nil { - return epmodels.GeneratePasswordResetTokenPOSTResponse{ - GeneralError: gr, - }, nil - } - return ogGeneratePasswordResetTokenPOST(formFields, tenantId, options, userContext) - } - - (*originalImplementation.EmailPasswordEmailExistsGET) = func(email string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.EmailExistsGETResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API email exists", true) - if gr != nil { - return epmodels.EmailExistsGETResponse{ - GeneralError: gr, - }, nil - } - return ogEmailExistsGET(email, tenantId, options, userContext) - } - - (*originalImplementation.EmailPasswordSignUpPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.SignUpPOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API sign up", false) - if gr != nil { - return tpepmodels.SignUpPOSTResponse{ - GeneralError: gr, - }, nil - } - return ogSignUpPOST(formFields, tenantId, options, userContext) - } - - (*originalImplementation.EmailPasswordSignInPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.SignInPOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API sign in", false) - if gr != nil { - return tpepmodels.SignInPOSTResponse{ - GeneralError: gr, - }, nil - } - return ogSignInPOST(formFields, tenantId, options, userContext) - } - return originalImplementation - }, - }, - SignUpFeature: &epmodels.TypeInputSignUp{ - FormFields: formFields, - }, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: os.Getenv("GOOGLE_CLIENT_ID"), - ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"), - }, - }, - }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: os.Getenv("GITHUB_CLIENT_ID"), - ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"), - }, - }, - }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "facebook", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: os.Getenv("FACEBOOK_CLIENT_ID"), - ClientSecret: os.Getenv("FACEBOOK_CLIENT_SECRET"), - }, - }, - }, - }, - customAuth0Provider(), - }, - }), session.Init(&sessmodels.TypeInput{ Override: &sessmodels.OverrideStruct{ APIs: func(originalImplementation sessmodels.APIInterface) sessmodels.APIInterface { @@ -533,111 +399,6 @@ func callSTInit(passwordlessConfig *plessmodels.TypeInput) { }, }, }), - thirdpartypasswordless.Init(tplmodels.TypeInput{ - ContactMethodPhone: passwordlessConfig.ContactMethodPhone, - ContactMethodEmail: passwordlessConfig.ContactMethodEmail, - ContactMethodEmailOrPhone: passwordlessConfig.ContactMethodEmailOrPhone, - FlowType: passwordlessConfig.FlowType, - GetCustomUserInputCode: passwordlessConfig.GetCustomUserInputCode, - EmailDelivery: passwordlessConfig.EmailDelivery, - SmsDelivery: passwordlessConfig.SmsDelivery, - Providers: []tpmodels.ProviderInput{ - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "google", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: os.Getenv("GOOGLE_CLIENT_ID"), - ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"), - }, - }, - }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "github", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: os.Getenv("GITHUB_CLIENT_ID"), - ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"), - }, - }, - }, - }, - { - Config: tpmodels.ProviderConfig{ - ThirdPartyId: "facebook", - Clients: []tpmodels.ProviderClientConfig{ - { - ClientID: os.Getenv("FACEBOOK_CLIENT_ID"), - ClientSecret: os.Getenv("FACEBOOK_CLIENT_SECRET"), - }, - }, - }, - }, - customAuth0Provider(), - }, - Override: &tplmodels.OverrideStruct{ - APIs: func(originalImplementation tplmodels.APIInterface) tplmodels.APIInterface { - ogConsumeCodePOST := *originalImplementation.ConsumeCodePOST - ogCreateCodePOST := *originalImplementation.CreateCodePOST - ogResendCodePOST := *originalImplementation.ResendCodePOST - ogAuthorisationUrlGET := *originalImplementation.AuthorisationUrlGET - ogSignInUpPOST := *originalImplementation.ThirdPartySignInUpPOST - - (*originalImplementation.AuthorisationUrlGET) = func(provider *tpmodels.TypeProvider, redirectURIOnProviderDashboard string, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tpmodels.AuthorisationUrlGETResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API authorisation url get", true) - if gr != nil { - return tpmodels.AuthorisationUrlGETResponse{ - GeneralError: gr, - }, nil - } - return ogAuthorisationUrlGET(provider, redirectURIOnProviderDashboard, tenantId, options, userContext) - } - - (*originalImplementation.ThirdPartySignInUpPOST) = func(provider *tpmodels.TypeProvider, input tpmodels.TypeSignInUpInput, tenantId string, options tpmodels.APIOptions, userContext supertokens.UserContext) (tplmodels.ThirdPartySignInUpPOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API sign in up", false) - if gr != nil { - return tplmodels.ThirdPartySignInUpPOSTResponse{ - GeneralError: gr, - }, nil - } - return ogSignInUpPOST(provider, input, tenantId, options, userContext) - } - - (*originalImplementation.ConsumeCodePOST) = func(userInput *plessmodels.UserInputCodeWithDeviceID, linkCode *string, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (tplmodels.ConsumeCodePOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API consume code", false) - if gr != nil { - return tplmodels.ConsumeCodePOSTResponse{ - GeneralError: gr, - }, nil - } - return ogConsumeCodePOST(userInput, linkCode, preAuthSessionID, tenantId, options, userContext) - } - - (*originalImplementation.CreateCodePOST) = func(email, phoneNumber *string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.CreateCodePOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API create code", false) - if gr != nil { - return plessmodels.CreateCodePOSTResponse{ - GeneralError: gr, - }, nil - } - return ogCreateCodePOST(email, phoneNumber, tenantId, options, userContext) - } - - (*originalImplementation.ResendCodePOST) = func(deviceID, preAuthSessionID string, tenantId string, options plessmodels.APIOptions, userContext supertokens.UserContext) (plessmodels.ResendCodePOSTResponse, error) { - gr := returnGeneralErrorIfNeeded(*options.Req, "general error from API resend code", false) - if gr != nil { - return plessmodels.ResendCodePOSTResponse{ - GeneralError: gr, - }, nil - } - return ogResendCodePOST(deviceID, preAuthSessionID, tenantId, options, userContext) - } - return originalImplementation - }, - }, - }), userroles.Init(nil), }, }) diff --git a/test/unittesting/testingutils.go b/test/unittesting/testingutils.go index db74e06c..0abdfa76 100644 --- a/test/unittesting/testingutils.go +++ b/test/unittesting/testingutils.go @@ -572,6 +572,42 @@ func SignInRequest(email string, password string, testUrl string) (*http.Respons return resp, nil } +func SignInRequestWithThirdpartyemailpasswordRid(email string, password string, testUrl string) (*http.Response, error) { + formFields := map[string][]map[string]string{ + "formFields": { + { + "id": "email", + "value": email, + }, + { + "id": "password", + "value": password, + }, + }, + } + + postBody, err := json.Marshal(formFields) + if err != nil { + fmt.Println(err.Error()) + return nil, err + } + + client := &http.Client{} + req, _ := http.NewRequest("POST", testUrl+"/auth/signin", bytes.NewBuffer(postBody)) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("rid", "thirdpartyemailpassword") + + resp, err := client.Do(req) + + if err != nil { + fmt.Println(err.Error()) + return nil, err + } + + return resp, nil +} + func SignInRequestWithTenantId(tenantId string, email string, password string, testUrl string) (*http.Response, error) { formFields := map[string][]map[string]string{ "formFields": {