Repository: supabase/gotrue
Pull request #1195 (2023-07-22 10:52:28)

refactor: replace fmt.Sprintf with generateTokenHash
## What kind of change does this PR introduce?

see title -  relevant command:
```
#!/bin/bash

# Function to apply sed command to files in a directory
replace_in_files() {
  # The sed command with the pattern for replacement
  sed -E -i '' 's/fmt\.Sprintf\("%x", sha256\.Sum224\(\[\]byte\(([^+]+)\+([^)]+)\)\)\)/crypto.GenerateTokenHash(\1, \2)/g' "$1"
}

# Check if a directory is provided as an argument
if [ -z "$1" ]; then
  echo "Please provide the directory path as an argument."
  exit 1
fi

# Check if the directory exists
if [ ! -d "$1" ]; then
  echo "Directory not found: $1"
  exit 1
fi

# Loop through all files in the directory and apply the sed command
for file in "$1"/*; do
  if [ -f "$file" ]; then
    replace_in_files "$file"
  fi
done
```




Patch:
diff --git a/internal/api/mail.go b/internal/api/mail.go
index f415baf0..9640212e 100644
--- a/internal/api/mail.go
+++ b/internal/api/mail.go
@@ -1,9 +1,7 @@
 package api
 
 import (
-	"crypto/sha256"
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"net/url"
 	"strings"
@@ -94,7 +92,7 @@ func (a *API) GenerateLink(w http.ResponseWriter, r *http.Request) error {
 	if err != nil {
 		return err
 	}
-	hashedToken := fmt.Sprintf("%x", sha256.Sum224([]byte(params.Email+otp)))
+	hashedToken := crypto.GenerateTokenHash(params.Email, otp)
 	err = db.Transaction(func(tx *storage.Connection) error {
 		var terr error
 		switch params.Type {
@@ -198,7 +196,7 @@ func (a *API) GenerateLink(w http.ResponseWriter, r *http.Request) error {
 			if params.Type == "email_change_current" {
 				user.EmailChangeTokenCurrent = hashedToken
 			} else if params.Type == "email_change_new" {
-				user.EmailChangeTokenNew = fmt.Sprintf("%x", sha256.Sum224([]byte(params.NewEmail+otp)))
+				user.EmailChangeTokenNew = crypto.GenerateTokenHash(params.NewEmail, otp)
 			}
 			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")
 		default:
@@ -243,7 +241,7 @@ func sendConfirmation(tx *storage.Connection, u *models.User, mailer mailer.Mail
 	if err != nil {
 		return err
 	}
-	token := fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+otp)))
+	token := crypto.GenerateTokenHash(u.GetEmail(), otp)
 	u.ConfirmationToken = addFlowPrefixToToken(token, flowType)
 	now := time.Now()
 	if err := mailer.ConfirmationMail(u, otp, referrerURL, externalURL); err != nil {
@@ -261,7 +259,7 @@ func sendInvite(tx *storage.Connection, u *models.User, mailer mailer.Mailer, re
 	if err != nil {
 		return err
 	}
-	u.ConfirmationToken = fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+otp)))
+	u.ConfirmationToken = crypto.GenerateTokenHash(u.GetEmail(), otp)
 	now := time.Now()
 	if err := mailer.InviteMail(u, otp, referrerURL, externalURL); err != nil {
 		u.ConfirmationToken = oldToken
@@ -283,7 +281,7 @@ func (a *API) sendPasswordRecovery(tx *storage.Connection, u *models.User, maile
 	if err != nil {
 		return err
 	}
-	token := fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+otp)))
+	token := crypto.GenerateTokenHash(u.GetEmail(), otp)
 	u.RecoveryToken = addFlowPrefixToToken(token, flowType)
 	now := time.Now()
 	if err := mailer.RecoveryMail(u, otp, referrerURL, externalURL); err != nil {
@@ -305,7 +303,7 @@ func (a *API) sendReauthenticationOtp(tx *storage.Connection, u *models.User, ma
 	if err != nil {
 		return err
 	}
-	u.ReauthenticationToken = fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+otp)))
+	u.ReauthenticationToken = crypto.GenerateTokenHash(u.GetEmail(), otp)
 	if err != nil {
 		return err
 	}
@@ -330,7 +328,7 @@ func (a *API) sendMagicLink(tx *storage.Connection, u *models.User, mailer maile
 	if err != nil {
 		return err
 	}
-	token := fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+otp)))
+	token := crypto.GenerateTokenHash(u.GetEmail(), otp)
 	u.RecoveryToken = addFlowPrefixToToken(token, flowType)
 
 	now := time.Now()
