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

adamu
When starting a new project, do you have any go-to libraries you pull in out of habit/preference? For example, Ash, Credo, Mox, ExMachin...
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
sym_num
I created a Forth processor in Elixir. This is my hobby project. https://github.com/sasagawa888/Forth
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
arcanemachine
Just wondering what the community currently thinks, prefers, and/or recommends for working with UI components. (e.g. daisyUI, MishkaChele...
New
yolo007wizard
I’ve been reading up on Firefly and it looks very impressive. Does this mean faster CPU bound Elixir? i.e. ballpark java speeds? Or at le...
New
bartblast
Hey there! :slight_smile: I’m working on updating the Hologram website home page and would love to get your input. Current situation: ...
New
bartblast
Added by a user on X: https://x.com/aldicodes/status/1952095492139299036
New
ashkan117
I’m wondering how do people structure their JSON Api’s with Phoenix. Using the blogs example, let’s say I have a blogs view like the foll...
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

9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
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
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement