Skip to content

Commit

Permalink
feat: add custom email sender hook
Browse files Browse the repository at this point in the history
  • Loading branch information
J0 committed Mar 26, 2024
1 parent a07fbdc commit da7b539
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 9 deletions.
33 changes: 24 additions & 9 deletions internal/api/mail.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"github.com/supabase/auth/internal/hooks"
"net/http"
"strings"
"time"
Expand Down Expand Up @@ -260,7 +261,7 @@ func (a *API) adminGenerateLink(w http.ResponseWriter, r *http.Request) error {
return sendJSON(w, http.StatusOK, resp)
}

func (a *API) sendConfirmation(r *http.Request, tx *storage.Connection, u *models.User, flowType models.FlowType) error {
func (a *API) sendConfirmation(r *http.Request, tx *storage.Connection, user *models.User, flowType models.FlowType) error {
ctx := r.Context()
mailer := a.Mailer()
config := a.config
Expand All @@ -269,24 +270,38 @@ func (a *API) sendConfirmation(r *http.Request, tx *storage.Connection, u *model
referrerURL := utilities.GetReferrer(r, config)
externalURL := getExternalHost(ctx)
var err error
if err := validateSentWithinFrequencyLimit(u.ConfirmationSentAt, maxFrequency); err != nil {
if err := validateSentWithinFrequencyLimit(user.ConfirmationSentAt, maxFrequency); err != nil {
return err
}
oldToken := u.ConfirmationToken
oldToken := user.ConfirmationToken
otp, err := crypto.GenerateOtp(otpLength)
if err != nil {
// OTP generation must succeeed
panic(err)
}
token := crypto.GenerateTokenHash(u.GetEmail(), otp)
u.ConfirmationToken = addFlowPrefixToToken(token, flowType)
token := crypto.GenerateTokenHash(user.GetEmail(), otp)
user.ConfirmationToken = addFlowPrefixToToken(token, flowType)
now := time.Now()
if err := mailer.ConfirmationMail(r, u, otp, referrerURL, externalURL); err != nil {
u.ConfirmationToken = oldToken
if config.Hook.CustomEmailProvider.Enabled {
input := hooks.CustomEmailProviderInput{
UserID: user.ID,
Email: user.Email.String(),
OTP: otp,
}
output := hooks.CustomEmailProviderOutput{}
err := a.invokeHTTPHook(ctx, r, &input, &output, config.Hook.CustomEmailProvider.URI)
if err != nil {
return err
}
return nil
}

if err := mailer.ConfirmationMail(r, user, otp, referrerURL, externalURL); err != nil {
user.ConfirmationToken = oldToken
return errors.Wrap(err, "Error sending confirmation email")
}
u.ConfirmationSentAt = &now
err = tx.UpdateOnly(u, "confirmation_token", "confirmation_sent_at")
user.ConfirmationSentAt = &now
err = tx.UpdateOnly(user, "confirmation_token", "confirmation_sent_at")
if err != nil {
return errors.Wrap(err, "Database error updating user for confirmation")
}
Expand Down
1 change: 1 addition & 0 deletions internal/conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ type HookConfiguration struct {
PasswordVerificationAttempt ExtensibilityPointConfiguration `json:"password_verification_attempt" split_words:"true"`
CustomAccessToken ExtensibilityPointConfiguration `json:"custom_access_token" split_words:"true"`
CustomSMSProvider ExtensibilityPointConfiguration `json:"custom_sms_provider" split_words:"true"`
CustomEmailProvider ExtensibilityPointConfiguration `json:"custom_email_provider" split_words:"true"`
}

type HTTPHookSecrets []string
Expand Down
11 changes: 11 additions & 0 deletions internal/hooks/auth_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ type CustomSMSProviderOutput struct {
HookError AuthHookError `json:"error,omitempty"`
}

type CustomEmailProviderInput struct {
UserID uuid.UUID `json:"user_id"`
Email string `json:"email"`
OTP string `json:"otp"`
}

type CustomEmailProviderOutput struct {
Success bool `json:"success"`
HookError AuthHookError `json:"error,omitempty"`
}

func (mf *MFAVerificationAttemptOutput) IsError() bool {
return mf.HookError.Message != ""
}
Expand Down

0 comments on commit da7b539

Please sign in to comment.