Skip to content

Commit

Permalink
Merge pull request #389 from supertokens/appinfo-refactor
Browse files Browse the repository at this point in the history
app info refactor
  • Loading branch information
rishabhpoddar authored Nov 25, 2023
2 parents ac39010 + b6d37a3 commit 1f08095
Show file tree
Hide file tree
Showing 44 changed files with 902 additions and 222 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]


## [0.17.1] - 2023-11-24

### Added

- Adds support for configuring multiple frontend domains to be used with the same backend
- Added new `Origin` and `GetOrigin` properties to `AppInfo`, this can be configured to allow you to conditionally return the value of the frontend domain. This property will replace `WebsiteDomain` in a future release of `supertokens-golang`
- `WebsiteDomain` inside `AppInfo` is now optional. Using `Origin` or `GetOrigin` is recommended over using `WebsiteDomain`. This is not a breaking change and using `WebsiteDomain` will continue to work.

## [0.17.0] - 2023-11-14

### Breaking change
Expand Down
7 changes: 6 additions & 1 deletion recipe/dashboard/api/analyticsPOST.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,13 @@ func AnalyticsPost(apiInterface dashboardmodels.APIInterface, tenantId string, o
}
}

websiteDomain, err := supertokensInstance.AppInfo.GetOrigin(nil, &map[string]interface{}{})
if err != nil {
return analyticsPostResponse{}, err
}

data := map[string]interface{}{
"websiteDomain": supertokensInstance.AppInfo.WebsiteDomain.GetAsStringDangerous(),
"websiteDomain": websiteDomain.GetAsStringDangerous(),
"apiDomain": supertokensInstance.AppInfo.APIDomain.GetAsStringDangerous(),
"appName": supertokensInstance.AppInfo.AppName,
"sdk": "golang",
Expand Down
2 changes: 1 addition & 1 deletion recipe/dashboard/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func (r *Recipe) getAllCORSHeaders() []string {
return []string{}
}

func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter) (bool, error) {
func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) {
return false, nil
}

Expand Down
4 changes: 3 additions & 1 deletion recipe/emailpassword/api/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ func MakeAPIImplementation() epmodels.APIInterface {
}, nil
}

passwordResetLink := GetPasswordResetLink(
passwordResetLink, err := GetPasswordResetLink(
options.AppInfo,
options.RecipeID,
response.OK.Token,
tenantId,
options.Req,
userContext,
)

if err != nil {
Expand Down
11 changes: 8 additions & 3 deletions recipe/emailpassword/api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package api
import (
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
Expand Down Expand Up @@ -125,13 +126,17 @@ func validateFormOrThrowError(configFormFields []epmodels.NormalisedFormField, i
return nil
}

func GetPasswordResetLink(appInfo supertokens.NormalisedAppinfo, recipeID string, token string, tenantId string) string {
func GetPasswordResetLink(appInfo supertokens.NormalisedAppinfo, recipeID string, 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",
appInfo.WebsiteDomain.GetAsStringDangerous(),
websiteDomain.GetAsStringDangerous(),
appInfo.WebsiteBasePath.GetAsStringDangerous(),
token,
recipeID,
tenantId,
)
), nil
}
20 changes: 14 additions & 6 deletions recipe/emailpassword/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,22 @@ func CreateResetPasswordLink(tenantId string, userID string, userContext ...supe
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: api.GetPasswordResetLink(
instance.RecipeModule.GetAppInfo(),
instance.RecipeModule.GetRecipeID(),
tokenResponse.OK.Token,
tenantId,
),
Link: link,
},
}, nil
}
Expand Down
103 changes: 103 additions & 0 deletions recipe/emailpassword/passwordReset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,106 @@ func TestValidTokenInputAndPasswordHasChanged(t *testing.T) {
assert.Equal(t, userInfo["id"], result3["user"].(map[string]interface{})["id"].(string))
assert.Equal(t, userInfo["email"], result3["user"].(map[string]interface{})["email"].(string))
}

