Skip to content

Commit 7c452a8

Browse files
authored
fix: add email_change support to generateLink (#560)
* fix: bump gotrue version * fix: add email_change types to generateLink
1 parent 3d84070 commit 7c452a8

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

api/mail.go

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ var (
2525
type GenerateLinkParams struct {
2626
Type string `json:"type"`
2727
Email string `json:"email"`
28+
NewEmail string `json:"new_email"`
2829
Password string `json:"password"`
2930
Data map[string]interface{} `json:"data"`
3031
RedirectTo string `json:"redirect_to"`
@@ -52,13 +53,13 @@ func (a *API) GenerateLink(w http.ResponseWriter, r *http.Request) error {
5253
user, err := models.FindUserByEmailAndAudience(a.db, instanceID, params.Email, aud)
5354
if err != nil {
5455
if models.IsNotFoundError(err) {
55-
if params.Type == "magiclink" {
56-
params.Type = "signup"
56+
if params.Type == magicLinkVerification {
57+
params.Type = signupVerification
5758
params.Password, err = password.Generate(64, 10, 0, false, true)
5859
if err != nil {
5960
return internalServerError("error creating user").WithInternalError(err)
6061
}
61-
} else if params.Type == "recovery" {
62+
} else if params.Type == recoveryVerification || params.Type == "email_change_current" || params.Type == "email_change_new" {
6263
return notFoundError(err.Error())
6364
}
6465
} else {
@@ -77,14 +78,14 @@ func (a *API) GenerateLink(w http.ResponseWriter, r *http.Request) error {
7778
err = a.db.Transaction(func(tx *storage.Connection) error {
7879
var terr error
7980
switch params.Type {
80-
case "magiclink", "recovery":
81+
case magicLinkVerification, recoveryVerification:
8182
if terr = models.NewAuditLogEntry(tx, instanceID, user, models.UserRecoveryRequestedAction, "", nil); terr != nil {
8283
return terr
8384
}
8485
user.RecoveryToken = hashedToken
8586
user.RecoverySentAt = &now
8687
terr = errors.Wrap(tx.UpdateOnly(user, "recovery_token", "recovery_sent_at"), "Database error updating user for recovery")
87-
case "invite":
88+
case inviteVerification:
8889
if user != nil {
8990
if user.IsConfirmed() {
9091
return unprocessableEntityError(DuplicateEmailMsg)
@@ -111,7 +112,7 @@ func (a *API) GenerateLink(w http.ResponseWriter, r *http.Request) error {
111112
user.ConfirmationSentAt = &now
112113
user.InvitedAt = &now
113114
terr = errors.Wrap(tx.UpdateOnly(user, "confirmation_token", "confirmation_sent_at", "invited_at"), "Database error updating user for invite")
114-
case "signup":
115+
case signupVerification:
115116
if user != nil {
116117
if user.IsConfirmed() {
117118
return unprocessableEntityError(DuplicateEmailMsg)
@@ -141,6 +142,28 @@ func (a *API) GenerateLink(w http.ResponseWriter, r *http.Request) error {
141142
user.ConfirmationToken = hashedToken
142143
user.ConfirmationSentAt = &now
143144
terr = errors.Wrap(tx.UpdateOnly(user, "confirmation_token", "confirmation_sent_at"), "Database error updating user for confirmation")
145+
case "email_change_current", "email_change_new":
146+
if !config.Mailer.SecureEmailChangeEnabled && params.Type == "email_change_current" {
147+
return unprocessableEntityError("Enable secure email change to generate link for current email")
148+
}
149+
if terr := a.validateEmail(ctx, params.NewEmail); terr != nil {
150+
return unprocessableEntityError("The new email address provided is invalid")
151+
}
152+
if exists, terr := models.IsDuplicatedEmail(tx, instanceID, params.NewEmail, user.Aud); terr != nil {
153+
return internalServerError("Database error checking email").WithInternalError(terr)
154+
} else if exists {
155+
return unprocessableEntityError(DuplicateEmailMsg)
156+
}
157+
now := time.Now()
158+
user.EmailChangeSentAt = &now
159+
user.EmailChange = params.NewEmail
160+
user.EmailChangeConfirmStatus = zeroConfirmation
161+
if params.Type == "email_change_current" {
162+
user.EmailChangeTokenCurrent = hashedToken
163+
} else if params.Type == "email_change_new" {
164+
user.EmailChangeTokenNew = fmt.Sprintf("%x", sha256.Sum224([]byte(params.NewEmail+otp)))
165+
}
166+
terr = errors.Wrap(tx.UpdateOnly(user, "email_change_token_current", "email_change_token_new", "email_change", "email_change_sent_at", "email_change_confirm_status"), "Database error updating user for email change")
144167
default:
145168
return badRequestError("Invalid email action link type requested: %v", params.Type)
146169
}

mailer/template.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,16 @@ func (m TemplateMailer) GetEmailActionLink(user *models.User, actionType, referr
317317
url, err = getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Invite, "token="+user.ConfirmationToken+"&type=invite"+redirectParam)
318318
case "signup":
319319
url, err = getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.Confirmation, "token="+user.ConfirmationToken+"&type=signup"+redirectParam)
320+
case "email_change_current":
321+
url, err = getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.EmailChange, "token="+user.EmailChangeTokenCurrent+"&type=email_change"+redirectParam)
322+
case "email_change_new":
323+
url, err = getSiteURL(referrerURL, globalConfig.API.ExternalURL, m.Config.Mailer.URLPaths.EmailChange, "token="+user.EmailChangeTokenNew+"&type=email_change"+redirectParam)
320324
default:
321325
return "", fmt.Errorf("Invalid email action link type: %s", actionType)
322326
}
323-
324327
if err != nil {
325328
return "", err
326329
}
327-
328330
return url, nil
329331
}
330332

0 commit comments

Comments
 (0)