@@ -353,7 +351,7 @@ func (a *API) sendEmailChange(tx *storage.Connection, config *conf.GlobalConfigu
 		return err
 	}
 	u.EmailChange = email
-	token := fmt.Sprintf("%x", sha256.Sum224([]byte(u.EmailChange+otpNew)))
+	token := crypto.GenerateTokenHash(u.EmailChange, otpNew)
 	u.EmailChangeTokenNew = addFlowPrefixToToken(token, flowType)
 
 	otpCurrent := ""
@@ -362,7 +360,7 @@ func (a *API) sendEmailChange(tx *storage.Connection, config *conf.GlobalConfigu
 		if err != nil {
 			return err
 		}
-		currentToken := fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+otpCurrent)))
+		currentToken := crypto.GenerateTokenHash(u.GetEmail(), otpCurrent)
 		u.EmailChangeTokenCurrent = addFlowPrefixToToken(currentToken, flowType)
 		if err != nil {
 			return err
diff --git a/internal/api/phone.go b/internal/api/phone.go
index 70109333..a9ffe1ff 100644
--- a/internal/api/phone.go
+++ b/internal/api/phone.go
@@ -2,7 +2,6 @@ package api
 
 import (
 	"context"
-	"crypto/sha256"
 	"fmt"
 	"regexp"
 	"strings"
@@ -76,7 +75,7 @@ func (a *API) sendPhoneConfirmation(ctx context.Context, tx *storage.Connection,
 	if err != nil {
 		return "", internalServerError("error generating otp").WithInternalError(err)
 	}
-	*token = fmt.Sprintf("%x", sha256.Sum224([]byte(phone+otp)))
+	*token = crypto.GenerateTokenHash(phone, otp)
 
 	var message string
 	if config.Sms.Template == "" {
diff --git a/internal/api/reauthenticate.go b/internal/api/reauthenticate.go
index 508ece41..2ac61f2c 100644
--- a/internal/api/reauthenticate.go
+++ b/internal/api/reauthenticate.go
@@ -1,13 +1,12 @@
 package api
 
 import (
-	"crypto/sha256"
 	"errors"
-	"fmt"
 	"net/http"
 
 	"github.com/supabase/gotrue/internal/api/sms_provider"
 	"github.com/supabase/gotrue/internal/conf"
+	"github.com/supabase/gotrue/internal/crypto"
 	"github.com/supabase/gotrue/internal/models"
 	"github.com/supabase/gotrue/internal/storage"
 )
@@ -82,7 +81,7 @@ func (a *API) verifyReauthentication(nonce string, tx *storage.Connection, confi
 	}
 	var isValid bool
 	if user.GetEmail() != "" {
-		tokenHash := fmt.Sprintf("%x", sha256.Sum224([]byte(user.GetEmail()+nonce)))
+		tokenHash := crypto.GenerateTokenHash(user.GetEmail(), nonce)
 		isValid = isOtpValid(tokenHash, user.ReauthenticationToken, user.ReauthenticationSentAt, config.Mailer.OtpExp)
 	} else if user.GetPhone() != "" {
 		if config.Sms.IsTwilioVerifyProvider() {
@@ -92,7 +91,7 @@ func (a *API) verifyReauthentication(nonce string, tx *storage.Connection, confi
 			}
 			return nil
 		} else {
-			tokenHash := fmt.Sprintf("%x", sha256.Sum224([]byte(user.GetPhone()+nonce)))
+			tokenHash := crypto.GenerateTokenHash(user.GetPhone(), nonce)
 			isValid = isOtpValid(tokenHash, user.ReauthenticationToken, user.ReauthenticationSentAt, config.Sms.OtpExp)
 		}
 	} else {
diff --git a/internal/api/verify.go b/internal/api/verify.go
index 23f849fc..1589225f 100644
--- a/internal/api/verify.go
+++ b/internal/api/verify.go
@@ -2,10 +2,8 @@ package api
 
 import (
 	"context"
-	"crypto/sha256"
 	"encoding/json"
 	"errors"
-	"fmt"
 	"net/http"
 	"net/url"
 	"strconv"
@@ -14,6 +12,7 @@ import (
 
 	"github.com/sethvargo/go-password/password"
 	"github.com/supabase/gotrue/internal/api/sms_provider"
+	"github.com/supabase/gotrue/internal/crypto"
 	"github.com/supabase/gotrue/internal/models"
 	"github.com/supabase/gotrue/internal/observability"
 	"github.com/supabase/gotrue/internal/storage"
@@ -77,13 +76,13 @@ func (p *VerifyParams) Validate(r *http.Request) error {
 				if err != nil {
 					return err
 				}
-				p.TokenHash = fmt.Sprintf("%x", sha256.Sum224([]byte(p.Phone+p.Token)))
+				p.TokenHash = crypto.GenerateTokenHash(p.Phone, p.Token)
 			} else if isEmailOtpVerification(p) {
 				p.Email, err = validateEmail(p.Email)
 				if err != nil {
 					return unprocessableEntityError("Invalid email format").WithInternalError(err)
 				}
-				p.TokenHash = fmt.Sprintf("%x", sha256.Sum224([]byte(p.Email+p.Token)))
+				p.TokenHash = crypto.GenerateTokenHash(p.Email, p.Token)
 			} else {
 				return badRequestError("Only an email address or phone number should be provided on verify")
 			}
diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go
index 70980c16..e8063ab1 100644
--- a/internal/crypto/crypto.go
+++ b/internal/crypto/crypto.go
@@ -2,6 +2,7 @@ package crypto
 
 import (
 	"crypto/rand"
+	"crypto/sha256"
 	"encoding/base64"
 	"fmt"
 	"io"
@@ -37,3 +38,6 @@ func GenerateOtp(digits int) (string, error) {
 	otp := fmt.Sprintf(expr, val.String())
 	return otp, nil
 }
+func GenerateTokenHash(emailOrPhone, otp string) string {
+	return fmt.Sprintf("%x", sha256.Sum224([]byte(emailOrPhone+otp)))
+}


Test patch:
diff --git a/internal/api/invite_test.go b/internal/api/invite_test.go
index e01906f7..ba0f0113 100644
--- a/internal/api/invite_test.go
+++ b/internal/api/invite_test.go
@@ -2,7 +2,6 @@ package api
 
 import (
 	"bytes"
-	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 	"net/http"
@@ -16,6 +15,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 	"github.com/supabase/gotrue/internal/conf"
+	"github.com/supabase/gotrue/internal/crypto"
 	"github.com/supabase/gotrue/internal/models"
 )
 
@@ -152,7 +152,7 @@ func (ts *InviteTestSuite) TestVerifyInvite() {
 			user.InvitedAt = &now
 			user.ConfirmationSentAt = &now
 			user.EncryptedPassword = ""
-			user.ConfirmationToken = fmt.Sprintf("%x", sha256.Sum224([]byte(c.email+c.requestBody["token"].(string))))
+			user.ConfirmationToken = crypto.GenerateTokenHash(c.email, c.requestBody["token"].(string))
 			require.NoError(ts.T(), err)
 			require.NoError(ts.T(), ts.API.db.Create(user))
 
diff --git a/internal/api/mail_test.go b/internal/api/mail_test.go
index 0f828f42..693f0f86 100644
--- a/internal/api/mail_test.go
+++ b/internal/api/mail_test.go
@@ -2,7 +2,6 @@ package api
 
 import (
 	"bytes"
-	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 	"net/http"
@@ -15,6 +14,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 	"github.com/supabase/gotrue/internal/conf"
+	"github.com/supabase/gotrue/internal/crypto"
 	"github.com/supabase/gotrue/internal/models"
 )
 
@@ -176,7 +176,7 @@ func (ts *MailTestSuite) TestGenerateLink() {
 			require.Equal(ts.T(), c.ExpectedResponse["redirect_to"], data["redirect_to"])
 
 			// check if hashed_token matches hash function of email and the raw otp
-			require.Equal(ts.T(), fmt.Sprintf("%x", sha256.Sum224([]byte(c.Body.Email+data["email_otp"].(string)))), data["hashed_token"])
+			require.Equal(ts.T(), crypto.GenerateTokenHash(c.Body.Email, data["email_otp"].(string)), data["hashed_token"])
 
 			// check if the host used in the email link matches the initial request host
 			u, err := url.ParseRequestURI(data["action_link"].(string))
diff --git a/internal/api/user_test.go b/internal/api/user_test.go
index 994b1b09..b554316c 100644
--- a/internal/api/user_test.go
+++ b/internal/api/user_test.go
@@ -2,7 +2,6 @@ package api
 
 import (
 	"bytes"
-	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 	"net/http"
@@ -14,6 +13,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 	"github.com/supabase/gotrue/internal/conf"
+	"github.com/supabase/gotrue/internal/crypto"
 	"github.com/supabase/gotrue/internal/models"
 )
 
@@ -340,7 +340,7 @@ func (ts *UserTestSuite) TestUserUpdatePasswordReauthentication() {
 	require.NotEmpty(ts.T(), u.ReauthenticationSentAt)
 
 	// update reauthentication token to a known token
-	u.ReauthenticationToken = fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456")))
+	u.ReauthenticationToken = crypto.GenerateTokenHash(u.GetEmail(), "123456")
 	require.NoError(ts.T(), ts.API.db.Update(u))
 
 	// update password with reauthentication token
diff --git a/internal/api/verify_test.go b/internal/api/verify_test.go
index 1ccea86c..c50c97b5 100644
--- a/internal/api/verify_test.go
+++ b/internal/api/verify_test.go
@@ -2,7 +2,6 @@ package api
 
 import (
 	"bytes"
-	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -16,6 +15,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 	"github.com/supabase/gotrue/internal/conf"
+	"github.com/supabase/gotrue/internal/crypto"
 	"github.com/supabase/gotrue/internal/models"
 )
 
@@ -787,13 +787,13 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":      smsVerification,
-				"tokenHash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetPhone()+"123456"))),
+				"tokenHash": crypto.GenerateTokenHash(u.GetPhone(), "123456"),
 				"token":     "123456",
 				"phone":     u.GetPhone(),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetPhone()+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.GetPhone(), "123456"),
 			},
 		},
 		{
@@ -801,13 +801,13 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":      signupVerification,
-				"tokenHash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456"))),
+				"tokenHash": crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 				"token":     "123456",
 				"email":     u.GetEmail(),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 		},
 		{
@@ -815,13 +815,13 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":      recoveryVerification,
-				"tokenHash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456"))),
+				"tokenHash": crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 				"token":     "123456",
 				"email":     u.GetEmail(),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 		},
 		{
@@ -829,13 +829,13 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":      emailOTPVerification,
-				"tokenHash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456"))),
+				"tokenHash": crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 				"token":     "123456",
 				"email":     u.GetEmail(),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.GetEmail()+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 		},
 		{
@@ -843,13 +843,13 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":      emailChangeVerification,
-				"tokenHash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.EmailChange+"123456"))),
+				"tokenHash": crypto.GenerateTokenHash(u.EmailChange, "123456"),
 				"token":     "123456",
 				"email":     u.EmailChange,
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.EmailChange+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.EmailChange, "123456"),
 			},
 		},
 		{
@@ -857,13 +857,13 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":      phoneChangeVerification,
-				"tokenHash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.PhoneChange+"123456"))),
+				"tokenHash": crypto.GenerateTokenHash(u.PhoneChange, "123456"),
 				"token":     "123456",
 				"phone":     u.PhoneChange,
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.PhoneChange+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.PhoneChange, "123456"),
 			},
 		},
 		{
@@ -871,11 +871,11 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":       signupVerification,
-				"token_hash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.Email+"123456"))),
+				"token_hash": crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.Email+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 		},
 		{
@@ -883,11 +883,11 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":       emailChangeVerification,
-				"token_hash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.EmailChange+"123456"))),
+				"token_hash": crypto.GenerateTokenHash(u.EmailChange, "123456"),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.EmailChange+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.EmailChange, "123456"),
 			},
 		},
 		{
@@ -895,11 +895,11 @@ func (ts *VerifyTestSuite) TestVerifyValidOtp() {
 			sentTime: time.Now(),
 			body: map[string]interface{}{
 				"type":       emailOTPVerification,
-				"token_hash": fmt.Sprintf("%x", sha256.Sum224([]byte(u.Email+"123456"))),
+				"token_hash": crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 			expected: expected{
 				code:      http.StatusOK,
-				tokenHash: fmt.Sprintf("%x", sha256.Sum224([]byte(u.Email+"123456"))),
+				tokenHash: crypto.GenerateTokenHash(u.GetEmail(), "123456"),
 			},
 		},
 	}