From 81fce1a4714360b788b38167db5f1ee779d6661d Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Tue, 9 Aug 2022 01:43:37 +0530 Subject: [PATCH] feat: send email based on template --- dashboard/src/constants.ts | 42 ++++----- server/constants/verification_types.go | 4 + server/db/models/user.go | 8 ++ server/email/email.go | 91 +++++++++++++++---- ...ication_email.go => email_verification.go} | 38 +------- server/email/forgot_password_email.go | 46 ++-------- server/email/invite_email.go | 42 ++------- server/email/otp.go | 42 ++------- server/email/utils.go | 1 + server/resolvers/forgot_password.go | 10 +- server/resolvers/invite_members.go | 11 ++- server/resolvers/login.go | 7 +- server/resolvers/magic_link_login.go | 8 +- server/resolvers/resend_otp.go | 7 +- server/resolvers/resend_verify_email.go | 13 ++- server/resolvers/signup.go | 9 +- server/resolvers/update_profile.go | 8 +- server/resolvers/update_user.go | 8 +- server/utils/common.go | 46 ++++++++++ server/validators/email_template.go | 2 +- 20 files changed, 239 insertions(+), 204 deletions(-) rename server/email/{verification_email.go => email_verification.go} (85%) create mode 100644 server/email/utils.go diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts index 79b6c26..b2e7152 100644 --- a/dashboard/src/constants.ts +++ b/dashboard/src/constants.ts @@ -206,27 +206,27 @@ export enum webhookVerifiedStatus { } export const emailTemplateVariables = { - 'user.id': '{user.id}}', - 'user.email': '{user.email}}', - 'user.given_name': '{user.given_name}}', - 'user.family_name': '{user.family_name}}', - 'user.signup_methods': '{user.signup_methods}}', - 'user.email_verified': '{user.email_verified}}', - 'user.picture': '{user.picture}}', - 'user.roles': '{user.roles}}', - 'user.middle_name': '{user.middle_name}}', - 'user.nickname': '{user.nickname}}', - 'user.preferred_username': '{user.preferred_username}}', - 'user.gender': '{user.gender}}', - 'user.birthdate': '{user.birthdate}}', - 'user.phone_number': '{user.phone_number}}', - 'user.phone_number_verified': '{user.phone_number_verified}}', - 'user.created_at': '{user.created_at}}', - 'user.updated_at': '{user.updated_at}}', - 'organization.name': '{organization.name}}', - 'organization.logo': '{organization.logo}}', - verification_url: '{verification_url}}', - otp: '{otp}}', + 'user.id': '{.user.id}}', + 'user.email': '{.user.email}}', + 'user.given_name': '{.user.given_name}}', + 'user.family_name': '{.user.family_name}}', + 'user.signup_methods': '{.user.signup_methods}}', + 'user.email_verified': '{.user.email_verified}}', + 'user.picture': '{.user.picture}}', + 'user.roles': '{.user.roles}}', + 'user.middle_name': '{.user.middle_name}}', + 'user.nickname': '{.user.nickname}}', + 'user.preferred_username': '{.user.preferred_username}}', + 'user.gender': '{.user.gender}}', + 'user.birthdate': '{.user.birthdate}}', + 'user.phone_number': '{.user.phone_number}}', + 'user.phone_number_verified': '{.user.phone_number_verified}}', + 'user.created_at': '{.user.created_at}}', + 'user.updated_at': '{.user.updated_at}}', + 'organization.name': '{.organization.name}}', + 'organization.logo': '{.organization.logo}}', + verification_url: '{.verification_url}}', + otp: '{.otp}}', }; export const webhookPayloadExample: string = `{ diff --git a/server/constants/verification_types.go b/server/constants/verification_types.go index de64dbb..0a83df8 100644 --- a/server/constants/verification_types.go +++ b/server/constants/verification_types.go @@ -9,4 +9,8 @@ const ( VerificationTypeUpdateEmail = "update_email" // VerificationTypeForgotPassword is the forgot_password verification type VerificationTypeForgotPassword = "forgot_password" + // VerificationTypeInviteMember is the invite_member verification type + VerificationTypeInviteMember = "invite_member" + // VerificationTypeOTP is the otp verification type + VerificationTypeOTP = "otp" ) diff --git a/server/db/models/user.go b/server/db/models/user.go index bc5bc04..f8a054d 100644 --- a/server/db/models/user.go +++ b/server/db/models/user.go @@ -1,6 +1,7 @@ package models import ( + "encoding/json" "strings" "github.com/authorizerdev/authorizer/server/graph/model" @@ -64,3 +65,10 @@ func (user *User) AsAPIUser() *model.User { UpdatedAt: refs.NewInt64Ref(user.UpdatedAt), } } + +func (user *User) ToMap() map[string]interface{} { + res := map[string]interface{}{} + data, _ := json.Marshal(user) // Convert to a json string + json.Unmarshal(data, &res) // Convert to a map + return res +} diff --git a/server/email/email.go b/server/email/email.go index 99b2c03..730ae56 100644 --- a/server/email/email.go +++ b/server/email/email.go @@ -2,8 +2,8 @@ package email import ( "bytes" + "context" "crypto/tls" - "encoding/json" "strconv" "text/template" @@ -11,27 +11,75 @@ import ( gomail "gopkg.in/mail.v2" "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/memorystore" ) -// addEmailTemplate is used to add html template in email body -func addEmailTemplate(a string, b map[string]interface{}, templateName string) string { - tmpl, err := template.New(templateName).Parse(a) - if err != nil { - output, _ := json.Marshal(b) - return string(output) +func getDefaultTemplate(event string) *model.EmailTemplate { + switch event { + case constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeMagicLinkLogin: + return &model.EmailTemplate{ + Subject: emailVerificationSubject, + Template: emailVerificationTemplate, + } + case constants.VerificationTypeForgotPassword: + return &model.EmailTemplate{ + Subject: forgotPasswordSubject, + Template: forgotPasswordTemplate, + } + case constants.VerificationTypeInviteMember: + return &model.EmailTemplate{ + Subject: inviteEmailSubject, + Template: inviteEmailTemplate, + } + case constants.VerificationTypeOTP: + return &model.EmailTemplate{ + Subject: otpEmailSubject, + Template: otpEmailTemplate, + } + default: + return nil } - buf := &bytes.Buffer{} - err = tmpl.Execute(buf, b) - if err != nil { - panic(err) - } - s := buf.String() - return s } -// SendMail function to send mail -func SendMail(to []string, Subject, bodyMessage string) error { +func getEmailTemplate(event string, data map[string]interface{}) (*model.EmailTemplate, error) { + ctx := context.Background() + tmp, err := db.Provider.GetEmailTemplateByEventName(ctx, event) + if err != nil || tmp == nil { + tmp = getDefaultTemplate(event) + } + + templ, err := template.New(event + "_template.tmpl").Parse(tmp.Template) + if err != nil { + return nil, err + } + buf := &bytes.Buffer{} + err = templ.Execute(buf, data) + if err != nil { + return nil, err + } + templateString := buf.String() + + subject, err := template.New(event + "_subject.tmpl").Parse(tmp.Subject) + if err != nil { + return nil, err + } + buf = &bytes.Buffer{} + err = subject.Execute(buf, data) + if err != nil { + return nil, err + } + subjectString := buf.String() + + return &model.EmailTemplate{ + Template: templateString, + Subject: subjectString, + }, nil +} + +// SendEmail function to send mail +func SendEmail(to []string, event string, data map[string]interface{}) error { // dont trigger email sending in case of test envKey, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEnv) if err != nil { @@ -40,6 +88,13 @@ func SendMail(to []string, Subject, bodyMessage string) error { if envKey == constants.TestEnv { return nil } + + tmp, err := getEmailTemplate(event, data) + if err != nil { + log.Errorf("Failed to get event template: ", err) + return err + } + m := gomail.NewMessage() senderEmail, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySenderEmail) if err != nil { @@ -79,8 +134,8 @@ func SendMail(to []string, Subject, bodyMessage string) error { m.SetHeader("From", senderEmail) m.SetHeader("To", to...) - m.SetHeader("Subject", Subject) - m.SetBody("text/html", bodyMessage) + m.SetHeader("Subject", tmp.Subject) + m.SetBody("text/html", tmp.Template) port, _ := strconv.Atoi(smtpPort) d := gomail.NewDialer(smtpHost, port, smtpUsername, smtpPassword) if !isProd { diff --git a/server/email/verification_email.go b/server/email/email_verification.go similarity index 85% rename from server/email/verification_email.go rename to server/email/email_verification.go index dded5ef..51a99bc 100644 --- a/server/email/verification_email.go +++ b/server/email/email_verification.go @@ -1,19 +1,8 @@ package email -import ( - log "github.com/sirupsen/logrus" - - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/memorystore" -) - -// SendVerificationMail to send verification email -func SendVerificationMail(toEmail, token, hostname string) error { - // The receiver needs to be in slice as the receive supports multiple receiver - Receiver := []string{toEmail} - - Subject := "Please verify your email" - message := ` +const ( + emailVerificationSubject = "Please verify your email" + emailVerificationTemplate = ` @@ -98,23 +87,4 @@ func SendVerificationMail(toEmail, token, hostname string) error { ` - data := make(map[string]interface{}, 3) - var err error - data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo) - if err != nil { - return err - } - data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName) - if err != nil { - return err - } - data["verification_url"] = hostname + "/verify_email?token=" + token - message = addEmailTemplate(message, data, "verify_email.tmpl") - // bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) - - err = SendMail(Receiver, Subject, message) - if err != nil { - log.Warn("error sending email: ", err) - } - return err -} +) diff --git a/server/email/forgot_password_email.go b/server/email/forgot_password_email.go index aabd6a9..678f83c 100644 --- a/server/email/forgot_password_email.go +++ b/server/email/forgot_password_email.go @@ -1,28 +1,8 @@ package email -import ( - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/memorystore" -) - -// SendForgotPasswordMail to send forgot password email -func SendForgotPasswordMail(toEmail, token, hostname string) error { - resetPasswordUrl, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL) - if err != nil { - return err - } - if resetPasswordUrl == "" { - if err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password"); err != nil { - return err - } - } - - // The receiver needs to be in slice as the receive supports multiple receiver - Receiver := []string{toEmail} - - Subject := "Reset Password" - - message := ` +const ( + forgotPasswordSubject = "Reset Password" + forgotPasswordTemplate = ` @@ -73,13 +53,13 @@ func SendForgotPasswordMail(toEmail, token, hostname string) error { - + @@ -106,18 +86,4 @@ func SendForgotPasswordMail(toEmail, token, hostname string) error { ` - - data := make(map[string]interface{}, 3) - data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo) - if err != nil { - return err - } - data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName) - if err != nil { - return err - } - data["verification_url"] = resetPasswordUrl + "?token=" + token - message = addEmailTemplate(message, data, "reset_password_email.tmpl") - - return SendMail(Receiver, Subject, message) -} +) diff --git a/server/email/invite_email.go b/server/email/invite_email.go index 4faaf3f..bec33a6 100644 --- a/server/email/invite_email.go +++ b/server/email/invite_email.go @@ -1,19 +1,8 @@ package email -import ( - log "github.com/sirupsen/logrus" - - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/memorystore" -) - -// InviteEmail to send invite email -func InviteEmail(toEmail, token, verificationURL, redirectURI string) error { - // The receiver needs to be in slice as the receive supports multiple receiver - Receiver := []string{toEmail} - - Subject := "Please accept the invitation" - message := ` +const ( + inviteEmailSubject = "Please accept the invitation" + inviteEmailTemplate = ` @@ -64,13 +53,13 @@ func InviteEmail(toEmail, token, verificationURL, redirectURI string) error {
iconicon

