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:
https://github.com/swoosh/swoosh/blob/f46be9a98623dc95eabfb7c37ca882bcaadc10b6/lib/swoosh/adapters/amazon_ses.ex#L59-L65

Where Next?

Popular in Discussions Top

matthias_toepp
I’d love to hear what people think about Wisp, the new Gleam web framework started by Gleam’s primary creator Louis Pilfold. Gleam, alon...
New
jswny
I would like to better understand what the advantages/disadvantages of umbrella applications are compared to structuring your app as as s...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
laiboonh
Hi all, I am trying to convince my team to use liveview over the current react. What are some of the points where one should consider us...
New
Crowdhailer
I’ve been hearing much about the new formatter and it’s something I have been keen to try. I find examples buy far the most illuminating...
248 19327 150
New
mbenatti
Following https://github.com/tbrand/which_is_the_fastest |&gt; https://raw.githubusercontent.com/tbrand/which_is_the_fastest/master/imgs...
New
fireproofsocks
I’ve been working on an Elixir project that has required a lot of scripting. I usually reach for Elixir because I like it more (and in th...
New
Fl4m3Ph03n1x
Background A few days ago I was listening to The future of Elixir from Elixir Talks, with Dave Thomas (@pragdave ) and Brian Mitchell. I...
New
kostonstyle
Hi all How can I compare haskell with elixir, included tools, webservices, ect. Thanks
New
paulanthonywilson
I like Umbrella projects and pretty much always use them for personal Elixir stuff, especially Nerves things. But I don’t think this is ...
New

Other popular topics Top

AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement