diff --git a/internal/mailer/mailer.go b/internal/mailer/mailer.go index 9206adc54..b1aac769b 100644 --- a/internal/mailer/mailer.go +++ b/internal/mailer/mailer.go @@ -25,6 +25,12 @@ type Mailer interface { GetEmailActionLink(user *models.User, actionType, referrerURL string, externalURL *url.URL) (string, error) } +type EmailParams struct { + Token string + Type string + RedirectTo string +} + // NewMailer returns a new gotrue mailer func NewMailer(globalConfig *conf.GlobalConfiguration) Mailer { mail := gomail.NewMessage() @@ -64,7 +70,7 @@ func withDefault(value, defaultValue string) string { return value } -func getPath(filepath string, params map[string]string) (*url.URL, error) { +func getPath(filepath string, params *EmailParams) (*url.URL, error) { path := &url.URL{} if filepath != "" { if p, err := url.Parse(filepath); err != nil { @@ -73,11 +79,8 @@ func getPath(filepath string, params map[string]string) (*url.URL, error) { path = p } } - v := url.Values{} - for key, val := range params { - v.Add(key, val) + if params != nil { + path.RawQuery = fmt.Sprintf("token=%s&type=%s&redirect_to=%s", url.QueryEscape(params.Token), url.QueryEscape(params.Type), encodeRedirectURL(params.RedirectTo)) } - // this should never return an error because we're always encoding the values first - path.RawQuery, _ = url.QueryUnescape(v.Encode()) return path, nil } diff --git a/internal/mailer/mailer_test.go b/internal/mailer/mailer_test.go index 1992f5a8e..290d65dd0 100644 --- a/internal/mailer/mailer_test.go +++ b/internal/mailer/mailer_test.go @@ -15,10 +15,15 @@ func enforceRelativeURL(url string) string { } func TestGetPath(t *testing.T) { + params := EmailParams{ + Token: "token", + Type: "signup", + RedirectTo: "https://example.com", + } cases := []struct { SiteURL string Path string - Params map[string]string + Params *EmailParams Expected string }{ { @@ -40,29 +45,16 @@ func TestGetPath(t *testing.T) { Expected: "https://test.example.com/trailingslash/", }, { - SiteURL: "https://test.example.com", - Path: "f", - Params: map[string]string{ - "key": "val", - }, - Expected: "https://test.example.com/f?key=val", - }, - { - SiteURL: "https://test.example.com", - Path: "", - Params: map[string]string{ - "key": "val", - }, - Expected: "https://test.example.com?key=val", + SiteURL: "https://test.example.com", + Path: "f", + Params: ¶ms, + Expected: "https://test.example.com/f?token=token&type=signup&redirect_to=https://example.com", }, { - SiteURL: "https://test.example.com", - Path: "", - Params: map[string]string{ - "key": "val", - "redirect_to": "http://localhost:3000?param=foo", - }, - Expected: "https://test.example.com?key=val&redirect_to=http://localhost:3000?param=foo", + SiteURL: "https://test.example.com", + Path: "", + Params: ¶ms, + Expected: "https://test.example.com?token=token&type=signup&redirect_to=https://example.com", }, } diff --git a/internal/mailer/template.go b/internal/mailer/template.go index 0fe1329fb..590eab74f 100644 --- a/internal/mailer/template.go +++ b/internal/mailer/template.go @@ -76,10 +76,10 @@ func (m TemplateMailer) ValidateEmail(email string) error { // InviteMail sends a invite mail to a new user func (m *TemplateMailer) InviteMail(user *models.User, otp, referrerURL string, externalURL *url.URL) error { - path, err := getPath(m.Config.Mailer.URLPaths.Invite, map[string]string{ - "token": user.ConfirmationToken, - "type": "invite", - "redirect_to": encodeRedirectURL(referrerURL), + path, err := getPath(m.Config.Mailer.URLPaths.Invite, &EmailParams{ + Token: user.ConfirmationToken, + Type: "invite", + RedirectTo: referrerURL, }) if err != nil { @@ -106,10 +106,10 @@ func (m *TemplateMailer) InviteMail(user *models.User, otp, referrerURL string, // ConfirmationMail sends a signup confirmation mail to a new user func (m *TemplateMailer) ConfirmationMail(user *models.User, otp, referrerURL string, externalURL *url.URL) error { - path, err := getPath(m.Config.Mailer.URLPaths.Confirmation, map[string]string{ - "token": user.ConfirmationToken, - "type": "signup", - "redirect_to": encodeRedirectURL(referrerURL), + path, err := getPath(m.Config.Mailer.URLPaths.Confirmation, &EmailParams{ + Token: user.ConfirmationToken, + Type: "signup", + RedirectTo: referrerURL, }) if err != nil { return err @@ -185,10 +185,10 @@ func (m *TemplateMailer) EmailChangeMail(user *models.User, otpNew, otpCurrent, for _, email := range emails { path, err := getPath( m.Config.Mailer.URLPaths.EmailChange, - map[string]string{ - "token": email.TokenHash, - "type": "email_change", - "redirect_to": encodeRedirectURL(referrerURL), + &EmailParams{ + Token: email.TokenHash, + Type: "email_change", + RedirectTo: referrerURL, }, ) if err != nil { @@ -226,10 +226,10 @@ func (m *TemplateMailer) EmailChangeMail(user *models.User, otpNew, otpCurrent, // RecoveryMail sends a password recovery mail func (m *TemplateMailer) RecoveryMail(user *models.User, otp, referrerURL string, externalURL *url.URL) error { - path, err := getPath(m.Config.Mailer.URLPaths.Recovery, map[string]string{ - "token": user.RecoveryToken, - "type": "recovery", - "redirect_to": encodeRedirectURL(referrerURL), + path, err := getPath(m.Config.Mailer.URLPaths.Recovery, &EmailParams{ + Token: user.RecoveryToken, + Type: "recovery", + RedirectTo: referrerURL, }) if err != nil { return err @@ -254,10 +254,10 @@ func (m *TemplateMailer) RecoveryMail(user *models.User, otp, referrerURL string // MagicLinkMail sends a login link mail func (m *TemplateMailer) MagicLinkMail(user *models.User, otp, referrerURL string, externalURL *url.URL) error { - path, err := getPath(m.Config.Mailer.URLPaths.Recovery, map[string]string{ - "token": user.RecoveryToken, - "type": "magiclink", - "redirect_to": encodeRedirectURL(referrerURL), + path, err := getPath(m.Config.Mailer.URLPaths.Recovery, &EmailParams{ + Token: user.RecoveryToken, + Type: "magiclink", + RedirectTo: referrerURL, }) if err != nil { return err @@ -297,43 +297,42 @@ func (m TemplateMailer) GetEmailActionLink(user *models.User, actionType, referr var err error var path *url.URL - referrerURL = encodeRedirectURL(referrerURL) switch actionType { case "magiclink": - path, err = getPath(m.Config.Mailer.URLPaths.Recovery, map[string]string{ - "token": user.RecoveryToken, - "type": "magiclink", - "redirect_to": referrerURL, + path, err = getPath(m.Config.Mailer.URLPaths.Recovery, &EmailParams{ + Token: user.RecoveryToken, + Type: "magiclink", + RedirectTo: referrerURL, }) case "recovery": - path, err = getPath(m.Config.Mailer.URLPaths.Recovery, map[string]string{ - "token": user.RecoveryToken, - "type": "recovery", - "redirect_to": referrerURL, + path, err = getPath(m.Config.Mailer.URLPaths.Recovery, &EmailParams{ + Token: user.RecoveryToken, + Type: "recovery", + RedirectTo: referrerURL, }) case "invite": - path, err = getPath(m.Config.Mailer.URLPaths.Invite, map[string]string{ - "token": user.ConfirmationToken, - "type": "invite", - "redirect_to": referrerURL, + path, err = getPath(m.Config.Mailer.URLPaths.Invite, &EmailParams{ + Token: user.ConfirmationToken, + Type: "invite", + RedirectTo: referrerURL, }) case "signup": - path, err = getPath(m.Config.Mailer.URLPaths.Confirmation, map[string]string{ - "token": user.ConfirmationToken, - "type": "signup", - "redirect_to": referrerURL, + path, err = getPath(m.Config.Mailer.URLPaths.Confirmation, &EmailParams{ + Token: user.ConfirmationToken, + Type: "signup", + RedirectTo: referrerURL, }) case "email_change_current": - path, err = getPath(m.Config.Mailer.URLPaths.EmailChange, map[string]string{ - "token": user.EmailChangeTokenCurrent, - "type": "email_change", - "redirect_to": referrerURL, + path, err = getPath(m.Config.Mailer.URLPaths.EmailChange, &EmailParams{ + Token: user.EmailChangeTokenCurrent, + Type: "email_change", + RedirectTo: referrerURL, }) case "email_change_new": - path, err = getPath(m.Config.Mailer.URLPaths.EmailChange, map[string]string{ - "token": user.EmailChangeTokenNew, - "type": "email_change", - "redirect_to": referrerURL, + path, err = getPath(m.Config.Mailer.URLPaths.EmailChange, &EmailParams{ + Token: user.EmailChangeTokenNew, + Type: "email_change", + RedirectTo: referrerURL, }) default: return "", fmt.Errorf("invalid email action link type: %s", actionType)