Hey there 👋

-

We have received a request to reset password for email: {{.org_name}}. If this is correct, please reset the password clicking the button below.


+

We have received a request to reset password for email: {{.organization.name}}. If this is correct, please reset the password clicking the button below.


Reset Password
- + @@ -98,23 +87,4 @@ func InviteEmail(toEmail, token, verificationURL, redirectURI string) error { ` - data := make(map[string]interface{}, 3) - var err error - data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo) - if err != nil { - return err - } - data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName) - if err != nil { - return err - } - data["verification_url"] = verificationURL + "?token=" + token + "&redirect_uri=" + redirectURI - message = addEmailTemplate(message, data, "invite_email.tmpl") - // bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) - - err = SendMail(Receiver, Subject, message) - if err != nil { - log.Warn("error sending email: ", err) - } - return err -} +) diff --git a/server/email/otp.go b/server/email/otp.go index 181a1e0..d3bf7c0 100644 --- a/server/email/otp.go +++ b/server/email/otp.go @@ -1,19 +1,8 @@ package email -import ( - log "github.com/sirupsen/logrus" - - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/memorystore" -) - -// SendOtpMail to send otp email -func SendOtpMail(toEmail, otp string) error { - // The receiver needs to be in slice as the receive supports multiple receiver - Receiver := []string{toEmail} - - Subject := "OTP for your multi factor authentication" - message := ` +const ( + otpEmailSubject = "OTP for your multi factor authentication" + otpEmailTemplate = ` @@ -64,13 +53,13 @@ func SendOtpMail(toEmail, otp string) error {
iconicon

Hi there 👋

-

Join us! You are invited to sign-up for {{.org_name}}. Please accept the invitation by clicking the button below.


+

Join us! You are invited to sign-up for {{.organization.name}}. Please accept the invitation by clicking the button below.


Get Started
- + @@ -96,23 +85,4 @@ func SendOtpMail(toEmail, otp string) error { ` - data := make(map[string]interface{}, 3) - var err error - data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo) - if err != nil { - return err - } - data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName) - if err != nil { - return err - } - data["otp"] = otp - message = addEmailTemplate(message, data, "otp.tmpl") - // bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) - - err = SendMail(Receiver, Subject, message) - if err != nil { - log.Warn("error sending email: ", err) - } - return err -} +) diff --git a/server/email/utils.go b/server/email/utils.go new file mode 100644 index 0000000..8774a6e --- /dev/null +++ b/server/email/utils.go @@ -0,0 +1 @@ +package email diff --git a/server/resolvers/forgot_password.go b/server/resolvers/forgot_password.go index a8672f3..77ff265 100644 --- a/server/resolvers/forgot_password.go +++ b/server/resolvers/forgot_password.go @@ -49,7 +49,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu log := log.WithFields(log.Fields{ "email": params.Email, }) - _, err = db.Provider.GetUserByEmail(ctx, params.Email) + user, err := db.Provider.GetUserByEmail(ctx, params.Email) if err != nil { log.Debug("User not found: ", err) return res, fmt.Errorf(`user with this email not found`) @@ -84,8 +84,12 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu return res, err } - // exec it as go routin so that we can reduce the api latency - go email.SendForgotPasswordMail(params.Email, verificationToken, hostname) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetForgotPasswordURL(verificationToken, hostname), + }) res = &model.Response{ Message: `Please check your inbox! We have sent a password reset link.`, diff --git a/server/resolvers/invite_members.go b/server/resolvers/invite_members.go index e05406e..c642740 100644 --- a/server/resolvers/invite_members.go +++ b/server/resolvers/invite_members.go @@ -115,7 +115,7 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput) return nil, err } - verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL) + verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeInviteMember, hostname, nonceHash, redirectURL) if err != nil { log.Debug("Failed to create verification token: ", err) } @@ -135,7 +135,7 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput) } else { // use basic authentication if that option is on user.SignupMethods = constants.AuthRecipeMethodBasicAuth - verificationRequest.Identifier = constants.VerificationTypeForgotPassword + verificationRequest.Identifier = constants.VerificationTypeInviteMember isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication) if err != nil { @@ -162,7 +162,12 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput) return nil, err } - go emailservice.InviteEmail(email, verificationToken, verifyEmailURL, redirectURL) + // exec it as go routine so that we can reduce the api latency + go emailservice.SendEmail([]string{user.Email}, constants.VerificationTypeInviteMember, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetInviteVerificationURL(verifyEmailURL, verificationToken, hostname), + }) } return &model.Response{ diff --git a/server/resolvers/login.go b/server/resolvers/login.go index 8587330..ed29e8c 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -123,7 +123,12 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes } go func() { - err := email.SendOtpMail(user.Email, otpData.Otp) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{params.Email}, constants.VerificationTypeOTP, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "otp": otpData.Otp, + }) if err != nil { log.Debug("Failed to send otp email: ", err) } diff --git a/server/resolvers/magic_link_login.go b/server/resolvers/magic_link_login.go index 456d5c0..611c47f 100644 --- a/server/resolvers/magic_link_login.go +++ b/server/resolvers/magic_link_login.go @@ -219,8 +219,12 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu return res, err } - // exec it as go routing so that we can reduce the api latency - go email.SendVerificationMail(params.Email, verificationToken, hostname) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{params.Email}, constants.VerificationTypeMagicLinkLogin, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetEmailVerificationURL(verificationToken, hostname), + }) } res = &model.Response{ diff --git a/server/resolvers/resend_otp.go b/server/resolvers/resend_otp.go index 4ae53c0..65d9cf1 100644 --- a/server/resolvers/resend_otp.go +++ b/server/resolvers/resend_otp.go @@ -84,7 +84,12 @@ func ResendOTPResolver(ctx context.Context, params model.ResendOTPRequest) (*mod } go func() { - err := email.SendOtpMail(params.Email, otp) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{params.Email}, constants.VerificationTypeOTP, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "otp": otp, + }) if err != nil { log.Debug("Error sending otp email: ", otp) } diff --git a/server/resolvers/resend_verify_email.go b/server/resolvers/resend_verify_email.go index bd72bdd..c58a034 100644 --- a/server/resolvers/resend_verify_email.go +++ b/server/resolvers/resend_verify_email.go @@ -39,6 +39,11 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma return res, fmt.Errorf("invalid identifier") } + user, err := db.Provider.GetUserByEmail(ctx, params.Email) + if err != nil { + return res, fmt.Errorf("invalid user") + } + verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, params.Email, params.Identifier) if err != nil { log.Debug("Failed to get verification request: ", err) @@ -74,8 +79,12 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma log.Debug("Failed to add verification request: ", err) } - // exec it as go routin so that we can reduce the api latency - go email.SendVerificationMail(params.Email, verificationToken, hostname) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{params.Email}, params.Identifier, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetEmailVerificationURL(verificationToken, hostname), + }) res = &model.Response{ Message: `Verification email has been sent. Please check your inbox`, diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index b7a548c..d5cd071 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -221,9 +221,14 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR return res, err } - // exec it as go routin so that we can reduce the api latency + // exec it as go routine so that we can reduce the api latency go func() { - email.SendVerificationMail(params.Email, verificationToken, hostname) + // exec it as go routine so that we can reduce the api latency + email.SendEmail([]string{params.Email}, constants.VerificationTypeBasicAuthSignup, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetEmailVerificationURL(verificationToken, hostname), + }) utils.RegisterEvent(ctx, constants.UserCreatedWebhookEvent, constants.AuthRecipeMethodBasicAuth, user) }() diff --git a/server/resolvers/update_profile.go b/server/resolvers/update_profile.go index 87762d9..9888aa5 100644 --- a/server/resolvers/update_profile.go +++ b/server/resolvers/update_profile.go @@ -244,8 +244,12 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput) return res, err } - // exec it as go routin so that we can reduce the api latency - go email.SendVerificationMail(newEmail, verificationToken, hostname) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{user.Email}, constants.VerificationTypeBasicAuthSignup, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetEmailVerificationURL(verificationToken, hostname), + }) } } diff --git a/server/resolvers/update_user.go b/server/resolvers/update_user.go index d20e4a9..e2f6618 100644 --- a/server/resolvers/update_user.go +++ b/server/resolvers/update_user.go @@ -156,8 +156,12 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod return res, err } - // exec it as go routin so that we can reduce the api latency - go email.SendVerificationMail(newEmail, verificationToken, hostname) + // exec it as go routine so that we can reduce the api latency + go email.SendEmail([]string{user.Email}, constants.VerificationTypeBasicAuthSignup, map[string]interface{}{ + "user": user.ToMap(), + "organization": utils.GetOrganization(), + "verification_url": utils.GetEmailVerificationURL(verificationToken, hostname), + }) } diff --git a/server/utils/common.go b/server/utils/common.go index 86156be..2040885 100644 --- a/server/utils/common.go +++ b/server/utils/common.go @@ -2,6 +2,9 @@ package utils import ( "reflect" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/memorystore" ) // StringSliceContains checks if a string slice contains a particular string @@ -58,3 +61,46 @@ func ConvertInterfaceToStringSlice(slice interface{}) []string { } return resSlice } + +// GetOrganization to get organization object +func GetOrganization() map[string]interface{} { + orgLogo, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo) + if err != nil { + return nil + } + orgName, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName) + if err != nil { + return nil + } + organization := map[string]interface{}{ + "name": orgName, + "logo": orgLogo, + } + + return organization +} + +// GetForgotPasswordURL to get url for given token and hostname +func GetForgotPasswordURL(token, hostname string) string { + resetPasswordUrl, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL) + if err != nil { + return "" + } + if resetPasswordUrl == "" { + if err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password"); err != nil { + return "" + } + } + verificationURL := resetPasswordUrl + "?token=" + token + return verificationURL +} + +// GetInviteVerificationURL to get url for invite email verification +func GetInviteVerificationURL(verificationURL, token, redirectURI string) string { + return verificationURL + "?token=" + token + "&redirect_uri=" + redirectURI +} + +// GetEmailVerificationURL to get url for invite email verification +func GetEmailVerificationURL(token, hostname string) string { + return hostname + "/verify_email?token=" + token +} diff --git a/server/validators/email_template.go b/server/validators/email_template.go index 1dd0766..fb5aa25 100644 --- a/server/validators/email_template.go +++ b/server/validators/email_template.go @@ -4,7 +4,7 @@ import "github.com/authorizerdev/authorizer/server/constants" // IsValidEmailTemplateEventName function to validate email template events func IsValidEmailTemplateEventName(eventName string) bool { - if eventName != constants.VerificationTypeBasicAuthSignup && eventName != constants.VerificationTypeForgotPassword && eventName != constants.VerificationTypeMagicLinkLogin && eventName != constants.VerificationTypeUpdateEmail { + if eventName != constants.VerificationTypeBasicAuthSignup && eventName != constants.VerificationTypeForgotPassword && eventName != constants.VerificationTypeMagicLinkLogin && eventName != constants.VerificationTypeUpdateEmail && eventName != constants.VerificationTypeOTP { return false }
iconicon

Hey there 👋

- {{.otp}} is your one time password (OTP) for accessing {{.org_name}}. Please keep your OTP confidential and it will expire in 1 minute. + {{.otp}} is your one time password (OTP) for accessing {{.organization.name}}. Please keep your OTP confidential and it will expire in 1 minute.