ScriptyScott

ScriptyScott

Swoosh: Swoosh.Adapters.SMTP Vs. Swoosh.Adapters.AmazonSES

Hey Folks,

I just spent the last couple of days doing a deep dive into using Swoosh with Amazon’s simple email service, check out this troubleshooting post for a bit of background.

While doing so, I came to two different working solutions, one that sent emails via interacting with AmazonSES API directly, and the other used Swoosh’s SMTP adapter and sent emails via AmazonSES’s SMTP endpoint. Both seemed to have their own pros and cons. I thought this would be a good discussion to have and I’m genuinely interested in seeing what the more popular choice is.

Swoosh.Adapters.AmazonSES

To use Swoosh’s AmazonSES adapter the setup is somewhat simple. Your configuration is fairly straight forward and is as follows:

config :app_name, AppName.Mailer,
  adapter: Swoosh.Adapters.AmazonSES,
  region: "us-east-2", # yours may be different
  access_key: "ROOT_AWS_ACCESS_KEY",
  secret: "ROOT_AWS_SECRET_KEY"

This is where the simplicity ends. Because you’re not using the the standard SMTP adapter Swoosh.Adapters.SMTP you need to enable an API client and add a few dependencies.

To enable an API client first change this line in your config.exs from:

config :swoosh, :api_client, false

To

config :swoosh, :api_client, Swoosh.ApiClient.Hackney

# or

config :swoosh, :api_client, Swoosh.ApiClient.Finch

Swoosh works well with both Finch and Hackney out of the box. The next step is to add your dependencies to mix.exs, fetch and restart the phoenix server.

  defp deps do
    [
      {:gen_smtp, "~> 1.1.1"},
      {:hackney, "~> 1.18.0"} # or Finch
    ]
  end

You’ll also notice that you need to add gen_smtp, this is required for non SMTP adapters as they require a SMTP client.

The Swoosh.Adapters.AmazonSES has a very simple config but requires additional dependencies and if you noticed requires you to use and include your root admin AWS credentials. Because the Swoosh.Adapters.AmazonSE adapter interacts with the SES APIs directly, you need to use your root AWS access and secrete key and you cannot use a SMTP user/credentials with limited access. I.e. if your Phoenix secretes are compromised an attacker will have access to all of your AWS services not just AmazonSES.

Swoosh.Adapters.SMTP

The config.exs has a couple more fields and is as follows:

config :app_name, AppName.Mailer,
  adapter: Swoosh.Adapters.SMTP,
  relay: "email-smtp.us-east-2.amazonaws.com", # yours may be different
  username: "SMTP_AWS_ACCESS_KEY",
  password: "SMTP_AWS_SECRET_KEY",
  port: 25,
  retries: 2,
  no_mx_lookups: false

This should be all you need! Simply create an SMTP IAM user that has restricted access to the AmazonSES service and use those credentials as your username and password.

The Swoosh.Adapters.SMTP is easier to use out of box, has less additional dependencies and you can use SMTP credentials NOT your admin AWS credentials. However, this solution comes with all of the issues that SMTP has.

Conclusion

The Swoosh.Adapters.AmazonSES adapter has a fairly basic config.exs and I like that it directly interacts with the SES APIs. However, it creates a bit of a security issue as you have to have an admin level AWS credential in your Phoenix secrets. This doc outlines which credentials should be used in each situation and further illustrates this issue. The Swoosh.Adapters.AmazonSES also requires additional dependencies which might be confusing for those new to Phoenix or time consuming for those who just want something up and running as quickly as possible.

The Swoosh.Adapters.SMTP adapter has less dependencies and works right out of the box. It’s also not dependant on AmazonSES or a specific SMTP implementation. You can very quickly adapt it to work with another SMTP server. Also, when using it with AmazonSES you can use an IAM user/credentials that only has access to the AmazonSES service. However, you inevitably get all of the issues that come with SMTP. It’s an older protocol that’s showing it’s age and I’ve run into issues getting it to work with certain security protocols.

To be honest I’m a bit divided. I like interacting directly with the AmazonSES APIs but having to add admin credentials makes me a bit nervous. I like the simplicity of SMTP but, maybe I’m showing bias here, am not a fan of the protocol. It feels clunky, dated and restrictive in some situations. I’d love to hear everyone’s thoughts on this.

Also, if anyone has figured out how to use the Swoosh.Adapters.AmazonSES without admin AWS credentials, please post here! I was unable to figure it out and after reading the doc linked above came to the conclusion you couldn’t. But I’d love to be wrong on this for obvious reasons.

Thanks,
Scott

Most Liked

ScriptyScott

ScriptyScott

@al2o3cr So I’ve spent a few (very long) days getting this to work with an IAM user with restricted access. I’ve come across a fairly large discrepancy within the Swoosh docs.

As it turns out getting this to work with an IAM user is even easier than the docs imply. Sadly, after adding ex_aws and ex_aws_sts as dependencies, working out how to request a X-Amz-Security-Token and spending hours getting the IAM roles/policies correct, it turns out it was all unnecessary.

I was able to make this work by simply creating a user in IAM, and restricting the access to only sending SES emails. Here is the policy I used to do so:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}

Two things to note for those trying to implement this:

  1. This IAM policy allows sending from ANY email address. You probably want to restrict this by filling out the resource field with one or more valid sender emails. Please see this AWS IAM doc for more information on restricting a user’s access to just sending emails.

  2. This should work with roles applied to an individual user or user group. I implemented this with an IAM policy but this should work with an IAM role as well.

Once I assigned this policy to the user and verified the receiver’s email address in SES, I could send emails regardless of if I added the |> put_provider_option(:security_token, "your_token_here") header or not. It just worked! All that was needed was the policy and the usage of the new user’s access_key and secret in place of the root credentials.

I found this to be fairly confusing as it’s clearly stated in the Swoosh docs. Can someone provide insight into why this might be? It looks like AWS recently revamped the SES dashboard and the service it’s self. Could this requirement have been removed when this was done?

To come back to the original topic, I prefer sending emails with the restricted IAM user far more than using SMTP or root credentials. It’s safer, less configuration and allows you to apply this policy or role to any new users or user groups you create in the future. I would recommend using this or a similar solution for anyone who finds themselves wanting to send emails via Amazon's SES and Swoosh.

Additional thoughts are welcome!

Thanks,
Scott

arcanemachine

arcanemachine

Just wanted to put this here in case it helps somebody else:

@ScriptyScott The line given in your original post wasn’t working for me on Phoenix 1.7.2:

config :swoosh, :api_client, Swoosh.ApiClient.Finch

It was causing the following error :

[error] GenServer #PID<0.931.0> terminating
** (ArgumentError) unknown registry: Swoosh.Finch
    (elixir 1.14.3) lib/registry.ex:1382: Registry.key_info!/1
    (elixir 1.14.3) lib/registry.ex:580: Registry.lookup/2

[the stack trace continues but I stopped here for the sake of brevity...]

I was able to copy the following line from config/prod.exs to get things working:

config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: TodoList.Finch
al2o3cr

al2o3cr

Swoosh allows for provider-specific options when sending - see the put_provider_option function in the Swoosh docs.

In particular, there’s a mention of using an IAM role along with the SES adapter:

ScriptyScott

ScriptyScott

@al2o3cr That’s awesome! I’m going to kick this around a bit, I’ll update the discussion.

Where Next?

Popular in Discussions Top

New
New
AstonJ
A recent chat with @leifericf inspired this thread - he’s worked in the gaming industry, and so it got me wondering what kind of industri...
New
SyntaxSorcerer
This is the start of my Elixir learning journal/journey. I plan to share my journey in this thread and am open to all feedback, construct...
New
smueller
With the announcement of 1.19 rc0 and the path to user-supplied type annotations, I want to make the case for inline types—both for funct...
New
AstonJ
@Garrison’s comment in another thread reminded me of this post by Joe: With the big five exerting more control than ever, new (AI) play...
New
tcoopman
Yesterday I watched OpenTelemetry: From Desire to Dashboard and it triggered me to take a new look at telemetry in Elixir. But I don’t k...
New
fredwu
Hey folks, Just wanted to share my journey and experiences so far. Preface: I’ve been using Elixir for about 10 years now, so relatively...
New
bartblast
I recently started a discussion about reactive patterns in Hologram, but I realized I need to step back and design the composability arch...
New
AstonJ
Posting this as a separate thread as I’m sure lots of people will be curious - what will the (or the intended) differences be and when mi...
New

Other popular topics Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement