Skip to content

Custom EmailType #434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
trivialkettle opened this issue Mar 10, 2025 · 2 comments
Open

Custom EmailType #434

trivialkettle opened this issue Mar 10, 2025 · 2 comments

Comments

@trivialkettle
Copy link

Hi
my use case is to send an email to all registered users, my solution looks like this:

create SMTP Service

// override email delivery to set custom content
func notificationEmailOverrides(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface {
	(*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) {
		user := input.EmailVerification.User
		content := emaildelivery.EmailContent{
			Body:    notificationEmailBody,
			IsHtml:  true,
			Subject: notificationEmailSubject,
			ToEmail: user.Email,
		}
		return content, nil
	}

	return originalImplementation
}

// SMTP Service to send emails
var Email *emaildelivery.EmailDeliveryInterface

func foo() {
// ...
    // create SMTP service instance
    Email = emailverification.MakeSMTPService(emaildelivery.SMTPServiceConfig{
			Settings: smtpSettings,
			Override: notificationEmailOverrides,
		})
 // ...

    sendNotificationEmail(...)

// ...
}

func sendNotificationEmail(userId string, email string, tenantId string, userContext supertokens.UserContext) {
	// we abuse the email type EmailVerification here, because we need to set any type and there is no custom type yet
	input := emaildelivery.EmailType{EmailVerification: &emaildelivery.EmailVerificationType{
		User: emaildelivery.User{
			ID:    userId,
			Email: email,
		},
		EmailVerifyLink: "",
		TenantId:        tenantId,
	}}
	err := (*Email.SendEmail)(input, userContext)
	return err
}

As you can see in sendNotificationEmail i set EmailType to EmailVerification because it is not possible to specify a custom type.

Is there any better solution or is it possible to update EmailType like this:

type EmailType struct {
	EmailVerification *EmailVerificationType
	PasswordReset     *PasswordResetType
	PasswordlessLogin *PasswordlessLoginType
	Custom *CustomType
}

type CustomType struct {
	User       User
	Data       map[string]interface{}{} // or use pointer type
	TenantId string
}

Thanks

@DLzer
Copy link
Contributor

DLzer commented Apr 9, 2025

The use-case you're presenting here is a major smell. Why are you trying to extend/piggyback on an authentication based email system to send out generalized notifications?

I would highly recommend creating an interface for your SMTP provider and passing that to ST for authentication based emails and creating a separate system for sending out generalized batched email notifications.

@trivialkettle
Copy link
Author

trivialkettle commented Apr 10, 2025

Hi,
I see your point. On the otherhand, that would mean to copy paste and maintain the supertokens SMTP functions.

My usecase is: I have an invite only flow based on https://supertokens.com/blog/how-to-create-an-invite-only-auth-flow. Though I don't use email reset, but an customized invitation email. I cannot use passwordless, because the golang sdk does not support account linking. Probably I could abuse usermetadata to check if the user has changed the initial password and override the resetEmail function to customize the content.

Currently I do this:

func customEmailOverrides(originalImplementation emaildelivery.SMTPInterface) emaildelivery.SMTPInterface {
	// see https://supertokens.com/docs/platform-configuration/email-delivery/smtp-service/change-email-content
	(*originalImplementation.GetContent) = func(input emaildelivery.EmailType, userContext supertokens.UserContext) (emaildelivery.EmailContent, error) {
		// ...
	}

	return originalImplementation
}

func createSmtp(smtp *SmtpSettings) {
      var customSmtpService = &emaildelivery.TypeInput{}
      
      smtpSettings := emaildelivery.SMTPSettings{
      			Host: smtp.Host,
      			From: emaildelivery.SMTPFrom{
      				Name:  smtp.FromName,
      				Email: smtp.FromEmail,
      			},
      			Port:     smtp.Port,
      			Username: &smtp.User,
      			Password: smtp.Password,
      			Secure:   smtp.Secure,
      		}
      
      customSmtpService.Service = emailverification.MakeSMTPService(emaildelivery.SMTPServiceConfig{
      			Settings: smtpSettings,
      			Override: customEmailOverrides,
      		})
}
// ...

func sendEmail(userId string, email string, tenantId string, userContext supertokens.UserContext) error {
	// we abuse the email type EmailVerification here, because we need to set any type and there is no custom type yet
	input := emaildelivery.EmailType{EmailVerification: &emaildelivery.EmailVerificationType{
		User: emaildelivery.User{
			ID:    userId,
			Email: email,
		},
		EmailVerifyLink: "",
		TenantId:        tenantId,
	}}
	err := *customSmtpService.Service.SendEmail(input, userContext)
	return err
}

to send out generalized notifications?

Well there are also authentication related notifications, like your password was not changed for 60 days (wouldnot do this), or your JWT / API token is expiring, etc..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants