Skip to content

Commit c5d03ef

Browse files
authored
Merge pull request #64 from supabase/feature/email-referer
Feature/email referer
2 parents 4c8a68a + 8d14850 commit c5d03ef

File tree

3 files changed

+55
-9
lines changed

3 files changed

+55
-9
lines changed

api/helpers.go

+16
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ func (a *API) getReferrer(r *http.Request) string {
111111
return referrer
112112
}
113113

114+
// validateRedirectURL ensures any redirect URL is from a safe origin
115+
func (a *API) validateRedirectURL(r *http.Request, reqref string) string {
116+
ctx := r.Context()
117+
config := a.getConfig(ctx)
118+
redirectURL := config.SiteURL
119+
if reqref != "" {
120+
base, berr := url.Parse(config.SiteURL)
121+
refurl, rerr := url.Parse(reqref)
122+
// As long as the referrer came from the site, we will redirect back there
123+
if berr == nil && rerr == nil && base.Hostname() == refurl.Hostname() {
124+
redirectURL = reqref
125+
}
126+
}
127+
return redirectURL
128+
}
129+
114130
var privateIPBlocks []*net.IPNet
115131

116132
func init() {

api/verify.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ const (
2828

2929
// VerifyParams are the parameters the Verify endpoint accepts
3030
type VerifyParams struct {
31-
Type string `json:"type"`
32-
Token string `json:"token"`
33-
Password string `json:"password"`
31+
Type string `json:"type"`
32+
Token string `json:"token"`
33+
Password string `json:"password"`
34+
RedirectTo string `json:"redirect_to"`
3435
}
3536

3637
// Verify exchanges a confirmation or recovery token to a refresh token
@@ -47,6 +48,7 @@ func (a *API) Verify(w http.ResponseWriter, r *http.Request) error {
4748
params.Token = r.FormValue("token")
4849
params.Password = ""
4950
params.Type = r.FormValue("type")
51+
params.RedirectTo = a.validateRedirectURL(r, r.FormValue("redirect_to"))
5052
case "POST":
5153
jsonDecoder := json.NewDecoder(r.Body)
5254
if err := jsonDecoder.Decode(params); err != nil {
@@ -110,7 +112,10 @@ func (a *API) Verify(w http.ResponseWriter, r *http.Request) error {
110112
// GET requests should return to the app site after confirmation
111113
switch r.Method {
112114
case "GET":
113-
rurl := config.SiteURL
115+
rurl := params.RedirectTo
116+
if rurl == "" {
117+
rurl = config.SiteURL
118+
}
114119
if token != nil {
115120
q := url.Values{}
116121
q.Set("access_token", token.Token)

mailer/template.go

+30-5
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ func (m TemplateMailer) ValidateEmail(email string) error {
5151
func (m *TemplateMailer) InviteMail(user *models.User, referrerURL string) error {
5252
globalConfig, err := conf.LoadGlobal(configFile)
5353

54-
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Invite, "token="+user.ConfirmationToken+"&type=invite")
54+
redirectParam := ""
55+
if len(referrerURL) > 0 {
56+
redirectParam = "&redirect_to=" + referrerURL
57+
}
58+
59+
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Invite, "token="+user.ConfirmationToken+"&type=invite"+redirectParam)
5560
if err != nil {
5661
return err
5762
}
@@ -76,7 +81,12 @@ func (m *TemplateMailer) InviteMail(user *models.User, referrerURL string) error
7681
func (m *TemplateMailer) ConfirmationMail(user *models.User, referrerURL string) error {
7782
globalConfig, err := conf.LoadGlobal(configFile)
7883

79-
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Confirmation, "token="+user.ConfirmationToken+"&type=signup")
84+
redirectParam := ""
85+
if len(referrerURL) > 0 {
86+
redirectParam = "&redirect_to=" + referrerURL
87+
}
88+
89+
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Confirmation, "token="+user.ConfirmationToken+"&type=signup"+redirectParam)
8090
if err != nil {
8191
return err
8292
}
@@ -99,7 +109,12 @@ func (m *TemplateMailer) ConfirmationMail(user *models.User, referrerURL string)
99109

100110
// EmailChangeMail sends an email change confirmation mail to a user
101111
func (m *TemplateMailer) EmailChangeMail(user *models.User, referrerURL string) error {
102-
url, err := getSiteURL(referrerURL, m.Config.SiteURL, m.Config.Mailer.URLPaths.EmailChange, "email_change_token="+user.EmailChangeToken)
112+
redirectParam := ""
113+
if len(referrerURL) > 0 {
114+
redirectParam = "&redirect_to=" + referrerURL
115+
}
116+
117+
url, err := getSiteURL(referrerURL, m.Config.SiteURL, m.Config.Mailer.URLPaths.EmailChange, "email_change_token="+user.EmailChangeToken+"&type=email_change"+redirectParam)
103118
if err != nil {
104119
return err
105120
}
@@ -125,7 +140,12 @@ func (m *TemplateMailer) EmailChangeMail(user *models.User, referrerURL string)
125140
func (m *TemplateMailer) RecoveryMail(user *models.User, referrerURL string) error {
126141
globalConfig, err := conf.LoadGlobal(configFile)
127142

128-
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Recovery, "token="+user.RecoveryToken+"&type=recovery")
143+
redirectParam := ""
144+
if len(referrerURL) > 0 {
145+
redirectParam = "&redirect_to=" + referrerURL
146+
}
147+
148+
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Recovery, "token="+user.RecoveryToken+"&type=recovery"+redirectParam)
129149
if err != nil {
130150
return err
131151
}
@@ -150,7 +170,12 @@ func (m *TemplateMailer) RecoveryMail(user *models.User, referrerURL string) err
150170
func (m *TemplateMailer) MagicLinkMail(user *models.User, referrerURL string) error {
151171
globalConfig, err := conf.LoadGlobal(configFile)
152172

153-
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Recovery, "token="+user.RecoveryToken+"&type=magiclink")
173+
redirectParam := ""
174+
if len(referrerURL) > 0 {
175+
redirectParam = "&redirect_to=" + referrerURL
176+
}
177+
178+
url, err := getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Recovery, "token="+user.RecoveryToken+"&type=magiclink"+redirectParam)
154179
if err != nil {
155180
return err
156181
}

0 commit comments

Comments
 (0)