Send an email if you sign up with an existing email

This commit is contained in:
Ben Visness 2023-07-19 17:36:00 -05:00
parent 7b2d016fe2
commit 368e657a79
5 changed files with 88 additions and 16 deletions

View File

@ -48,7 +48,47 @@ func SendRegistrationEmail(
perf.EndBlock() perf.EndBlock()
perf.StartBlock("EMAIL", "Sending email") perf.StartBlock("EMAIL", "Sending email")
err = sendMail(toAddress, toName, "[handmade.network] Registration confirmation", contents) err = sendMail(toAddress, toName, "[Handmade Network] Registration confirmation", contents)
if err != nil {
return oops.New(err, "Failed to send email")
}
perf.EndBlock()
perf.EndBlock()
return nil
}
type ExistingAccountEmailData struct {
Name string
Username string
HomepageUrl string
LoginUrl string
}
func SendExistingAccountEmail(
toAddress string,
toName string,
username string,
destination string,
perf *perf.RequestPerf,
) error {
perf.StartBlock("EMAIL", "Existing account email")
perf.StartBlock("EMAIL", "Rendering template")
contents, err := renderTemplate("email_account_existing.html", ExistingAccountEmailData{
Name: toName,
Username: username,
HomepageUrl: hmnurl.BuildHomepage(),
LoginUrl: hmnurl.BuildLoginPage(destination),
})
if err != nil {
return err
}
perf.EndBlock()
perf.StartBlock("EMAIL", "Sending email")
err = sendMail(toAddress, toName, "[Handmade Network] You already have an account!", contents)
if err != nil { if err != nil {
return oops.New(err, "Failed to send email") return oops.New(err, "Failed to send email")
} }
@ -80,7 +120,7 @@ func SendPasswordReset(toAddress string, toName string, username string, resetTo
perf.EndBlock() perf.EndBlock()
perf.StartBlock("EMAIL", "Sending email") perf.StartBlock("EMAIL", "Sending email")
err = sendMail(toAddress, toName, "[handmade.network] Your password reset request", contents) err = sendMail(toAddress, toName, "[Handmade Network] Your password reset request", contents)
if err != nil { if err != nil {
return oops.New(err, "Failed to send email") return oops.New(err, "Failed to send email")
} }
@ -118,7 +158,7 @@ func SendTimeMachineEmail(profileUrl, username, userEmail, discordUsername strin
return err return err
} }
err = sendMail("team@handmade.network", "HMN Team", "[time machine] New submission", contents) err = sendMail("team@handmade.network", "HMN Team", "[Time Machine] New submission", contents)
if err != nil { if err != nil {
return oops.New(err, "Failed to send email") return oops.New(err, "Failed to send email")
} }

View File

@ -165,7 +165,11 @@ var RegexLoginPage = regexp.MustCompile("^/login$")
func BuildLoginPage(redirectTo string) string { func BuildLoginPage(redirectTo string) string {
defer CatchPanic() defer CatchPanic()
return Url("/login", []Q{{Name: "redirect", Value: redirectTo}}) var q []Q
if redirectTo != "" {
q = append(q, Q{Name: "redirect", Value: redirectTo})
}
return Url("/login", q)
} }
var RegexLoginWithDiscord = regexp.MustCompile("^/login-with-discord$") var RegexLoginWithDiscord = regexp.MustCompile("^/login-with-discord$")

View File

@ -0,0 +1,16 @@
<p>
Hello {{ .Name }} - you already have a <a href="{{ .HomepageUrl }}">Handmade Network</a> account. Welcome back!
</p>
<p>
Your username is <b>{{ .Username }}</b>. To sign in, please visit the following link and try signing in with your existing username:
</p>
<p>
<a href="{{ .LoginUrl }}">{{ .LoginUrl }}</a>
</p>
<p>Thanks,<br />
The Handmade Network staff.</p>
<hr />
<p style="font-size:small; -webkit-text-size-adjust:none; color: #666">
You are receiving this email because someone tried creating a new account with your email address at <a href="{{ .HomepageUrl }}">handmade.network</a>. If that wasn't you, kindly ignore this email.
</p>

View File

@ -5,7 +5,7 @@
To complete the registration process, please use the following link: To complete the registration process, please use the following link:
</p> </p>
<p> <p>
<a href="{{ .CompleteRegistrationUrl }}">{{ .CompleteRegistrationUrl }}</a>. <a href="{{ .CompleteRegistrationUrl }}">{{ .CompleteRegistrationUrl }}</a>
</p> </p>
<p>Thanks,<br /> <p>Thanks,<br />
The Handmade Network staff.</p> The Handmade Network staff.</p>

View File

@ -232,26 +232,38 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
return c.RejectRequest(fmt.Sprintf("Username (%s) already exists.", username)) return c.RejectRequest(fmt.Sprintf("Username (%s) already exists.", username))
} }
emailAlreadyExists := true existingUser, err := db.QueryOne[models.User](c, c.Conn,
_, err = db.QueryOneScalar[int](c, c.Conn,
` `
SELECT id SELECT $columns
FROM hmn_user FROM hmn_user
WHERE LOWER(email) = LOWER($1) WHERE LOWER(email) = LOWER($1)
`, `,
emailAddress, emailAddress,
) )
if err != nil { if errors.Is(err, db.NotFound) {
if errors.Is(err, db.NotFound) { // this is fine
emailAlreadyExists = false } else if err != nil {
} else { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user"))
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user"))
}
} }
c.Perf.EndBlock() c.Perf.EndBlock()
if emailAlreadyExists { if existingUser != nil {
// NOTE(asaf): Silent rejection so we don't allow attackers to harvest emails. // Render the page as if it was a successful new registration, but
// instead send an email to the duplicate email address containing
// their actual username. Spammers won't be able to harvest emails, but
// normal users will be able to find and access their old accounts.
err := email.SendExistingAccountEmail(
existingUser.Email,
existingUser.BestName(),
existingUser.Username,
destination,
c.Perf,
)
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send existing account email"))
}
return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther) return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther)
} }