• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / gotrue / 8316299588

17 Mar 2024 02:55PM UTC coverage: 64.923% (-0.3%) from 65.241%
8316299588

Pull #1474

github

J0
fix: remove unneeded if check
Pull Request #1474: feat: add custom sms hook

87 of 197 new or added lines in 13 files covered. (44.16%)

72 existing lines in 3 files now uncovered.

8005 of 12330 relevant lines covered (64.92%)

59.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.85
/internal/api/reauthenticate.go
1
package api
2

3
import (
4
        "errors"
5
        "net/http"
6

7
        "github.com/supabase/auth/internal/api/sms_provider"
8
        "github.com/supabase/auth/internal/conf"
9
        "github.com/supabase/auth/internal/crypto"
10
        "github.com/supabase/auth/internal/models"
11
        "github.com/supabase/auth/internal/storage"
12
)
13

14
const InvalidNonceMessage = "Nonce has expired or is invalid"
15

16
// Reauthenticate sends a reauthentication otp to either the user's email or phone
17
func (a *API) Reauthenticate(w http.ResponseWriter, r *http.Request) error {
5✔
18
        ctx := r.Context()
5✔
19
        db := a.db.WithContext(ctx)
5✔
20
        config := a.config
5✔
21

5✔
22
        user := getUser(ctx)
5✔
23
        email, phone := user.GetEmail(), user.GetPhone()
5✔
24

5✔
25
        if email == "" && phone == "" {
5✔
26
                return badRequestError(ErrorCodeValidationFailed, "Reauthentication requires the user to have an email or a phone number")
×
27
        }
×
28

29
        if email != "" {
6✔
30
                if !user.IsConfirmed() {
1✔
31
                        return unprocessableEntityError(ErrorCodeEmailNotConfirmed, "Please verify your email first.")
×
32
                }
×
33
        } else if phone != "" {
8✔
34
                if !user.IsPhoneConfirmed() {
4✔
35
                        return unprocessableEntityError(ErrorCodePhoneNotConfirmed, "Please verify your phone first.")
×
36
                }
×
37
        }
38

39
        messageID := ""
5✔
40
        err := db.Transaction(func(tx *storage.Connection) error {
10✔
41
                if terr := models.NewAuditLogEntry(r, tx, user, models.UserReauthenticateAction, "", nil); terr != nil {
5✔
42
                        return terr
×
43
                }
×
44
                if email != "" {
6✔
45
                        mailer := a.Mailer(ctx)
1✔
46
                        return a.sendReauthenticationOtp(tx, user, mailer, config.SMTP.MaxFrequency, config.Mailer.OtpLength)
1✔
47
                } else if phone != "" {
9✔
48
                        smsProvider, terr := sms_provider.GetSmsProvider(*config)
4✔
49
                        if terr != nil {
8✔
50
                                return internalServerError("Failed to get SMS provider").WithInternalError(terr)
4✔
51
                        }
4✔
NEW
52
                        mID, err := a.sendPhoneConfirmation(r, tx, user, phone, phoneReauthenticationOtp, smsProvider, sms_provider.SMSProvider)
×
53
                        if err != nil {
×
54
                                return err
×
55
                        }
×
56

57
                        messageID = mID
×
58
                }
59
                return nil
×
60
        })
61
        if err != nil {
9✔
62
                if errors.Is(err, MaxFrequencyLimitError) {
4✔
63
                        reason := ErrorCodeOverEmailSendRateLimit
×
64
                        if phone != "" {
×
65
                                reason = ErrorCodeOverSMSSendRateLimit
×
66
                        }
×
67

68
                        return tooManyRequestsError(reason, "For security purposes, you can only request this once every 60 seconds")
×
69
                }
70
                return err
4✔
71
        }
72

73
        ret := map[string]any{}
1✔
74
        if messageID != "" {
1✔
75
                ret["message_id"] = messageID
×
76

×
77
        }
×
78

79
        return sendJSON(w, http.StatusOK, ret)
1✔
80
}
81

82
// verifyReauthentication checks if the nonce provided is valid
83
func (a *API) verifyReauthentication(nonce string, tx *storage.Connection, config *conf.GlobalConfiguration, user *models.User) error {
2✔
84
        if user.ReauthenticationToken == "" || user.ReauthenticationSentAt == nil {
3✔
85
                return unprocessableEntityError(ErrorCodeReauthenticationNotValid, InvalidNonceMessage)
1✔
86
        }
1✔
87
        var isValid bool
1✔
88
        if user.GetEmail() != "" {
2✔
89
                tokenHash := crypto.GenerateTokenHash(user.GetEmail(), nonce)
1✔
90
                isValid = isOtpValid(tokenHash, user.ReauthenticationToken, user.ReauthenticationSentAt, config.Mailer.OtpExp)
1✔
91
        } else if user.GetPhone() != "" {
1✔
92
                if config.Sms.IsTwilioVerifyProvider() {
×
93
                        smsProvider, _ := sms_provider.GetSmsProvider(*config)
×
94
                        if err := smsProvider.(*sms_provider.TwilioVerifyProvider).VerifyOTP(string(user.Phone), nonce); err != nil {
×
95
                                return forbiddenError(ErrorCodeOTPExpired, "Token has expired or is invalid").WithInternalError(err)
×
96
                        }
×
97
                        return nil
×
98
                } else {
×
99
                        tokenHash := crypto.GenerateTokenHash(user.GetPhone(), nonce)
×
100
                        isValid = isOtpValid(tokenHash, user.ReauthenticationToken, user.ReauthenticationSentAt, config.Sms.OtpExp)
×
101
                }
×
102
        } else {
×
103
                return unprocessableEntityError(ErrorCodeReauthenticationNotValid, "Reauthentication requires an email or a phone number")
×
104
        }
×
105
        if !isValid {
1✔
106
                return unprocessableEntityError(ErrorCodeReauthenticationNotValid, InvalidNonceMessage)
×
107
        }
×
108
        if err := user.ConfirmReauthentication(tx); err != nil {
1✔
109
                return internalServerError("Error during reauthentication").WithInternalError(err)
×
110
        }
×
111
        return nil
1✔
112
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc