Skip to content

Commit

Permalink
feat: refactor one-time tokens for performance
Browse files Browse the repository at this point in the history
  • Loading branch information
hf committed Apr 28, 2024
1 parent 6ec08b3 commit 988959a
Show file tree
Hide file tree
Showing 8 changed files with 823 additions and 285 deletions.
65 changes: 63 additions & 2 deletions internal/api/mail.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package api

import (
"github.com/supabase/auth/internal/hooks"
mail "github.com/supabase/auth/internal/mailer"
"net/http"
"strings"
"time"

"github.com/supabase/auth/internal/hooks"
mail "github.com/supabase/auth/internal/mailer"

"github.com/badoux/checkmail"
"github.com/fatih/structs"
"github.com/pkg/errors"
Expand Down Expand Up @@ -123,6 +124,13 @@ func (a *API) adminGenerateLink(w http.ResponseWriter, r *http.Request) error {
terr = tx.UpdateOnly(user, "recovery_token", "recovery_sent_at")
if terr != nil {
terr = errors.Wrap(terr, "Database error updating user for recovery")
return terr
}

terr = models.CreateOneTimeToken(tx, user.ID, user.GetEmail(), user.RecoveryToken, models.RecoveryToken)
if terr != nil {
terr = errors.Wrap(terr, "Database error reating recovery token in admin")
return terr
}
case mail.InviteVerification:
if user != nil {
Expand Down Expand Up @@ -171,6 +179,10 @@ func (a *API) adminGenerateLink(w http.ResponseWriter, r *http.Request) error {
if terr != nil {
terr = errors.Wrap(terr, "Database error updating user for invite")

Check failure on line 180 in internal/api/mail.go

View workflow job for this annotation

GitHub Actions / test (1.21.x)

this value of terr is never used (SA4006)
}
terr = models.CreateOneTimeToken(tx, user.ID, user.GetEmail(), user.ConfirmationToken, models.ConfirmationToken)
if terr != nil {
terr = errors.Wrap(terr, "Database error reating confirmation token for invite in admin")
}
case mail.SignupVerification:
if user != nil {
if user.IsConfirmed() {
Expand Down Expand Up @@ -202,6 +214,12 @@ func (a *API) adminGenerateLink(w http.ResponseWriter, r *http.Request) error {
terr = tx.UpdateOnly(user, "confirmation_token", "confirmation_sent_at")
if terr != nil {
terr = errors.Wrap(terr, "Database error updating user for confirmation")
return terr
}
terr = models.CreateOneTimeToken(tx, user.ID, user.GetEmail(), user.ConfirmationToken, models.ConfirmationToken)
if terr != nil {
terr = errors.Wrap(terr, "Database error reating confirmation token for signup in admin")
return terr
}
case mail.EmailChangeCurrentVerification, mail.EmailChangeNewVerification:
if !config.Mailer.SecureEmailChangeEnabled && params.Type == "email_change_current" {
Expand Down Expand Up @@ -229,6 +247,14 @@ func (a *API) adminGenerateLink(w http.ResponseWriter, r *http.Request) error {
if terr != nil {
terr = errors.Wrap(terr, "Database error updating user for email change")

Check failure on line 248 in internal/api/mail.go

View workflow job for this annotation

GitHub Actions / test (1.21.x)

this value of terr is never used (SA4006)
}
terr = models.CreateOneTimeToken(tx, user.ID, user.GetEmail(), user.EmailChangeTokenCurrent, models.EmailChangeTokenCurrent)
if terr != nil {
terr = errors.Wrap(terr, "Database error creating email change token current in admin")

Check failure on line 252 in internal/api/mail.go

View workflow job for this annotation

GitHub Actions / test (1.21.x)

this value of terr is never used (SA4006)
}
terr = models.CreateOneTimeToken(tx, user.ID, user.EmailChange, user.EmailChangeTokenNew, models.EmailChangeTokenNew)
if terr != nil {
terr = errors.Wrap(terr, "Database error creating email change token new in admin")
}
default:
return badRequestError(ErrorCodeValidationFailed, "Invalid email action link type requested: %v", params.Type)
}
Expand Down Expand Up @@ -290,6 +316,11 @@ func (a *API) sendConfirmation(r *http.Request, tx *storage.Connection, u *model
return errors.Wrap(err, "Database error updating user for confirmation")
}

err = models.CreateOneTimeToken(tx, u.ID, u.GetEmail(), u.ConfirmationToken, models.ConfirmationToken)
if err != nil {
return errors.Wrap(err, "Database error creating confirmation token")
}

return nil
}

Expand Down Expand Up @@ -317,6 +348,11 @@ func (a *API) sendInvite(r *http.Request, tx *storage.Connection, u *models.User
return errors.Wrap(err, "Database error updating user for invite")
}

err = models.CreateOneTimeToken(tx, u.ID, u.GetEmail(), u.ConfirmationToken, models.ConfirmationToken)
if err != nil {
return errors.Wrap(err, "Database error creating confirmation token for invite")
}

return nil
}

Expand Down Expand Up @@ -349,6 +385,11 @@ func (a *API) sendPasswordRecovery(r *http.Request, tx *storage.Connection, u *m
return errors.Wrap(err, "Database error updating user for recovery")
}

err = models.CreateOneTimeToken(tx, u.ID, u.GetEmail(), u.RecoveryToken, models.RecoveryToken)
if err != nil {
return errors.Wrap(err, "Database error creating recovery token")
}

return nil
}

Expand Down Expand Up @@ -381,6 +422,11 @@ func (a *API) sendReauthenticationOtp(r *http.Request, tx *storage.Connection, u
return errors.Wrap(err, "Database error updating user for reauthentication")
}

err = models.CreateOneTimeToken(tx, u.ID, u.GetEmail(), u.ReauthenticationToken, models.ReauthenticationToken)
if err != nil {
return errors.Wrap(err, "Database error creating reauthentication token")
}

return nil
}

Expand Down Expand Up @@ -416,6 +462,11 @@ func (a *API) sendMagicLink(r *http.Request, tx *storage.Connection, u *models.U
return errors.Wrap(err, "Database error updating user for recovery")
}

err = models.CreateOneTimeToken(tx, u.ID, u.GetEmail(), u.RecoveryToken, models.RecoveryToken)
if err != nil {
return errors.Wrap(err, "Database error creating recovery token")
}

return nil
}

Expand Down Expand Up @@ -469,6 +520,16 @@ func (a *API) sendEmailChange(r *http.Request, tx *storage.Connection, u *models
return errors.Wrap(err, "Database error updating user for email change")
}

err = models.CreateOneTimeToken(tx, u.ID, u.GetEmail(), u.EmailChangeTokenCurrent, models.EmailChangeTokenCurrent)
if err != nil {
return errors.Wrap(err, "Database error creating email change token current")
}

err = models.CreateOneTimeToken(tx, u.ID, u.EmailChange, u.EmailChangeTokenNew, models.EmailChangeTokenNew)
if err != nil {
return errors.Wrap(err, "Database error creating email change token new")
}

return nil
}

Expand Down
23 changes: 21 additions & 2 deletions internal/api/phone.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (a *API) sendPhoneConfirmation(ctx context.Context, r *http.Request, tx *st
if err != nil {
return "", err
}

if config.Hook.SendSMS.Enabled {
input := hooks.SendSMSInput{
User: user,
Expand All @@ -108,7 +109,6 @@ func (a *API) sendPhoneConfirmation(ctx context.Context, r *http.Request, tx *st
return "", err
}
} else {

messageID, err = smsProvider.SendMessage(phone, message, channel, otp)
if err != nil {
return messageID, err
Expand All @@ -127,7 +127,26 @@ func (a *API) sendPhoneConfirmation(ctx context.Context, r *http.Request, tx *st
user.ReauthenticationSentAt = &now
}

return messageID, errors.Wrap(tx.UpdateOnly(user, includeFields...), "Database error updating user for confirmation")
if err := tx.UpdateOnly(user, includeFields...); err != nil {
return messageID, errors.Wrap(err, "Database error updating user for phone")
}

switch otpType {
case phoneConfirmationOtp:
if err := models.CreateOneTimeToken(tx, user.ID, user.GetPhone(), user.ConfirmationToken, models.ConfirmationToken); err != nil {
return messageID, errors.Wrap(err, "Database error creating confirmation token for phone")
}
case phoneChangeVerification:
if err := models.CreateOneTimeToken(tx, user.ID, user.PhoneChange, user.PhoneChangeToken, models.PhoneChangeToken); err != nil {
return messageID, errors.Wrap(err, "Database error creating phone change token")
}
case phoneReauthenticationOtp:
if err := models.CreateOneTimeToken(tx, user.ID, user.GetPhone(), user.ReauthenticationToken, models.ReauthenticationToken); err != nil {
return messageID, errors.Wrap(err, "Database error creating reauthentication token for phone")
}
}

return messageID, nil
}

func generateSMSFromTemplate(SMSTemplate *template.Template, otp string) (string, error) {
Expand Down
21 changes: 19 additions & 2 deletions internal/api/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,11 +479,28 @@ func (a *API) emailChangeVerify(r *http.Request, conn *storage.Connection, param
config := a.config
if config.Mailer.SecureEmailChangeEnabled && user.EmailChangeConfirmStatus == zeroConfirmation && user.GetEmail() != "" {
err := conn.Transaction(func(tx *storage.Connection) error {
currentOTT, terr := models.FindOneTimeToken(tx, params.TokenHash, models.EmailChangeTokenCurrent)
if terr != nil && !models.IsNotFoundError(terr) {
return terr
}

newOTT, terr := models.FindOneTimeToken(tx, params.TokenHash, models.EmailChangeTokenNew)
if terr != nil && !models.IsNotFoundError(terr) {
return terr
}

user.EmailChangeConfirmStatus = singleConfirmation
if params.Token == user.EmailChangeTokenCurrent || params.TokenHash == user.EmailChangeTokenCurrent {

if params.Token == user.EmailChangeTokenCurrent || params.TokenHash == user.EmailChangeTokenCurrent || (currentOTT != nil && params.TokenHash == currentOTT.TokenHash) {
user.EmailChangeTokenCurrent = ""
} else if params.Token == user.EmailChangeTokenNew || params.TokenHash == user.EmailChangeTokenNew {
if terr := models.ClearOneTimeTokenForUser(tx, user.ID, models.EmailChangeTokenCurrent); terr != nil {
return terr
}
} else if params.Token == user.EmailChangeTokenNew || params.TokenHash == user.EmailChangeTokenNew || (newOTT != nil && params.TokenHash == newOTT.TokenHash) {
user.EmailChangeTokenNew = ""
if terr := models.ClearOneTimeTokenForUser(tx, user.ID, models.EmailChangeTokenNew); terr != nil {
return terr
}
}
if terr := tx.UpdateOnly(user, "email_change_confirm_status", "email_change_token_current", "email_change_token_new"); terr != nil {
return terr
Expand Down
Loading

0 comments on commit 988959a

Please sign in to comment.