Gmail SMTP, Ruby on Rails + Actionmailer, and you

Edit - 03/25/2016 - As a commenter pointed out to me, if a person is not using Two-factor authentication with Google, there may be problems following this tutorial which I was not previously aware of. For everyone using 2FA, the process involves going to the security page in Google's account management area, and generating an app-specific password to use. For everyone not using 2FA, it was brought to my attention that the setting to 'allow less secure apps' needs to be toggled on here: https://www.google.com/settings/u/1/security/lesssecureapps

I hope this saves folks some trouble/frustration.


I've been fiddling with a rails tutorial, and one step of the project is to add an email action when a user submits a contact form. The tutorial's solution is to just ignore the development environment, and add the Sendgrid addon to the production environment using heroku addons:create sendgrid:starter. Unfortunately, Heroku now requires a credit card to perform this action, since sending more than 400 emails per day bumps you up into the "we're gonna charge you for that" territory. Assuming that surely there must be another (free) way to solve this problem, I set off in search of a solution.

Very quickly I came across old instructions for how to set up rails with Gmail (like, 2009 era old), but I wanted to dig a bit further to make sure that I found some documentation that was more recent. The solution below works on my local machine and deployed on Heroku -- just copy the config.action_mailer lines to both production.rb and development.rb and modify the hostnames appropriately.

Cobbling together a solution

Ultimately this page at railsapps.github.io had the information that was most relevant to my specific scenario. Searching in the page for "Enabling email" or "Gmail" should get you to this section:

Gmail

If you’re sending test emails during development, Gmail is the easiest option.

To use Gmail from Heroku, add the following to your config/environments/production.rb file:

config.action_mailer.default_url_options = { :host => 'myapp.herokuapp.com' }  
config.action_mailer.delivery_method = :smtp  
config.action_mailer.perform_deliveries = true  
config.action_mailer.raise_delivery_errors = false  
config.action_mailer.default :charset => "utf-8"  
config.action_mailer.smtp_settings = {  
  address: "smtp.gmail.com",
  port: 587,
  domain: "myapp.herokuapp.com",
  authentication: "plain",
  enable_starttls_auto: true,
  user_name: ENV["GMAIL_USERNAME"],
  password: ENV["GMAIL_PASSWORD"]
}

To avoid storing your Gmail username and password in your Git repository, use environment variables to pass the username and password to your application. See Heroku’s documentation Configuration and Config Vars.

Set your Gmail username and password as Heroku environment variables:

$ heroku config:add GMAIL_USERNAME=no-reply@example.com GMAIL_PASSWORD=please

Which is more or less what I needed to get going, but I made a few changes and have a few notes to add. One to mention here is that I copied those lines to both /projectFolder/config/environments/production.rb and /projectFolder/config/environments/development.rb, modifying the hostnames accordingly.

This is the part where I tell you what I screwed up

  • I had my email address wrong in /projectFolder/app/mailers/nameOfMailer.rb
    • techncially it was my email address, but I have a couple Gmail addresses set up to forward to one main address, and the combination of my forwarding rules with my SMTP configuration somehow short-circuited the forwarding.
    • I double-checked afterwards, and the emails did get sent to the "wrong" email address, but I spent time thinking that something was broken because they weren't forwarding to my main address.
    • Yes, I know that this is an edge case, but there's some slim chance that someone else may be fighting the same stupid thing and stumble across this post.
  • Second, for testing or development environments, change config.action_mailer.raise_deliver_errors = false to true. Ignoring delivery errors isn't helpful if I'm trying to actively debug delivery errors...

Figaro

The final piece the puzzle was adding Figaro "to securely configure Rails applications." It is probably overkill for this particular application of mine, but I wanted to use something to properly manage my Gmail account credentials. If my goal is learning, doing something the hack/shortcut way is somewhat counter-productive.

Figaro isn't hard to add or configure1. gem 'figaro' goes in the Gemfile. Then run bundle exec figaro install. Figaro stores configuration values (like Gmail account credentials) in /projectFolder/config/application.yml -- and it adds this file to .gitignore on install, so no risk of dumping your usernames and passwords into a public repo. Also, if it is present, Figaro will check /projectFolder/config/initializers/Figaro.rb for required configuration keys and raise an error if they are missing.

My application.yml has these lines:

GMAIL_USERNAME: 'myUsername@gmail.com'  
GMAIL_PASSWORD: '16digitAppPassword'  

And Figaro.rb looks like this:

Figaro.require_keys('GMAIL_USERNAME', 'GMAIL_PASSWORD')  

With that stuff in place, the command figaro heroku:set -e production copies my configuration variables over to Heroku as environment variables for my app to use. Notice above that the SMTP configuration is looking for the Gmail username/password as environment variables. So everything should be good to go.

Note

I'm using two-factor authentication for my Gmail account. Everyone should have two-factor authentication set up, but that's another blog post topic. With 2FA set up, it's still simple to set up this SMTP stuff, but you have to use an "app password." https://security.google.com/settings/security/apppasswords is the place to generate new app passwords or revoke existing passwords. They're intended to be one-time use (you can't view existing passwords). If it's necessary to re-enter one, just revoke the old one and generate a new one.

For this situation, generate a new app password and copy it over to application.yml -- don't worry about remembering or saving it elsewhere. Also, spaces are ignored by Google in app passwords, so it shouldn't matter if they're in the string or not.

Conclusion

I can now send emails from the contact form in my app. They show up as if my Gmail account sent them. I don't have to give Heroku my credit card number, use an add-on in my project, or integrate with any other 3rd-party service. Life is good.

  1. It's ruby. Everything (that is a gem, which is everything) is easy to install. :-P