amazon-web-servicesgoemailmimeaws-pinpoint

Sending an email with RAW content using AWS Pinpoint and Go returns 403


I'm trying to send an email via AWS pinpoint containing attachments. To send attachments with an email, you must use the 'RAW' email content. The only documentation I can find about this is here: https://docs.aws.amazon.com/pinpoint-email/latest/APIReference/API_RawMessage.html, but it is missing quite a few things (like, what are the required headers?)

When I send an email using the 'simple' content, it works fine:

emailInput := &pinpointemail.SendEmailInput{
    Destination: &pinpointemail.Destination{
        ToAddresses: []*string{&address},
    },
    FromEmailAddress: &sender,
    Content: &pinpointemail.EmailContent{
                Simple: &pinpointemail.Message{
                    Body: &pinpointemail.Body{
                        Html: &pinpointemail.Content{
                            Charset: &charset,
                            Data:    &emailHTML,
                        },
                        Text: &pinpointemail.Content{
                            Charset: &charset,
                            Data:    &emailText,
                        },
                    },
                    Subject: &pinpointemail.Content{
                        Charset: &charset,
                        Data:    &emailSubject,
                    },
                },
}

Since I want to add attachments, I have to use the 'RAW' content type. I have written a function which generates the email content, based on: https://gist.github.com/douglasmakey/90753ecf37ac10c25873825097f46300:

func generateRawEmailContent(subject, to, from, HTMLBody string, attachments *[]EmailAttachment) []byte {
    buf := bytes.NewBuffer(nil)
    buf.WriteString(fmt.Sprintf("Subject: %s\n", subject))
    buf.WriteString(fmt.Sprintf("To: %s\n", to))
    buf.WriteString(fmt.Sprintf("From: %s\n\n", from))

    buf.WriteString("MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n")
    buf.WriteString(HTMLBody)

    writer := multipart.NewWriter(buf)
    boundary := writer.Boundary()

    buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
    buf.WriteString(fmt.Sprintf("--%s\n", boundary))

    for _, attachment := range *attachments {
        buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
        buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(attachment.Data)))
        buf.WriteString("Content-Transfer-Encoding: base64\n")
        buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", attachment.FileName))

        b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
        base64.StdEncoding.Encode(b, attachment.Data)
        buf.Write(b)
        buf.WriteString(fmt.Sprintf("\n--%s", boundary))
    }

    buf.WriteString("--")

    log.Println(string(buf.Bytes()))

    return buf.Bytes()
}

This generates the following (emails changed):

Subject: Welcome \nTo: xxxxx@gmail.com\nFrom: xxxxx@gmail.com\n\nMIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n\u003ch1\u003eHello ,\u003c/h1\u003e\u003cp\u003eYou now have an account.\u003c/p\u003e\nContent-Type: multipart/mixed; boundary=8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\n\n\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment; filename=test.json\newogICJ0ZXN0IjogdHJ1ZQp9\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647--

I then construct the email as follows:

&pinpointemail.SendEmailInput{
    Destination: &pinpointemail.Destination{
        ToAddresses: []*string{&address},
    },
    FromEmailAddress: &sender,
    Content: &pinpointemail.EmailContent{
                Raw: &pinpointemail.RawMessage{
                    Data: generateRawEmailContent(emailSubject, address, sender, emailHTML, emailAttachments),
                },
}

When sending this email via the github.com/aws/aws-sdk-go/service/pinpoint package, I get a 403 returned, and I have no idea why. A 403 means that the resource I'm trying to access is forbidden, but I don't see how that is relevant here? Also, there is no documentation about a 403 even being a possible response. Any help would be greatly appreciated!

I have also tried using libraries, like for instance the gomail-v2 library as follows:

m := gomail.NewMessage()
    m.SetHeader("From", from)
    m.SetHeader("To", to)
    m.SetHeader("Subject", subject)
    m.SetBody("text/plain", textBody)
    m.AddAlternative("text/html", HTMLBody)
    m.Attach("foo.txt", gomail.SetCopyFunc(func(w io.Writer) error {
        _, err := w.Write((*attachments)[0].Data)
        return err
    }))

    buf := bytes.NewBuffer(make([]byte, 0, 2048))
    _, werr := m.WriteTo(buf)
    if werr != nil {
        return nil, common.NewStackError(werr)
    }

But that still gives me a 403 error.


Solution

  • Okay, found the issue. Turns out that this 403 error has nothing to do with my code, but rather with IAM permissions in AWS. Apparently an IAM permission has to be turned on to enable RAW email content.