func TestPasswordResetLinkUsesOriginFunctionIfProvided(t *testing.T) {
resetURL := ""
tokenInfo := ""
ridInfo := ""
sendEmailFunc := func(input emaildelivery.EmailType, userContext supertokens.UserContext) error {
u, err := url.Parse(input.PasswordReset.PasswordResetLink)
if err != nil {
return err
}
resetURL = u.Scheme + "://" + u.Host + u.Path
tokenInfo = u.Query().Get("token")
ridInfo = u.Query().Get("rid")
return nil
}
configValue := supertokens.TypeInput{
Supertokens: &supertokens.ConnectionInfo{
ConnectionURI: "http://localhost:8080",
},
AppInfo: supertokens.AppInfo{
APIDomain: "api.supertokens.io",
AppName: "SuperTokens",
GetOrigin: func(request *http.Request, userContext supertokens.UserContext) (string, error) {
// read request body
decoder := json.NewDecoder(request.Body)
var requestBody map[string]interface{}
err := decoder.Decode(&requestBody)
if err != nil {
return "https://supertokens.com", nil
}
if requestBody["origin"] == nil {
return "https://supertokens.com", nil
}
return requestBody["origin"].(string), nil
},
},
RecipeList: []supertokens.Recipe{
Init(&epmodels.TypeInput{
EmailDelivery: &emaildelivery.TypeInput{
Service: &emaildelivery.EmailDeliveryInterface{
SendEmail: &sendEmailFunc,
},
},
}),
session.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.SignupRequest("[email protected]", "validpass123", testServer.URL)
if err != nil {
t.Error(err.Error())
}
assert.NoError(t, err)
dataInBytes, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err.Error())
}
res.Body.Close()
var result map[string]interface{}
err = json.Unmarshal(dataInBytes, &result)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, 200, res.StatusCode)
assert.Equal(t, "OK", result["status"])

formFields := map[string]interface{}{
"origin": "localhost:2000",
"formFields": []map[string]interface{}{{
"id": "email",
"value": "[email protected]",
}},
}

postBody, err := json.Marshal(formFields)
if err != nil {
t.Error(err.Error())
}

resp, err := http.Post(testServer.URL+"/auth/user/password/reset/token", "application/json", bytes.NewBuffer(postBody))

if err != nil {
t.Error(err.Error())
}

assert.NoError(t, err)

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"))
}
2 changes: 1 addition & 1 deletion recipe/emailpassword/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (r *Recipe) getAllCORSHeaders() []string {
return []string{}
}

