SMTP golang TLS tutorial using go-smtp is already
Sending secure emails with Go's popular SMTP package using secure socket layer is easy with this tutorial. Learn how to do it with just a few steps!
Sending email over SMTP in go is easy. Go contains its own net/smtp package but it can be cumbersome. Luckily there exists the excellent open source emersion go-smtp package. In this post we will show you how to send email using SMTP commands in go.
Install dependencies
Add the github.com/emersion/go-smtp
dependency to your go project. For this example we will also add MailSlurp to create an SMTP inbox for our test. We can find this at github.com/mailslurp/mailslurp-client-go
.
import (
"context"
"github.com/antihax/optional"
sasl "github.com/emersion/go-sasl"
smtp "github.com/emersion/go-smtp"
mailslurp "github.com/mailslurp/mailslurp-client-go"
"github.com/stretchr/testify/assert"
"log"
"os"
"strings"
"testing"
)
Notice the assert
package - we can use that to test our SMTP client code.
Get MailServer connection details
We need to send email with Go to and through email mailservers. We can do this if we know a mailservers port and host names. With MailSlurp we can generate a new disposable mailbox and use the details associated with it.
var apiKey = os.Getenv("API_KEY")
func getMailSlurpClient(t *testing.T) (*mailslurp.APIClient, context.Context) {
assert.NotNil(t, apiKey)
// create a context with your api key
ctx := context.WithValue(context.Background(), mailslurp.ContextAPIKey, mailslurp.APIKey{Key: apiKey})
// create mailslurp client
config := mailslurp.NewConfiguration()
client := mailslurp.NewAPIClient(config)
return client, ctx
}
Sending secure email over TLS
To send email safely with Go we should use TLS secure socket connections. We can do this like so:
func Test_CanSendEmail_TLS(t *testing.T) {
// create a context with your api key
client, ctx := getMailSlurpClient(t)
// create an inbox using the inbox controller
opts := &mailslurp.CreateInboxOpts{
InboxType: optional.NewString("SMTP_INBOX"),
}
// create two inboxes for testing
inbox1, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)
smtpAccess, _, _ := client.InboxControllerApi.GetImapSmtpAccess(ctx, &mailslurp.GetImapSmtpAccessOpts{
InboxId: optional.NewInterface(inbox1.Id),
})
inbox2, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)
// send email from inbox1 to inbox2
auth := sasl.NewPlainClient("", smtpAccess.SmtpUsername, smtpAccess.SmtpPassword)
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
to := []string{inbox2.EmailAddress}
msg := strings.NewReader("To: " + inbox2.EmailAddress + "\r\n" +
"Subject: Hello Gophers!\r\n" +
"\r\n" +
"This is the email body.\r\n")
// not TLS mailslurp uses a different host
err := smtp.SendMail("mailslurp.mx:587", auth, inbox1.EmailAddress, to, msg)
if err != nil {
log.Fatal(err)
assert.NoError(t, err, "Expect smtp send to work")
}
// fetch the email for inbox2
waitOpts := &mailslurp.WaitForLatestEmailOpts{
InboxId: optional.NewInterface(inbox2.Id),
Timeout: optional.NewInt64(30000),
UnreadOnly: optional.NewBool(true),
}
email, _, err := client.WaitForControllerApi.WaitForLatestEmail(ctx, waitOpts)
assert.NoError(t, err)
assert.Contains(t, *email.Subject, "Hello Gophers")
assert.Contains(t, *email.Body, "This is the email body")
}
How to send when insecure?
If necessary we can also send emails with TLS using an insecure connection:
func Test_CanSendEmail_Insecure(t *testing.T) {
// create a context with your api key
client, ctx := getMailSlurpClient(t)
// create an inbox using the inbox controller
opts := &mailslurp.CreateInboxOpts{
InboxType: optional.NewString("SMTP_INBOX"),
}
// create two inboxes for testing
inbox1, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)
smtpAccess, _, _ := client.InboxControllerApi.GetImapSmtpAccess(ctx, &mailslurp.GetImapSmtpAccessOpts{
InboxId: optional.NewInterface(inbox1.Id),
})
inbox2, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)
// create a plain auth client with smtp access details
auth := sasl.NewPlainClient("", smtpAccess.SmtpUsername, smtpAccess.SmtpPassword)
// dial connection to the smtp server
c, err := smtp.Dial("mx.mailslurp.com:2525")
assert.NoError(t, err, "Expect client dial")
defer c.Close()
// issue hello smtp command
log.Println("Say hello")
err = c.Hello("test")
assert.NoError(t, err, "Expect hello")
// issue auth smtp command
log.Println("Set auth")
err = c.Auth(auth)
assert.NoError(t, err, "Expect auth")
// send the email
log.Println("Send email")
to := []string{inbox2.EmailAddress}
msg := strings.NewReader("To: " + inbox2.EmailAddress + "\r\n" +
"Subject: Hello Insecure Gophers!\r\n" +
"\r\n" +
"This is the email body.\r\n")
err = c.SendMail(inbox1.EmailAddress, to, msg)
assert.NoError(t, err, "Expect insecure smtp send to work")
// fetch the email for inbox2
log.Println("Wait for email to arrive")
waitOpts := &mailslurp.WaitForLatestEmailOpts{
InboxId: optional.NewInterface(inbox2.Id),
Timeout: optional.NewInt64(30000),
UnreadOnly: optional.NewBool(true),
}
email, _, err := client.WaitForControllerApi.WaitForLatestEmail(ctx, waitOpts)
// assert email contents
log.Println("Email received: " + *email.Subject)
assert.NoError(t, err)
assert.Contains(t, *email.Subject, "Hello Insecure Gophers")
assert.Contains(t, *email.Body, "This is the email body")
}