func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter) (bool, error) {
func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) {
if defaultErrors.As(err, &errors.FieldError{}) {
errs := err.(errors.FieldError)
return true, supertokens.Send200Response(res, map[string]interface{}{
Expand Down
9 changes: 8 additions & 1 deletion recipe/emailverification/api/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,20 @@ func MakeAPIImplementation() evmodels.APIInterface {
ID: userID,
Email: email.OK.Email,
}
emailVerificationURL := GetEmailVerifyLink(

emailVerificationURL, err := GetEmailVerifyLink(
options.AppInfo,
response.OK.Token,
options.RecipeID,
sessionContainer.GetTenantIdWithContext(userContext),
options.Req,
userContext,
)

if err != nil {
return evmodels.GenerateEmailVerifyTokenPOSTResponse{}, err
}

supertokens.LogDebugMessage(fmt.Sprintf("Sending email verification email to %s", email.OK.Email))
err = (*options.EmailDelivery.IngredientInterfaceImpl.SendEmail)(emaildelivery.EmailType{
EmailVerification: &emaildelivery.EmailVerificationType{
Expand Down
11 changes: 8 additions & 3 deletions recipe/emailverification/api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ package api

import (
"fmt"
"net/http"

"github.com/supertokens/supertokens-golang/supertokens"
)

func GetEmailVerifyLink(appInfo supertokens.NormalisedAppinfo, token string, recipeID string, tenantId string) string {
func GetEmailVerifyLink(appInfo supertokens.NormalisedAppinfo, token string, recipeID 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",
appInfo.WebsiteDomain.GetAsStringDangerous(),
websiteDomain.GetAsStringDangerous(),
appInfo.WebsiteBasePath.GetAsStringDangerous(),
token,
recipeID,
tenantId,
)
), nil
}
55 changes: 55 additions & 0 deletions recipe/emailverification/emailverification_email_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"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 TestBackwardCompatibilityServiceWithoutCustomFunction(t *testing.T) {
Expand Down Expand Up @@ -286,6 +287,60 @@ func TestSMTPServiceOverrideDefaultEmailTemplate(t *testing.T) {
assert.Equal(t, sendRawEmailCalled, true)
}

func TestThatLinkUsesResultFromOriginFunction(t *testing.T) {
link := ""
configValue := supertokens.TypeInput{
Supertokens: &supertokens.ConnectionInfo{
ConnectionURI: "http://localhost:8080",
},
AppInfo: supertokens.AppInfo{
APIDomain: "api.supertokens.io",
AppName: "SuperTokens",
GetOrigin: func(request *http.Request, userContext supertokens.UserContext) (string, error) {
return (*userContext)["link"].(string), nil
},
},
RecipeList: []supertokens.Recipe{
Init(evmodels.TypeInput{
Mode: "OPTIONAL",
EmailDelivery: &emaildelivery.TypeInput{
Override: func(originalImplementation emaildelivery.EmailDeliveryInterface) emaildelivery.EmailDeliveryInterface {
(*originalImplementation.SendEmail) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error {
link = input.EmailVerification.EmailVerifyLink
return nil
}
return originalImplementation
},
},
}),
session.Init(nil),
},
}

BeforeEach()
unittesting.StartUpST("localhost", "8080")
defer AfterEach()
err := supertokens.Init(configValue)
if err != nil {
t.Error(err.Error())
}

email := "[email protected]"
resp, err := SendEmailVerificationEmail("public", "userId", &email, &map[string]interface{}{
"link": "localhost:8080",
})
if err != nil {
t.Error(err.Error())
}
assert.True(t, resp.OK != nil)

assert.Equal(t, EmailVerificationEmailSentForTest, false)
// assert that link starts with http://localhost:8080. We use starts with because the link
// can continue a path and random query params too
assert.Equal(t, link[:21], "http://localhost:8080")

}

// func TestSMTPServiceManually(t *testing.T) {
// targetEmail := "..."
// fromEmail := "[email protected]"
Expand Down
8 changes: 7 additions & 1 deletion recipe/emailverification/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,15 @@ 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])

if err != nil {
return evmodels.CreateEmailVerificationLinkResponse{}, err
}

return evmodels.CreateEmailVerificationLinkResponse{
OK: &struct{ Link string }{
Link: api.GetEmailVerifyLink(st.AppInfo, emailVerificationTokenResponse.OK.Token, instance.RecipeModule.GetRecipeID(), tenantId),
Link: link,
},
}, nil
}
Expand Down
2 changes: 1 addition & 1 deletion recipe/emailverification/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func (r *Recipe) getAllCORSHeaders() []string {
return []string{}
}

func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter) (bool, error) {
func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) {
return false, nil
}

Expand Down
2 changes: 1 addition & 1 deletion recipe/jwt/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (r *Recipe) getAllCORSHeaders() []string {
return []string{}
}

func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter) (bool, error) {
func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) {
return false, nil
}

Expand Down
2 changes: 1 addition & 1 deletion recipe/multitenancy/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func (r *Recipe) getAllCORSHeaders() []string {
return []string{}
}

func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter) (bool, error) {
func (r *Recipe) handleError(err error, req *http.Request, res http.ResponseWriter, userContext supertokens.UserContext) (bool, error) {
return false, nil
}

Expand Down
Loading

0 comments on commit 1f08095

Please sign in to comment.